提交 d31b4b6e 编写于 作者: C Charles Stoner

Allow calls to IsManagedType while computing GetMembers

上级 583c674c
......@@ -16,6 +16,8 @@ internal sealed partial class BinderFactory
private sealed class BinderFactoryVisitor : CSharpSyntaxVisitor<Binder>
{
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)
{
......
......@@ -87,34 +87,38 @@ private bool InScript
}
/// <summary>
/// Note, there is no guarantee that the factory always gives back the same binder instance for the same <param name="node"/>.
/// Return binder for binding at node.
/// <paramref name="memberDeclarationOpt"/> and <paramref name="memberOpt"/>
/// are optional syntax and symbol for the member containing <paramref name="node"/>.
/// If provided, the <see cref="BinderFactoryVisitor"/> 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.
/// </summary>
internal Binder GetBinder(CSharpSyntaxNode node)
/// <remarks>
/// Note, there is no guarantee that the factory always gives back the same binder instance for the same node.
/// </remarks>
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;
......
// 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<HashSet<Symbol>> s_hsPool =
new ObjectPool<HashSet<Symbol>>(() => new HashSet<Symbol>(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<Symbol>.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<Symbol> partialClosure)
private static void ClassDependsClosure(NamedTypeSymbol type, CSharpCompilation currentCompilation, HashSet<Symbol> 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<Symbol>.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<Symbol> partialClosure, NamedTypeSymbol on)
private static void StructDependsClosure(NamedTypeSymbol type, HashSet<Symbol> 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<T> { public T t; }
......@@ -90,7 +72,8 @@ private static void StructDependsClosure(TypeSymbol type, HashSet<Symbol> 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<Symbol> 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<Symbol> closure = s_hsPool.Allocate();
bool result = DependsOnDefinitelyManagedType(type, closure);
closure.Clear();
s_hsPool.Free(closure);
var hs = PooledHashSet<Symbol>.GetInstance();
bool result = DependsOnDefinitelyManagedType(type, hs);
hs.Free();
return result;
}
private static bool DependsOnDefinitelyManagedType(NamedTypeSymbol type, HashSet<Symbol> 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<Symbol>.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<Symbol> partialClosure)
private static void InterfaceDependsClosure(NamedTypeSymbol type, HashSet<Symbol> 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#
......
......@@ -561,6 +561,32 @@ internal virtual ImmutableArray<Symbol> GetSimpleNonTypeMembers(string name)
/// returns an empty ImmutableArray. Never returns null.</returns>
public abstract override ImmutableArray<NamedTypeSymbol> GetTypeMembers(string name, int arity);
/// <summary>
/// Get all instance field and event members.
/// </summary>
/// <remarks>
/// For source symbols may be called while calculating
/// <see cref="NamespaceOrTypeSymbol.GetMembersUnordered"/>.
/// </remarks>
internal virtual IEnumerable<Symbol> GetInstanceFieldsAndEvents()
{
return GetMembersUnordered().Where(IsInstanceFieldOrEvent);
}
protected static Func<Symbol, bool> IsInstanceFieldOrEvent = symbol =>
{
if (!symbol.IsStatic)
{
switch (symbol.Kind)
{
case SymbolKind.Field:
case SymbolKind.Event:
return true;
}
}
return false;
};
/// <summary>
/// Get this accessibility that was declared on this symbol. For symbols that do not have
/// accessibility declared on them, returns NotApplicable.
......
......@@ -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);
}
......
......@@ -1297,6 +1297,12 @@ private MembersAndInitializers GetMembersAndInitializers()
return _lazyMembersDictionary;
}
internal override IEnumerable<Symbol> 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.
......
......@@ -37,14 +37,6 @@ internal sealed class SourceMemberMethodSymbol : SourceMethodSymbol
/// </summary>
private SourceMemberMethodSymbol _otherPartOfPartial;
/// <summary>
/// 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.)
/// </summary>
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<TypeParameterSymbol>.Empty;
}
else
{
var parameterBinder = new WithMethodTypeParametersBinder(this, bodyBinder);
withTypeParamsBinder = parameterBinder;
_typeParameters = MakeTypeParameters(syntax, diagnostics);
}
_typeParameters = (syntax.Arity == 0) ?
ImmutableArray<TypeParameterSymbol>.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<TypeParameterConstraintClause> 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.
......
......@@ -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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册