From d31b4b6e0cf3ee7dcbe05f86f6c687d307b45782 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 7 Apr 2016 09:11:04 -0700 Subject: [PATCH] Allow calls to IsManagedType while computing GetMembers --- .../BinderFactory.BinderFactoryVisitor.cs | 28 ++++- .../CSharp/Portable/Binder/BinderFactory.cs | 38 +++--- .../Portable/Symbols/BaseTypeAnalysis.cs | 116 +++++++----------- .../Portable/Symbols/NamedTypeSymbol.cs | 26 ++++ .../Retargeting/RetargetingNamedTypeSymbol.cs | 9 +- .../Source/SourceMemberContainerSymbol.cs | 8 +- .../Source/SourceMemberMethodSymbol.cs | 66 ++-------- .../Test/Semantic/Semantics/UnsafeTests.cs | 13 ++ 8 files changed, 149 insertions(+), 155 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs index df4b443133a..12723d1f6be 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs @@ -16,6 +16,8 @@ internal sealed partial class BinderFactory private sealed class BinderFactoryVisitor : CSharpSyntaxVisitor { private int _position; + private CSharpSyntaxNode _memberDeclarationOpt; + private Symbol _memberOpt; private readonly BinderFactory _factory; internal BinderFactoryVisitor(BinderFactory factory) @@ -23,12 +25,13 @@ internal BinderFactoryVisitor(BinderFactory factory) _factory = factory; } - internal int Position + internal void Initialize(int position, CSharpSyntaxNode memberDeclarationOpt, Symbol memberOpt) { - set - { - _position = value; - } + Debug.Assert((memberDeclarationOpt == null) == (memberOpt == null)); + + _position = position; + _memberDeclarationOpt = memberDeclarationOpt; + _memberOpt = memberOpt; } private CSharpCompilation compilation @@ -449,6 +452,11 @@ private static string GetPropertyOrEventName(BasePropertyDeclarationSyntax baseP // Get the correct methods symbol within container that corresponds to the given method syntax. private SourceMethodSymbol GetMethodSymbol(BaseMethodDeclarationSyntax baseMethodDeclarationSyntax, Binder outerBinder) { + if (baseMethodDeclarationSyntax == _memberDeclarationOpt) + { + return (SourceMethodSymbol)_memberOpt; + } + NamedTypeSymbol container = GetContainerType(outerBinder, baseMethodDeclarationSyntax); if ((object)container == null) { @@ -461,6 +469,11 @@ private SourceMethodSymbol GetMethodSymbol(BaseMethodDeclarationSyntax baseMetho private SourcePropertySymbol GetPropertySymbol(BasePropertyDeclarationSyntax basePropertyDeclarationSyntax, Binder outerBinder) { + if (basePropertyDeclarationSyntax == _memberDeclarationOpt) + { + return (SourcePropertySymbol)_memberOpt; + } + Debug.Assert(basePropertyDeclarationSyntax.Kind() == SyntaxKind.PropertyDeclaration || basePropertyDeclarationSyntax.Kind() == SyntaxKind.IndexerDeclaration); NamedTypeSymbol container = GetContainerType(outerBinder, basePropertyDeclarationSyntax); @@ -475,6 +488,11 @@ private SourcePropertySymbol GetPropertySymbol(BasePropertyDeclarationSyntax bas private SourceEventSymbol GetEventSymbol(EventDeclarationSyntax eventDeclarationSyntax, Binder outerBinder) { + if (eventDeclarationSyntax == _memberDeclarationOpt) + { + return (SourceEventSymbol)_memberOpt; + } + NamedTypeSymbol container = GetContainerType(outerBinder, eventDeclarationSyntax); if ((object)container == null) { diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs index 113f43ef2b6..82e1a2a8790 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs @@ -87,34 +87,38 @@ private bool InScript } /// - /// Note, there is no guarantee that the factory always gives back the same binder instance for the same . + /// Return binder for binding at node. + /// and + /// are optional syntax and symbol for the member containing . + /// If provided, the will use the member symbol rather + /// than looking up the member in the containing type, allowing this method to be called + /// while calculating the member list. /// - internal Binder GetBinder(CSharpSyntaxNode node) + /// + /// Note, there is no guarantee that the factory always gives back the same binder instance for the same node. + /// + internal Binder GetBinder(CSharpSyntaxNode node, CSharpSyntaxNode memberDeclarationOpt = null, Symbol memberOpt = null) { int position = node.SpanStart; - // Special case: In interactive code, we may be trying to retrieve a binder for global statements - // at the *very* top-level (i.e. in a completely empty file). In this case, we use the compilation unit - // directly since it's parent would be null. - if (InScript && node.Kind() == SyntaxKind.CompilationUnit) + // Unless this is interactive retrieving a binder for global statements + // at the very top-level (i.e. in a completely empty file) use + // node.Parent to maintain existing behavior. + if (!InScript || node.Kind() != SyntaxKind.CompilationUnit) { - return GetBinder(node, position); + node = node.Parent; } - // ACASEY: Using node.Parent here to maintain existing behavior, - // but I have no idea why. - return GetBinder(node.Parent, position); + return GetBinder(node, position, memberDeclarationOpt, memberOpt); } - internal Binder GetBinder(CSharpSyntaxNode node, int position) + internal Binder GetBinder(CSharpSyntaxNode node, int position, CSharpSyntaxNode memberDeclarationOpt = null, Symbol memberOpt = null) { Debug.Assert(node != null); - Binder result = null; - BinderFactoryVisitor visitor = _binderFactoryVisitorPool.Allocate(); - visitor.Position = position; - result = node.Accept(visitor); + visitor.Initialize(position, memberDeclarationOpt, memberOpt); + Binder result = node.Accept(visitor); _binderFactoryVisitorPool.Free(visitor); return result; @@ -135,7 +139,7 @@ internal InContainerBinder GetImportsBinder(CSharpSyntaxNode unit, bool inUsing case SyntaxKind.NamespaceDeclaration: { BinderFactoryVisitor visitor = _binderFactoryVisitorPool.Allocate(); - visitor.Position = 0; + visitor.Initialize(0, null, null); var result = visitor.VisitNamespaceDeclaration((NamespaceDeclarationSyntax)unit, unit.SpanStart, inBody: true, inUsing: inUsing); _binderFactoryVisitorPool.Free(visitor); return result; @@ -145,7 +149,7 @@ internal InContainerBinder GetImportsBinder(CSharpSyntaxNode unit, bool inUsing // imports are bound by the Script class binder: { BinderFactoryVisitor visitor = _binderFactoryVisitorPool.Allocate(); - visitor.Position = 0; + visitor.Initialize(0, null, null); var result = visitor.VisitCompilationUnit((CompilationUnitSyntax)unit, inUsing: inUsing, inScript: InScript); _binderFactoryVisitorPool.Free(visitor); return result; diff --git a/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs b/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs index 971bc8ad28e..adff30f53c7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs +++ b/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs @@ -1,87 +1,69 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Collections.Generic; using System.Diagnostics; -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Collections; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols { internal static class BaseTypeAnalysis { - // let's keep up to 16 hashsets so that we do not need to allocate them over and over. - // we do not allocate hashsets recursively, so even for big hierarchies, one hashset is sufficient - // We may need more than one in a case of running this analysis concurrently, so we will keep up to 16 - // which seems plenty for this scenario. - private static readonly ObjectPool> s_hsPool = - new ObjectPool>(() => new HashSet(ReferenceEqualityComparer.Instance), 16); - - internal static bool ClassDependsOn(TypeSymbol depends, TypeSymbol on) + internal static bool ClassDependsOn(NamedTypeSymbol depends, NamedTypeSymbol on) { - if ((object)depends == null || (object)on == null) - { - return false; - } + Debug.Assert((object)depends != null); + Debug.Assert((object)on != null); + Debug.Assert(on.IsDefinition); - var hs = s_hsPool.Allocate(); + var hs = PooledHashSet.GetInstance(); ClassDependsClosure(depends, depends.DeclaringCompilation, hs); - var result = hs.Contains(on.OriginalDefinition); - hs.Clear(); - s_hsPool.Free(hs); + var result = hs.Contains(on); + hs.Free(); return result; } - private static void ClassDependsClosure(TypeSymbol type, CSharpCompilation currentCompilation, HashSet partialClosure) + private static void ClassDependsClosure(NamedTypeSymbol type, CSharpCompilation currentCompilation, HashSet partialClosure) { if ((object)type == null) { return; } - var namedType = type.OriginalDefinition as NamedTypeSymbol; - if ((object)namedType != null && partialClosure.Add(namedType)) + type = type.OriginalDefinition; + if (partialClosure.Add(type)) { - ClassDependsClosure(namedType.GetDeclaredBaseType(null), currentCompilation, partialClosure); + ClassDependsClosure(type.GetDeclaredBaseType(null), currentCompilation, partialClosure); // containment is interesting only for the current compilation - if (currentCompilation != null && namedType.IsFromCompilation(currentCompilation)) + if (currentCompilation != null && type.IsFromCompilation(currentCompilation)) { - ClassDependsClosure(namedType.ContainingType, currentCompilation, partialClosure); + ClassDependsClosure(type.ContainingType, currentCompilation, partialClosure); } } } - internal static bool StructDependsOn(TypeSymbol depends, NamedTypeSymbol on) + internal static bool StructDependsOn(NamedTypeSymbol depends, NamedTypeSymbol on) { - if ((object)depends == null || (object)on == null) - { - return false; - } + Debug.Assert((object)depends != null); + Debug.Assert((object)on != null); + Debug.Assert(on.IsDefinition); - var hs = s_hsPool.Allocate(); + var hs = PooledHashSet.GetInstance(); StructDependsClosure(depends, hs, on); var result = hs.Contains(on); - hs.Clear(); - s_hsPool.Free(hs); + hs.Free(); return result; } - private static void StructDependsClosure(TypeSymbol type, HashSet partialClosure, NamedTypeSymbol on) + private static void StructDependsClosure(NamedTypeSymbol type, HashSet partialClosure, NamedTypeSymbol on) { - if ((object)type == null) - { - return; - } + Debug.Assert((object)type != null); - var nt = type as NamedTypeSymbol; - if ((object)nt != null && ReferenceEquals(nt.OriginalDefinition, on)) + if ((object)type.OriginalDefinition == on) { // found a possibly expanding cycle, for example // struct X { public T t; } @@ -90,7 +72,8 @@ private static void StructDependsClosure(TypeSymbol type, HashSet partia partialClosure.Add(on); return; } - if ((object)nt != null && partialClosure.Add(nt)) + + if (partialClosure.Add(type)) { foreach (var member in type.GetMembersUnordered()) { @@ -100,7 +83,7 @@ private static void StructDependsClosure(TypeSymbol type, HashSet partia continue; } - StructDependsClosure(field.Type, partialClosure, on); + StructDependsClosure((NamedTypeSymbol)field.Type, partialClosure, on); } } } @@ -134,41 +117,36 @@ internal static bool IsManagedType(NamedTypeSymbol type) } // Otherwise, we have to build and inspect the closure of depended-upon types. - HashSet closure = s_hsPool.Allocate(); - bool result = DependsOnDefinitelyManagedType(type, closure); - closure.Clear(); - s_hsPool.Free(closure); + var hs = PooledHashSet.GetInstance(); + bool result = DependsOnDefinitelyManagedType(type, hs); + hs.Free(); return result; } private static bool DependsOnDefinitelyManagedType(NamedTypeSymbol type, HashSet partialClosure) { - Debug.Assert(!ReferenceEquals(type, null)); + Debug.Assert((object)type != null); // NOTE: unlike in StructDependsClosure, we don't have to check for expanding cycles, // because as soon as we see something with non-zero arity we kick out (generic => managed). if (partialClosure.Add(type)) { - foreach (var member in type.GetMembersUnordered()) + foreach (var member in type.GetInstanceFieldsAndEvents()) { // Only instance fields (including field-like events) affect the outcome. - if (member.IsStatic) - { - continue; - } - - FieldSymbol field = null; - + FieldSymbol field; switch (member.Kind) { case SymbolKind.Field: field = (FieldSymbol)member; - Debug.Assert(ReferenceEquals(field.AssociatedSymbol as EventSymbol, null), + Debug.Assert((object)(field.AssociatedSymbol as EventSymbol) == null, "Didn't expect to find a field-like event backing field in the member list."); break; case SymbolKind.Event: field = ((EventSymbol)member).AssociatedField; break; + default: + throw ExceptionUtilities.UnexpectedValue(member.Kind); } if ((object)field == null) @@ -275,29 +253,27 @@ private static ThreeState IsManagedTypeHelper(NamedTypeSymbol type) } } - internal static bool InterfaceDependsOn(TypeSymbol depends, TypeSymbol on) + internal static bool InterfaceDependsOn(NamedTypeSymbol depends, NamedTypeSymbol on) { - if ((object)depends == null || (object)on == null) - { - return false; - } + Debug.Assert((object)depends != null); + Debug.Assert((object)on != null); + Debug.Assert(on.IsDefinition); - var hs = s_hsPool.Allocate(); + var hs = PooledHashSet.GetInstance(); InterfaceDependsClosure(depends, hs); - var result = hs.Contains(on.OriginalDefinition); - hs.Clear(); - s_hsPool.Free(hs); + var result = hs.Contains(on); + hs.Free(); return result; } - private static void InterfaceDependsClosure(TypeSymbol type, HashSet partialClosure) + private static void InterfaceDependsClosure(NamedTypeSymbol type, HashSet partialClosure) { - var nt = type.OriginalDefinition as NamedTypeSymbol; - if ((object)nt != null && partialClosure.Add(nt)) + type = type.OriginalDefinition; + if (partialClosure.Add(type)) { - foreach (var bt in nt.GetDeclaredInterfaces(null)) + foreach (var bt in type.GetDeclaredInterfaces(null)) { InterfaceDependsClosure(bt, partialClosure); // containment is not interesting for interfaces as they cannot nest in C# diff --git a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs index d418e5abc56..cc0365d5b1b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs @@ -561,6 +561,32 @@ internal virtual ImmutableArray GetSimpleNonTypeMembers(string name) /// returns an empty ImmutableArray. Never returns null. public abstract override ImmutableArray GetTypeMembers(string name, int arity); + /// + /// Get all instance field and event members. + /// + /// + /// For source symbols may be called while calculating + /// . + /// + internal virtual IEnumerable GetInstanceFieldsAndEvents() + { + return GetMembersUnordered().Where(IsInstanceFieldOrEvent); + } + + protected static Func IsInstanceFieldOrEvent = symbol => + { + if (!symbol.IsStatic) + { + switch (symbol.Kind) + { + case SymbolKind.Field: + case SymbolKind.Event: + return true; + } + } + return false; + }; + /// /// Get this accessibility that was declared on this symbol. For symbols that do not have /// accessibility declared on them, returns NotApplicable. diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs index 044ceb244b8..a1505a155b7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs @@ -431,10 +431,6 @@ internal override NamedTypeSymbol BaseTypeNoUseSiteDiagnostics if (ReferenceEquals(_lazyBaseType, ErrorTypeSymbol.UnknownResultType)) { NamedTypeSymbol acyclicBase = GetDeclaredBaseType(null); - if (BaseTypeAnalysis.ClassDependsOn(acyclicBase, this)) - { - return CyclicInheritanceError(this, acyclicBase); - } if ((object)acyclicBase == null) { @@ -446,6 +442,11 @@ internal override NamedTypeSymbol BaseTypeNoUseSiteDiagnostics } } + if ((object)acyclicBase != null && BaseTypeAnalysis.ClassDependsOn(acyclicBase, this)) + { + return CyclicInheritanceError(this, acyclicBase); + } + Interlocked.CompareExchange(ref _lazyBaseType, acyclicBase, ErrorTypeSymbol.UnknownResultType); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index f8099181986..8cd981476d9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -1297,6 +1297,12 @@ private MembersAndInitializers GetMembersAndInitializers() return _lazyMembersDictionary; } + internal override IEnumerable GetInstanceFieldsAndEvents() + { + var membersAndInitializers = this.GetMembersAndInitializers(); + return membersAndInitializers.NonTypeNonIndexerMembers.Where(IsInstanceFieldOrEvent); + } + protected void AfterMembersChecks(DiagnosticBag diagnostics) { if (IsInterface) @@ -1769,7 +1775,7 @@ private bool HasStructCircularity(DiagnosticBag diagnostics) var type = field.Type; if (((object)type != null) && (type.TypeKind == TypeKind.Struct) && - BaseTypeAnalysis.StructDependsOn(type, this) && + BaseTypeAnalysis.StructDependsOn((NamedTypeSymbol)type, this) && !type.IsPrimitiveRecursiveStruct()) // allow System.Int32 to contain a field of its own type { // If this is a backing field, report the error on the associated property. diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs index 98750937dc6..d49b764b1ab 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs @@ -37,14 +37,6 @@ internal sealed class SourceMemberMethodSymbol : SourceMethodSymbol /// private SourceMemberMethodSymbol _otherPartOfPartial; - /// - /// A binder to use for binding generic constraints. The field is only non-null while the .ctor - /// is executing, and allows constraints to be bound before the method is added to the - /// containing type. (Until the method symbol has been added to the container, we cannot - /// get a binder for the method without triggering a recursive attempt to bind the method.) - /// - private readonly Binder _constraintClauseBinder; - public static SourceMemberMethodSymbol CreateMethodSymbol( NamedTypeSymbol containingType, Binder bodyBinder, @@ -63,7 +55,7 @@ internal sealed class SourceMemberMethodSymbol : SourceMethodSymbol ? MethodKind.Ordinary : MethodKind.ExplicitInterfaceImplementation; - return new SourceMemberMethodSymbol(containingType, explicitInterfaceType, name, location, bodyBinder, syntax, methodKind, diagnostics); + return new SourceMemberMethodSymbol(containingType, explicitInterfaceType, name, location, syntax, methodKind, diagnostics); } private SourceMemberMethodSymbol( @@ -71,7 +63,6 @@ internal sealed class SourceMemberMethodSymbol : SourceMethodSymbol TypeSymbol explicitInterfaceType, string name, Location location, - Binder bodyBinder, MethodDeclarationSyntax syntax, MethodKind methodKind, DiagnosticBag diagnostics) : @@ -103,25 +94,9 @@ internal sealed class SourceMemberMethodSymbol : SourceMethodSymbol this.MakeFlags(methodKind, declarationModifiers, returnsVoid, isExtensionMethod, isMetadataVirtualIgnoringModifiers); - // NOTE: by creating a WithMethodTypeParametersBinder, we are effectively duplicating the - // functionality of the BinderFactory. Unfortunately, we cannot use the BinderFactory - // because it depends on having access to the member list of our containing type and - // that list cannot be complete because we're not finished constructing this member. - // TODO: at least keep this in sync with BinderFactory.VisitMethodDeclaration. - bodyBinder = bodyBinder.WithUnsafeRegionIfNecessary(modifiers); - - Binder withTypeParamsBinder; - if (syntax.Arity == 0) - { - withTypeParamsBinder = bodyBinder; - _typeParameters = ImmutableArray.Empty; - } - else - { - var parameterBinder = new WithMethodTypeParametersBinder(this, bodyBinder); - withTypeParamsBinder = parameterBinder; - _typeParameters = MakeTypeParameters(syntax, diagnostics); - } + _typeParameters = (syntax.Arity == 0) ? + ImmutableArray.Empty : + MakeTypeParameters(syntax, diagnostics); bool hasBlockBody = syntax.Body != null; _isExpressionBodied = !hasBlockBody && syntax.ExpressionBody != null; @@ -136,23 +111,6 @@ internal sealed class SourceMemberMethodSymbol : SourceMethodSymbol { diagnostics.Add(info, location); } - - if (this.IsPartial) - { - // Partial methods must be completed early because they are matched up - // by signature while producing the enclosing type's member list. However, - // that means any type parameter constraints will be bound before the method - // is added to the containing type. To enable binding of constraints before the - // .ctor completes we hold on to the current binder while the .ctor is executing. - // If we change the handling of partial methods, so that partial methods are - // completed lazily, the 'constraintClauseBinder' field should be removed. - _constraintClauseBinder = withTypeParamsBinder; - - state.NotePartComplete(CompletionPart.StartMethodChecks); - MethodChecks(syntax, withTypeParamsBinder, diagnostics); - state.NotePartComplete(CompletionPart.FinishMethodChecks); - _constraintClauseBinder = null; - } } public override bool ReturnsVoid @@ -453,7 +411,7 @@ protected sealed override void LazyAsyncMethodChecks(CancellationToken cancellat protected override void MethodChecks(DiagnosticBag diagnostics) { var syntax = GetSyntax(); - var withTypeParamsBinder = this.DeclaringCompilation.GetBinderFactory(syntax.SyntaxTree).GetBinder(syntax.ReturnType); + var withTypeParamsBinder = this.DeclaringCompilation.GetBinderFactory(syntax.SyntaxTree).GetBinder(syntax.ReturnType, syntax, this); MethodChecks(syntax, withTypeParamsBinder, diagnostics); } @@ -512,17 +470,9 @@ private ImmutableArray MakeTypeParameterConstrain } var syntaxTree = syntax.SyntaxTree; - - // If we're binding these constraints before the method has been - // fully constructed (see partial method comment in .ctor), we have - // a binder. Otherwise, lookup the binder in the BinderFactory. - var binder = _constraintClauseBinder; - if (binder == null) - { - var compilation = this.DeclaringCompilation; - var binderFactory = compilation.GetBinderFactory(syntaxTree); - binder = binderFactory.GetBinder(constraintClauses[0]); - } + var compilation = this.DeclaringCompilation; + var binderFactory = compilation.GetBinderFactory(syntaxTree); + var binder = binderFactory.GetBinder(constraintClauses[0]); // Wrap binder from factory in a generic constraints specific binder // to avoid checking constraints when binding type names. diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs index 22e59c1c848..51e58d60545 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs @@ -3001,6 +3001,19 @@ public unsafe struct S Diagnostic(ErrorCode.ERR_ManagedAddr, "S*").WithArguments("S")); } + [WorkItem(10195, "https://github.com/dotnet/roslyn/issues/10195")] + [Fact] + public void PointerToStructInPartialMethodSignature() + { + string text = +@"unsafe partial struct S +{ + partial void M(S *p) { } + partial void M(S *p); +}"; + CreateCompilationWithMscorlib(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + #endregion IsManagedType #region AddressOf operand kinds -- GitLab