| File: Symbols\ConstantValueUtils.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System.Collections.Immutable; using System.Collections.Generic; using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp.Symbols { internal sealed class EvaluatedConstant { public readonly ConstantValue Value; public readonly ReadOnlyBindingDiagnostic<AssemblySymbol> Diagnostics; public EvaluatedConstant(ConstantValue value, ReadOnlyBindingDiagnostic<AssemblySymbol> diagnostics) { this.Value = value; this.Diagnostics = diagnostics.NullToEmpty(); } } internal static class ConstantValueUtils { public static ConstantValue EvaluateFieldConstant( SourceFieldSymbol symbol, EqualsValueClauseSyntax equalsValueNode, HashSet<SourceFieldSymbolWithSyntaxReference> dependencies, bool earlyDecodingWellKnownAttributes, BindingDiagnosticBag diagnostics) { var compilation = symbol.DeclaringCompilation; var binderFactory = compilation.GetBinderFactory(equalsValueNode.SyntaxTree); var binder = binderFactory.GetBinder(equalsValueNode); binder = new WithPrimaryConstructorParametersBinder(symbol.ContainingType, binder); if (earlyDecodingWellKnownAttributes) { binder = new EarlyWellKnownAttributeBinder(binder); } var inProgressBinder = new ConstantFieldsInProgressBinder(new ConstantFieldsInProgress(symbol, dependencies), binder); BoundFieldEqualsValue boundValue = BindFieldOrEnumInitializer(inProgressBinder, symbol, equalsValueNode, diagnostics); var value = GetAndValidateConstantValue(boundValue.Value, symbol, symbol.Type, equalsValueNode.Value, diagnostics); Debug.Assert(value != null); return value; } private static BoundFieldEqualsValue BindFieldOrEnumInitializer( Binder binder, FieldSymbol fieldSymbol, EqualsValueClauseSyntax initializer, BindingDiagnosticBag diagnostics) { var enumConstant = fieldSymbol as SourceEnumConstantSymbol; Binder collisionDetector = new LocalScopeBinder(binder); collisionDetector = new ExecutableCodeBinder(initializer, fieldSymbol, collisionDetector); BoundFieldEqualsValue result; if ((object)enumConstant != null) { result = collisionDetector.BindEnumConstantInitializer(enumConstant, initializer, diagnostics); } else { result = collisionDetector.BindFieldInitializer(fieldSymbol, initializer, diagnostics); } return result; } internal static ConstantValue GetAndValidateConstantValue( BoundExpression boundValue, Symbol thisSymbol, TypeSymbol typeSymbol, SyntaxNode initValueNode, BindingDiagnosticBag diagnostics) { var value = ConstantValue.Bad; CheckLangVersionForConstantValue(boundValue, diagnostics); if (!boundValue.HasAnyErrors) { if (typeSymbol.TypeKind == TypeKind.TypeParameter) { diagnostics.Add(ErrorCode.ERR_InvalidConstantDeclarationType, initValueNode.Location, thisSymbol, typeSymbol); } else { bool hasDynamicConversion = false; var unconvertedBoundValue = boundValue; while (unconvertedBoundValue.Kind == BoundKind.Conversion) { var conversion = (BoundConversion)unconvertedBoundValue; hasDynamicConversion = hasDynamicConversion || conversion.ConversionKind.IsDynamic(); unconvertedBoundValue = conversion.Operand; } // If we have already computed the unconverted constant value, then this call is cheap // because BoundConversions store their constant values (i.e. not recomputing anything). var constantValue = boundValue.ConstantValueOpt; var unconvertedConstantValue = unconvertedBoundValue.ConstantValueOpt; if (unconvertedConstantValue != null && !unconvertedConstantValue.IsNull && typeSymbol.IsReferenceType && typeSymbol.SpecialType != SpecialType.System_String) { // Suppose we are in this case: // // const object x = "some_string" // // A constant of type object can only be initialized to // null; it may not contain an implicit reference conversion // from string. // // Give a special error for that case. diagnostics.Add(ErrorCode.ERR_NotNullConstRefField, initValueNode.Location, thisSymbol, typeSymbol); // If we get here, then the constantValue will likely be null. // However, it seems reasonable to assume that the programmer will correct the error not // by changing the value to "null", but by updating the type of the constant. Consequently, // we retain the unconverted constant value so that it can propagate through the rest of // constant folding. constantValue = constantValue ?? unconvertedConstantValue; } if (constantValue != null && !hasDynamicConversion) { value = constantValue; } else { diagnostics.Add(ErrorCode.ERR_NotConstantExpression, initValueNode.Location, thisSymbol); } } } return value; } private sealed class CheckConstantInterpolatedStringValidity : BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator { internal readonly BindingDiagnosticBag diagnostics; public CheckConstantInterpolatedStringValidity(BindingDiagnosticBag diagnostics) { this.diagnostics = diagnostics; } public override BoundNode VisitInterpolatedString(BoundInterpolatedString node) { Binder.CheckFeatureAvailability(node.Syntax, MessageID.IDS_FeatureConstantInterpolatedStrings, diagnostics); return null; } } internal static void CheckLangVersionForConstantValue(BoundExpression expression, BindingDiagnosticBag diagnostics) { if (!(expression.Type is null) && expression.Type.IsStringType()) { var visitor = new CheckConstantInterpolatedStringValidity(diagnostics); visitor.Visit(expression); } } } }