diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.cs b/src/Compilers/CSharp/Portable/Binder/Binder.cs index 74787aa500a5a..205804acb6c4f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.cs @@ -65,11 +65,8 @@ internal bool IsEarlyAttributeBinder } } - // Is the given node being bound as a nameof(...) operator? - protected virtual bool IsNameofArgument(SyntaxNode node) - { - return false; - } + // Return the nearest enclosing node being bound as a nameof(...) argument, if any, or null if none. + protected virtual SyntaxNode EnclosingNameofArgument => null; /// /// Get the next binder in which to look up a name, if not found by this binder. diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 57d5437d104e9..022f0e3393842 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -1052,7 +1052,8 @@ private BoundExpression BindNonMethod(SimpleNameSyntax node, Symbol symbol, Diag case SymbolKind.Local: { var localSymbol = (LocalSymbol)symbol; - var constantValueOpt = localSymbol.IsConst ? localSymbol.GetConstantValue(node, this.LocalInProgress, diagnostics) : null; + var constantValueOpt = localSymbol.IsConst && this.EnclosingNameofArgument == null + ? localSymbol.GetConstantValue(node, this.LocalInProgress, diagnostics) : null; TypeSymbol type; Location localSymbolLocation = localSymbol.Locations[0]; @@ -1277,7 +1278,7 @@ private BoundExpression SynthesizeReceiver(SimpleNameSyntax node, Symbol member, if (currentType.IsEqualToOrDerivedFrom(member.ContainingType, ignoreDynamic: false, useSiteDiagnostics: ref useSiteDiagnostics)) { bool hasErrors = false; - if (!IsNameofArgument(node)) + if (EnclosingNameofArgument != node) { if (InFieldInitializer && !currentType.IsScriptClass) { @@ -4594,7 +4595,7 @@ private bool IsUsingAliasInScope(string name) Error(diagnostics, ErrorCode.ERR_BadSKunknown, boundLeft.Syntax, leftType, MessageID.IDS_SK_TYVAR.Localize()); return BadExpression(node, LookupResultKind.NotAValue, boundLeft); } - else if (this.IsNameofArgument(node)) + else if (this.EnclosingNameofArgument == node) { // Support selecing an extension method from a type name in nameof(.) return BindInstanceMemberAccess(node, right, boundLeft, rightName, rightArity, typeArgumentsSyntax, typeArguments, invoked, diagnostics); @@ -5244,7 +5245,7 @@ private static void CombineExtensionMethodArguments(BoundExpression receiver, An ConstantValue constantValueOpt = null; - if (fieldSymbol.IsConst) + if (fieldSymbol.IsConst && this.EnclosingNameofArgument == null) { constantValueOpt = fieldSymbol.GetConstantValue(this.ConstantFieldsInProgress, this.IsEarlyAttributeBinder); if (constantValueOpt == ConstantValue.Unset) @@ -5386,7 +5387,7 @@ private bool InEnumMemberInitializer() } else { - if (instanceReceiver == false && !IsNameofArgument(node)) + if (instanceReceiver == false && EnclosingNameofArgument != node) { Error(diagnostics, ErrorCode.ERR_ObjectRequired, node, symbol); resultKind = LookupResultKind.StaticInstanceMismatch; diff --git a/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs b/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs index 2b3a9743687cb..9dbfa3d00177c 100644 --- a/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs @@ -19,9 +19,6 @@ public NameofBinder(SyntaxNode nameofArgument, Binder next) : base(next) _nameofArgument = nameofArgument; } - protected override bool IsNameofArgument(SyntaxNode possibleNameofArgument) - { - return possibleNameofArgument == _nameofArgument; - } + protected override SyntaxNode EnclosingNameofArgument => _nameofArgument; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs index 48092dcc24d7f..2201cf3ab6b58 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs @@ -9,104 +9,104 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { - /// - /// Represents a local variable in a method body. - /// - internal class SourceLocalSymbol : LocalSymbol - { - protected readonly Binder binder; - - /// - /// Might not be a method symbol. - /// - private readonly Symbol _containingSymbol; - - private readonly SyntaxToken _identifierToken; - private readonly ImmutableArray _locations; - private readonly TypeSyntax _typeSyntax; - private readonly LocalDeclarationKind _declarationKind; - private TypeSymbol _type; - - /// - /// There are three ways to initialize a fixed statement local: - /// 1) with an address; - /// 2) with an array (or fixed-size buffer); or - /// 3) with a string. - /// - /// In the first two cases, the resulting local will be emitted with a "pinned" modifier. - /// In the third case, it is not the fixed statement local but a synthesized temp that is pinned. - /// Unfortunately, we can't distinguish these cases when the local is declared; we only know - /// once we have bound the initializer. - /// - /// - /// CompareExchange doesn't support bool, so use an int. First bit is true/false, second bit - /// is read/unread (debug-only). - /// - private int _isSpecificallyNotPinned; - - private SourceLocalSymbol( - Symbol containingSymbol, - Binder binder, - TypeSyntax typeSyntax, - SyntaxToken identifierToken, - LocalDeclarationKind declarationKind) - { - Debug.Assert(identifierToken.Kind() != SyntaxKind.None); - Debug.Assert(declarationKind != LocalDeclarationKind.None); - - this.binder = binder; - _containingSymbol = containingSymbol; - _identifierToken = identifierToken; - _typeSyntax = typeSyntax; - _declarationKind = declarationKind; - - // create this eagerly as it will always be needed for the EnsureSingleDefinition - _locations = ImmutableArray.Create(identifierToken.GetLocation()); - } - - public static SourceLocalSymbol MakeForeachLocal( - MethodSymbol containingMethod, - ForEachLoopBinder binder, - TypeSyntax typeSyntax, - SyntaxToken identifierToken, - ExpressionSyntax collection) - { - return new ForEachLocal(containingMethod, binder, typeSyntax, identifierToken, collection, LocalDeclarationKind.ForEachIterationVariable); - } - - public static SourceLocalSymbol MakeLocal( - Symbol containingSymbol, - Binder binder, - TypeSyntax typeSyntax, - SyntaxToken identifierToken, - LocalDeclarationKind declarationKind, - EqualsValueClauseSyntax initializer = null) - { - Debug.Assert(declarationKind != LocalDeclarationKind.ForEachIterationVariable); - return (initializer == null) ? - new SourceLocalSymbol(containingSymbol, binder, typeSyntax, identifierToken, declarationKind) : - new LocalWithInitializer(containingSymbol, binder, typeSyntax, identifierToken, initializer, declarationKind); - } - - internal override bool IsImportedFromMetadata - { - get { return false; } - } - - internal override LocalDeclarationKind DeclarationKind - { - get { return _declarationKind; } - } - - internal override SynthesizedLocalKind SynthesizedKind - { - get { return SynthesizedLocalKind.UserDefined; } - } - - internal override bool IsPinned - { - get - { + /// + /// Represents a local variable in a method body. + /// + internal class SourceLocalSymbol : LocalSymbol + { + protected readonly Binder binder; + + /// + /// Might not be a method symbol. + /// + private readonly Symbol _containingSymbol; + + private readonly SyntaxToken _identifierToken; + private readonly ImmutableArray _locations; + private readonly TypeSyntax _typeSyntax; + private readonly LocalDeclarationKind _declarationKind; + private TypeSymbol _type; + + /// + /// There are three ways to initialize a fixed statement local: + /// 1) with an address; + /// 2) with an array (or fixed-size buffer); or + /// 3) with a string. + /// + /// In the first two cases, the resulting local will be emitted with a "pinned" modifier. + /// In the third case, it is not the fixed statement local but a synthesized temp that is pinned. + /// Unfortunately, we can't distinguish these cases when the local is declared; we only know + /// once we have bound the initializer. + /// + /// + /// CompareExchange doesn't support bool, so use an int. First bit is true/false, second bit + /// is read/unread (debug-only). + /// + private int _isSpecificallyNotPinned; + + private SourceLocalSymbol( + Symbol containingSymbol, + Binder binder, + TypeSyntax typeSyntax, + SyntaxToken identifierToken, + LocalDeclarationKind declarationKind) + { + Debug.Assert(identifierToken.Kind() != SyntaxKind.None); + Debug.Assert(declarationKind != LocalDeclarationKind.None); + + this.binder = binder; + _containingSymbol = containingSymbol; + _identifierToken = identifierToken; + _typeSyntax = typeSyntax; + _declarationKind = declarationKind; + + // create this eagerly as it will always be needed for the EnsureSingleDefinition + _locations = ImmutableArray.Create(identifierToken.GetLocation()); + } + + public static SourceLocalSymbol MakeForeachLocal( + MethodSymbol containingMethod, + ForEachLoopBinder binder, + TypeSyntax typeSyntax, + SyntaxToken identifierToken, + ExpressionSyntax collection) + { + return new ForEachLocal(containingMethod, binder, typeSyntax, identifierToken, collection, LocalDeclarationKind.ForEachIterationVariable); + } + + public static SourceLocalSymbol MakeLocal( + Symbol containingSymbol, + Binder binder, + TypeSyntax typeSyntax, + SyntaxToken identifierToken, + LocalDeclarationKind declarationKind, + EqualsValueClauseSyntax initializer = null) + { + Debug.Assert(declarationKind != LocalDeclarationKind.ForEachIterationVariable); + return (initializer == null) ? + new SourceLocalSymbol(containingSymbol, binder, typeSyntax, identifierToken, declarationKind) : + new LocalWithInitializer(containingSymbol, binder, typeSyntax, identifierToken, initializer, declarationKind); + } + + internal override bool IsImportedFromMetadata + { + get { return false; } + } + + internal override LocalDeclarationKind DeclarationKind + { + get { return _declarationKind; } + } + + internal override SynthesizedLocalKind SynthesizedKind + { + get { return SynthesizedLocalKind.UserDefined; } + } + + internal override bool IsPinned + { + get + { #if DEBUG if ((_isSpecificallyNotPinned & 2) == 0) { @@ -114,150 +114,150 @@ internal override bool IsPinned Debug.Assert((_isSpecificallyNotPinned & 2) == 2, "Regardless of which thread won, the read bit should be set."); } #endif - return _declarationKind == LocalDeclarationKind.FixedVariable && (_isSpecificallyNotPinned & 1) == 0; - } - } - - internal void SetSpecificallyNotPinned() - { - Debug.Assert((_isSpecificallyNotPinned & 2) == 0, "Shouldn't be writing after first read."); - Interlocked.CompareExchange(ref _isSpecificallyNotPinned, _isSpecificallyNotPinned | 1, _isSpecificallyNotPinned); - Debug.Assert((_isSpecificallyNotPinned & 1) == 1, "Regardless of which thread won, the flag bit should be set."); - } - - public override Symbol ContainingSymbol - { - get { return _containingSymbol; } - } - - /// - /// Gets the name of the local variable. - /// - public override string Name - { - get - { - return _identifierToken.ValueText; - } - } - - // Get the identifier token that defined this local symbol. This is useful for robustly - // checking if a local symbol actually matches a particular definition, even in the presence - // of duplicates. - internal override SyntaxToken IdentifierToken - { - get - { - return _identifierToken; - } - } - - public override TypeSymbol Type - { - get - { - if ((object)_type == null) - { - TypeSymbol localType = GetTypeSymbol(); - SetTypeSymbol(localType); - } - - return _type; - } - } - - public bool IsVar - { - get - { - if (_typeSyntax.IsVar) - { - bool isVar; - TypeSymbol declType = this.binder.BindType(_typeSyntax, new DiagnosticBag(), out isVar); - return isVar; - } - - return false; - } - } - - private TypeSymbol GetTypeSymbol() - { - var diagnostics = DiagnosticBag.GetInstance(); - - Binder typeBinder = this.binder; - - bool isVar; - TypeSymbol declType = typeBinder.BindType(_typeSyntax, diagnostics, out isVar); - - if (isVar) - { - TypeSymbol inferredType = InferTypeOfVarVariable(diagnostics); - - // If we got a valid result that was not void then use the inferred type - // else create an error type. - if ((object)inferredType != null && - inferredType.SpecialType != SpecialType.System_Void) - { - declType = inferredType; - } - else - { - declType = typeBinder.CreateErrorType("var"); - } - } - - Debug.Assert((object)declType != null); - - diagnostics.Free(); - return declType; - } - - protected virtual TypeSymbol InferTypeOfVarVariable(DiagnosticBag diagnostics) - { - return null; - } - - internal void SetTypeSymbol(TypeSymbol newType) - { - TypeSymbol originalType = _type; - - // In the event that we race to set the type of a local, we should - // always deduce the same type, or deduce that the type is an error. - - Debug.Assert((object)originalType == null || - originalType.IsErrorType() && newType.IsErrorType() || - originalType == newType); - - if ((object)originalType == null) - { - Interlocked.CompareExchange(ref _type, newType, null); - } - } - - /// - /// Gets the locations where the local symbol was originally defined in source. - /// There should not be local symbols from metadata, and there should be only one local variable declared. - /// TODO: check if there are multiple same name local variables - error symbol or local symbol? - /// - public override ImmutableArray Locations - { - get - { - return _locations; - } - } - - internal sealed override SyntaxNode GetDeclaratorSyntax() - { - return _identifierToken.Parent; - } - - public override ImmutableArray DeclaringSyntaxReferences - { - get - { - SyntaxNode node = _identifierToken.Parent; + return _declarationKind == LocalDeclarationKind.FixedVariable && (_isSpecificallyNotPinned & 1) == 0; + } + } + + internal void SetSpecificallyNotPinned() + { + Debug.Assert((_isSpecificallyNotPinned & 2) == 0, "Shouldn't be writing after first read."); + Interlocked.CompareExchange(ref _isSpecificallyNotPinned, _isSpecificallyNotPinned | 1, _isSpecificallyNotPinned); + Debug.Assert((_isSpecificallyNotPinned & 1) == 1, "Regardless of which thread won, the flag bit should be set."); + } + + public override Symbol ContainingSymbol + { + get { return _containingSymbol; } + } + + /// + /// Gets the name of the local variable. + /// + public override string Name + { + get + { + return _identifierToken.ValueText; + } + } + + // Get the identifier token that defined this local symbol. This is useful for robustly + // checking if a local symbol actually matches a particular definition, even in the presence + // of duplicates. + internal override SyntaxToken IdentifierToken + { + get + { + return _identifierToken; + } + } + + public override TypeSymbol Type + { + get + { + if ((object)_type == null) + { + TypeSymbol localType = GetTypeSymbol(); + SetTypeSymbol(localType); + } + + return _type; + } + } + + public bool IsVar + { + get + { + if (_typeSyntax.IsVar) + { + bool isVar; + TypeSymbol declType = this.binder.BindType(_typeSyntax, new DiagnosticBag(), out isVar); + return isVar; + } + + return false; + } + } + + private TypeSymbol GetTypeSymbol() + { + var diagnostics = DiagnosticBag.GetInstance(); + + Binder typeBinder = this.binder; + + bool isVar; + TypeSymbol declType = typeBinder.BindType(_typeSyntax, diagnostics, out isVar); + + if (isVar) + { + TypeSymbol inferredType = InferTypeOfVarVariable(diagnostics); + + // If we got a valid result that was not void then use the inferred type + // else create an error type. + if ((object)inferredType != null && + inferredType.SpecialType != SpecialType.System_Void) + { + declType = inferredType; + } + else + { + declType = typeBinder.CreateErrorType("var"); + } + } + + Debug.Assert((object)declType != null); + + diagnostics.Free(); + return declType; + } + + protected virtual TypeSymbol InferTypeOfVarVariable(DiagnosticBag diagnostics) + { + return null; + } + + internal void SetTypeSymbol(TypeSymbol newType) + { + TypeSymbol originalType = _type; + + // In the event that we race to set the type of a local, we should + // always deduce the same type, or deduce that the type is an error. + + Debug.Assert((object)originalType == null || + originalType.IsErrorType() && newType.IsErrorType() || + originalType == newType); + + if ((object)originalType == null) + { + Interlocked.CompareExchange(ref _type, newType, null); + } + } + + /// + /// Gets the locations where the local symbol was originally defined in source. + /// There should not be local symbols from metadata, and there should be only one local variable declared. + /// TODO: check if there are multiple same name local variables - error symbol or local symbol? + /// + public override ImmutableArray Locations + { + get + { + return _locations; + } + } + + internal sealed override SyntaxNode GetDeclaratorSyntax() + { + return _identifierToken.Parent; + } + + public override ImmutableArray DeclaringSyntaxReferences + { + get + { + SyntaxNode node = _identifierToken.Parent; #if DEBUG switch (_declarationKind) { @@ -281,159 +281,159 @@ public override ImmutableArray DeclaringSyntaxReferences throw ExceptionUtilities.UnexpectedValue(_declarationKind); } #endif - return ImmutableArray.Create(node.GetReference()); - } - } - - internal override bool IsCompilerGenerated - { - get { return false; } - } - - internal override ConstantValue GetConstantValue(SyntaxNode node, LocalSymbol inProgress, DiagnosticBag diagnostics) - { - return null; - } - - internal override ImmutableArray GetConstantValueDiagnostics(BoundExpression boundInitValue) - { - return ImmutableArray.Empty; - } - - internal override RefKind RefKind - { - get { return RefKind.None; } - } - - public sealed override bool Equals(object obj) - { - if (obj == (object)this) - { - return true; - } - - var symbol = obj as SourceLocalSymbol; - return (object)symbol != null - && symbol._identifierToken.Equals(_identifierToken) - && Equals(symbol._containingSymbol, _containingSymbol); - } - - public sealed override int GetHashCode() - { - return Hash.Combine(_identifierToken.GetHashCode(), _containingSymbol.GetHashCode()); - } - - private sealed class LocalWithInitializer : SourceLocalSymbol - { - private readonly EqualsValueClauseSyntax _initializer; - - /// - /// Store the constant value and the corresponding diagnostics together - /// to avoid having the former set by one thread and the latter set by - /// another. - /// - private EvaluatedConstant _constantTuple; - - public LocalWithInitializer( - Symbol containingSymbol, - Binder binder, - TypeSyntax typeSyntax, - SyntaxToken identifierToken, - EqualsValueClauseSyntax initializer, - LocalDeclarationKind declarationKind) : - base(containingSymbol, binder, typeSyntax, identifierToken, declarationKind) - { - Debug.Assert(declarationKind != LocalDeclarationKind.ForEachIterationVariable); - Debug.Assert(initializer != null); - - _initializer = initializer; - } - - protected override TypeSymbol InferTypeOfVarVariable(DiagnosticBag diagnostics) - { - var newBinder = new ImplicitlyTypedLocalBinder(this.binder, this); - var initializerOpt = newBinder.BindInferredVariableInitializer(diagnostics, _initializer, _initializer); - if (initializerOpt != null) - { - return initializerOpt.Type; - } - - return null; - } - - /// - /// Determine the constant value of this local and the corresponding diagnostics. - /// Set both to constantTuple in a single operation for thread safety. - /// - /// Null for the initial call, non-null if we are in the process of evaluating a constant. - /// If we already have the bound node for the initial value, pass it in to avoid recomputing it. - private void MakeConstantTuple(LocalSymbol inProgress, BoundExpression boundInitValue) - { - if (this.IsConst && _constantTuple == null) - { - var value = Microsoft.CodeAnalysis.ConstantValue.Bad; - var initValueNodeLocation = _initializer.Value.Location; - var diagnostics = DiagnosticBag.GetInstance(); - Debug.Assert(inProgress != this); - var type = this.Type; - if (boundInitValue == null) - { - var inProgressBinder = new LocalInProgressBinder(this, this.binder); - boundInitValue = inProgressBinder.BindVariableOrAutoPropInitializer(_initializer, type, diagnostics); - } - - value = ConstantValueUtils.GetAndValidateConstantValue(boundInitValue, this, type, initValueNodeLocation, diagnostics); - Interlocked.CompareExchange(ref _constantTuple, new EvaluatedConstant(value, diagnostics.ToReadOnlyAndFree()), null); - } - } - - internal override ConstantValue GetConstantValue(SyntaxNode node, LocalSymbol inProgress, DiagnosticBag diagnostics = null) - { - if (this.IsConst && inProgress == this) - { - if (diagnostics != null) - { - diagnostics.Add(ErrorCode.ERR_CircConstValue, node.GetLocation(), this); - } - - return Microsoft.CodeAnalysis.ConstantValue.Bad; - } - - MakeConstantTuple(inProgress, boundInitValue: null); - return _constantTuple == null ? null : _constantTuple.Value; - } - - internal override ImmutableArray GetConstantValueDiagnostics(BoundExpression boundInitValue) - { - Debug.Assert(boundInitValue != null); - MakeConstantTuple(inProgress: null, boundInitValue: boundInitValue); - return _constantTuple == null ? ImmutableArray.Empty : _constantTuple.Diagnostics; - } - } - - private sealed class ForEachLocal : SourceLocalSymbol - { - private readonly ExpressionSyntax _collection; - - public ForEachLocal( - Symbol containingSymbol, - Binder binder, - TypeSyntax typeSyntax, - SyntaxToken identifierToken, - ExpressionSyntax collection, - LocalDeclarationKind declarationKind) : - base(containingSymbol, binder, typeSyntax, identifierToken, declarationKind) - { - Debug.Assert(declarationKind == LocalDeclarationKind.ForEachIterationVariable); - _collection = collection; - } - - protected override TypeSymbol InferTypeOfVarVariable(DiagnosticBag diagnostics) - { - // Normally, it would not be safe to cast to a specific binder type. However, we verified the type - // in the factory method call for this symbol. - return ((ForEachLoopBinder)this.binder).InferCollectionElementType(diagnostics, _collection); - } - } - } + return ImmutableArray.Create(node.GetReference()); + } + } + + internal override bool IsCompilerGenerated + { + get { return false; } + } + + internal override ConstantValue GetConstantValue(SyntaxNode node, LocalSymbol inProgress, DiagnosticBag diagnostics) + { + return null; + } + + internal override ImmutableArray GetConstantValueDiagnostics(BoundExpression boundInitValue) + { + return ImmutableArray.Empty; + } + + internal override RefKind RefKind + { + get { return RefKind.None; } + } + + public sealed override bool Equals(object obj) + { + if (obj == (object)this) + { + return true; + } + + var symbol = obj as SourceLocalSymbol; + return (object)symbol != null + && symbol._identifierToken.Equals(_identifierToken) + && Equals(symbol._containingSymbol, _containingSymbol); + } + + public sealed override int GetHashCode() + { + return Hash.Combine(_identifierToken.GetHashCode(), _containingSymbol.GetHashCode()); + } + + private sealed class LocalWithInitializer : SourceLocalSymbol + { + private readonly EqualsValueClauseSyntax _initializer; + + /// + /// Store the constant value and the corresponding diagnostics together + /// to avoid having the former set by one thread and the latter set by + /// another. + /// + private EvaluatedConstant _constantTuple; + + public LocalWithInitializer( + Symbol containingSymbol, + Binder binder, + TypeSyntax typeSyntax, + SyntaxToken identifierToken, + EqualsValueClauseSyntax initializer, + LocalDeclarationKind declarationKind) : + base(containingSymbol, binder, typeSyntax, identifierToken, declarationKind) + { + Debug.Assert(declarationKind != LocalDeclarationKind.ForEachIterationVariable); + Debug.Assert(initializer != null); + + _initializer = initializer; + } + + protected override TypeSymbol InferTypeOfVarVariable(DiagnosticBag diagnostics) + { + var newBinder = new ImplicitlyTypedLocalBinder(this.binder, this); + var initializerOpt = newBinder.BindInferredVariableInitializer(diagnostics, _initializer, _initializer); + if (initializerOpt != null) + { + return initializerOpt.Type; + } + + return null; + } + + /// + /// Determine the constant value of this local and the corresponding diagnostics. + /// Set both to constantTuple in a single operation for thread safety. + /// + /// Null for the initial call, non-null if we are in the process of evaluating a constant. + /// If we already have the bound node for the initial value, pass it in to avoid recomputing it. + private void MakeConstantTuple(LocalSymbol inProgress, BoundExpression boundInitValue) + { + if (this.IsConst && _constantTuple == null) + { + var value = Microsoft.CodeAnalysis.ConstantValue.Bad; + var initValueNodeLocation = _initializer.Value.Location; + var diagnostics = DiagnosticBag.GetInstance(); + Debug.Assert(inProgress != this); + var type = this.Type; + if (boundInitValue == null) + { + var inProgressBinder = new LocalInProgressBinder(this, this.binder); + boundInitValue = inProgressBinder.BindVariableOrAutoPropInitializer(_initializer, type, diagnostics); + } + + value = ConstantValueUtils.GetAndValidateConstantValue(boundInitValue, this, type, initValueNodeLocation, diagnostics); + Interlocked.CompareExchange(ref _constantTuple, new EvaluatedConstant(value, diagnostics.ToReadOnlyAndFree()), null); + } + } + + internal override ConstantValue GetConstantValue(SyntaxNode node, LocalSymbol inProgress, DiagnosticBag diagnostics = null) + { + if (this.IsConst && inProgress == this) + { + if (diagnostics != null) + { + diagnostics.Add(ErrorCode.ERR_CircConstValue, node.GetLocation(), this); + } + + return Microsoft.CodeAnalysis.ConstantValue.Bad; + } + + MakeConstantTuple(inProgress, boundInitValue: null); + return _constantTuple == null ? null : _constantTuple.Value; + } + + internal override ImmutableArray GetConstantValueDiagnostics(BoundExpression boundInitValue) + { + Debug.Assert(boundInitValue != null); + MakeConstantTuple(inProgress: null, boundInitValue: boundInitValue); + return _constantTuple == null ? ImmutableArray.Empty : _constantTuple.Diagnostics; + } + } + + private sealed class ForEachLocal : SourceLocalSymbol + { + private readonly ExpressionSyntax _collection; + + public ForEachLocal( + Symbol containingSymbol, + Binder binder, + TypeSyntax typeSyntax, + SyntaxToken identifierToken, + ExpressionSyntax collection, + LocalDeclarationKind declarationKind) : + base(containingSymbol, binder, typeSyntax, identifierToken, declarationKind) + { + Debug.Assert(declarationKind == LocalDeclarationKind.ForEachIterationVariable); + _collection = collection; + } + + protected override TypeSymbol InferTypeOfVarVariable(DiagnosticBag diagnostics) + { + // Normally, it would not be safe to cast to a specific binder type. However, we verified the type + // in the factory method call for this symbol. + return ((ForEachLoopBinder)this.binder).InferCollectionElementType(diagnostics, _collection); + } + } + } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index dd8960406572f..072bf3d0f7100 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -1138,5 +1138,22 @@ public static void Main(string[] args) Assert.Equal(CandidateReason.MemberGroup, symbolInfo.CandidateReason); Assert.Equal(1, symbolInfo.CandidateSymbols.Length); } + + [Fact, WorkItem(40, "github.com/dotnet/roslyn")] + public void ConstInitializerUsingSelf() + { + var source = +@"public class X +{ + const string N1 = nameof(N1); + public static void Main() + { + const string N2 = nameof(N2); + System.Console.WriteLine(N1 + N2); + } +}"; + var compilation = CreateCompilationWithMscorlib45(source); + var comp = CompileAndVerify(source, expectedOutput: @"N1N2"); + } } }