public override bool Equals(Symbol other, TypeCompareKind compareKind) { if (other == (object)this) { return(true); } if (!(other is LocalSymbol otherLocal)) { return(false); } SourceLocalSymbol?otherSource = otherLocal switch { UpdatedContainingSymbolAndNullableAnnotationLocal updated => updated._underlyingLocal, SourceLocalSymbol source => source, _ => null }; if (otherSource is null || !_underlyingLocal.Equals(otherSource, compareKind)) { return(false); } var ignoreNullable = (compareKind & TypeCompareKind.AllNullableIgnoreOptions) != 0; return(ignoreNullable || (TypeWithAnnotations.Equals(otherLocal.TypeWithAnnotations, compareKind) && ContainingSymbol.Equals(otherLocal.ContainingSymbol, compareKind))); }
internal UpdatedContainingSymbolAndNullableAnnotationLocal( SourceLocalSymbol underlyingLocal, Symbol updatedContainingSymbol, TypeWithAnnotations updatedType ) : this(underlyingLocal, updatedContainingSymbol, updatedType, assertContaining : true) { }
private UpdatedContainingSymbolAndNullableAnnotationLocal(SourceLocalSymbol underlyingLocal, Symbol updatedContainingSymbol, TypeWithAnnotations updatedType, bool assertContaining) { RoslynDebug.Assert(underlyingLocal is object); RoslynDebug.Assert(updatedContainingSymbol is object); Debug.Assert(!assertContaining || updatedContainingSymbol.Equals(underlyingLocal.ContainingSymbol, TypeCompareKind.AllNullableIgnoreOptions)); ContainingSymbol = updatedContainingSymbol; TypeWithAnnotations = updatedType; _underlyingLocal = underlyingLocal; }
/// <summary> /// Creates a new <see cref="UpdatedContainingSymbolAndNullableAnnotationLocal"/> for testing purposes, /// which does not verify that the containing symbol matches the original containing symbol. /// </summary> internal static UpdatedContainingSymbolAndNullableAnnotationLocal CreateForTest( SourceLocalSymbol underlyingLocal, Symbol updatedContainingSymbol, TypeWithAnnotations updatedType ) { return(new UpdatedContainingSymbolAndNullableAnnotationLocal( underlyingLocal, updatedContainingSymbol, updatedType, assertContaining: false )); }
private bool IsValidFixedVariableInitializer(TypeSymbol declType, SourceLocalSymbol localSymbol, ref BoundExpression initializerOpt, DiagnosticBag diagnostics) { Debug.Assert(!ReferenceEquals(declType, null)); Debug.Assert(declType.IsPointerType()); if (ReferenceEquals(initializerOpt, null)) { return false; } TypeSymbol initializerType = initializerOpt.Type; CSharpSyntaxNode initializerSyntax = initializerOpt.Syntax; if (ReferenceEquals(initializerType, null)) { // Dev10 just reports the assignment conversion error (which must occur, unless the initializer is a null literal). initializerOpt = GenerateConversionForAssignment(declType, initializerOpt, diagnostics); if (!initializerOpt.HasAnyErrors) { Debug.Assert(initializerOpt.Kind == BoundKind.Conversion && ((BoundConversion)initializerOpt).Operand.IsLiteralNull(), "All other typeless expressions should have conversion errors"); // CONSIDER: this is a very confusing error message, but it's what Dev10 reports. Error(diagnostics, ErrorCode.ERR_FixedNotNeeded, initializerSyntax); } } else if (initializerType.SpecialType == SpecialType.System_String) { // See ExpressionBinder::bindPtrToString TypeSymbol elementType = this.GetSpecialType(SpecialType.System_Char, diagnostics, initializerSyntax); Debug.Assert(!elementType.IsManagedType); initializerOpt = GetFixedLocalCollectionInitializer(initializerOpt, elementType, declType, false, diagnostics); // The string case is special - we'll pin a synthesized string temp, rather than the pointer local. localSymbol.SetSpecificallyNotPinned(); // UNDONE: ExpressionBinder::CheckFieldUse (something about MarshalByRef) } else if (initializerType.IsArray()) { // See ExpressionBinder::BindPtrToArray (though most of that functionality is now in LocalRewriter). var arrayType = (ArrayTypeSymbol)initializerType; TypeSymbol elementType = arrayType.ElementType; bool hasErrors = false; if (elementType.IsManagedType) { Error(diagnostics, ErrorCode.ERR_ManagedAddr, initializerSyntax, elementType); hasErrors = true; } initializerOpt = GetFixedLocalCollectionInitializer(initializerOpt, elementType, declType, hasErrors, diagnostics); } else { if (!initializerOpt.HasAnyErrors) { switch (initializerOpt.Kind) { case BoundKind.AddressOfOperator: // OK break; case BoundKind.Conversion: // The following assertion would not be correct because there might be an implicit conversion after (above) the explicit one. //Debug.Assert(((BoundConversion)initializerOpt).ExplicitCastInCode, "The assignment conversion hasn't been applied yet, so this must be from source."); // NOTE: Dev10 specifically doesn't report this error for the array or string cases. Error(diagnostics, ErrorCode.ERR_BadCastInFixed, initializerSyntax); break; case BoundKind.FieldAccess: var fa = (BoundFieldAccess)initializerOpt; if (!fa.FieldSymbol.IsFixed) { Error(diagnostics, ErrorCode.ERR_FixedNotNeeded, initializerSyntax); } break; default: // CONSIDER: this is a very confusing error message, but it's what Dev10 reports. Error(diagnostics, ErrorCode.ERR_FixedNotNeeded, initializerSyntax); break; } } initializerOpt = GenerateConversionForAssignment(declType, initializerOpt, diagnostics); } return true; }
protected BoundLocalDeclaration BindVariableDeclaration( SourceLocalSymbol localSymbol, LocalDeclarationKind kind, bool isVar, VariableDeclaratorSyntax declarator, TypeSyntax typeSyntax, TypeSymbol declTypeOpt, AliasSymbol aliasOpt, DiagnosticBag diagnostics, CSharpSyntaxNode associatedSyntaxNode = null) { Debug.Assert(declarator != null); Debug.Assert((object)declTypeOpt != null || isVar); Debug.Assert(typeSyntax != null); var localDiagnostics = DiagnosticBag.GetInstance(); // if we are not given desired syntax, we use declarator associatedSyntaxNode = associatedSyntaxNode ?? declarator; bool hasErrors = false; BoundExpression initializerOpt; // Check for variable declaration errors. hasErrors |= this.ValidateDeclarationNameConflictsInScope(localSymbol, localDiagnostics); EqualsValueClauseSyntax equalsValueClauseSyntax = declarator.Initializer; if (isVar) { aliasOpt = null; var binder = new ImplicitlyTypedLocalBinder(this, localSymbol); initializerOpt = binder.BindInferredVariableInitializer(localDiagnostics, equalsValueClauseSyntax, declarator); // If we got a good result then swap the inferred type for the "var" if ((object)initializerOpt?.Type != null) { declTypeOpt = initializerOpt.Type; if (declTypeOpt.SpecialType == SpecialType.System_Void) { Error(localDiagnostics, ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, declarator, declTypeOpt); declTypeOpt = CreateErrorType("var"); hasErrors = true; } if (!declTypeOpt.IsErrorType()) { if (declTypeOpt.IsStatic) { Error(localDiagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, initializerOpt.Type); hasErrors = true; } } } else { declTypeOpt = CreateErrorType("var"); hasErrors = true; } } else { if (ReferenceEquals(equalsValueClauseSyntax, null)) { initializerOpt = null; } else { // Basically inlined BindVariableInitializer, but with conversion optional. initializerOpt = BindPossibleArrayInitializer(equalsValueClauseSyntax.Value, declTypeOpt, localDiagnostics); if (kind != LocalDeclarationKind.FixedVariable) { // If this is for a fixed statement, we'll do our own conversion since there are some special cases. initializerOpt = GenerateConversionForAssignment(declTypeOpt, initializerOpt, localDiagnostics); } } } Debug.Assert((object)declTypeOpt != null); if (kind == LocalDeclarationKind.FixedVariable) { // NOTE: this is an error, but it won't prevent further binding. if (isVar) { if (!hasErrors) { Error(localDiagnostics, ErrorCode.ERR_ImplicitlyTypedLocalCannotBeFixed, declarator); hasErrors = true; } } if (!declTypeOpt.IsPointerType()) { if (!hasErrors) { Error(localDiagnostics, ErrorCode.ERR_BadFixedInitType, declarator); hasErrors = true; } } else if (!IsValidFixedVariableInitializer(declTypeOpt, localSymbol, ref initializerOpt, localDiagnostics)) { hasErrors = true; } } if (this.ContainingMemberOrLambda.Kind == SymbolKind.Method && ((MethodSymbol)this.ContainingMemberOrLambda).IsAsync && declTypeOpt.IsRestrictedType()) { Error(localDiagnostics, ErrorCode.ERR_BadSpecialByRefLocal, typeSyntax, declTypeOpt); hasErrors = true; } DeclareLocalVariable( localSymbol, declarator.Identifier, declTypeOpt); Debug.Assert((object)localSymbol != null); ImmutableArray<BoundExpression> arguments = BindDeclaratorArguments(declarator, localDiagnostics); if (kind == LocalDeclarationKind.FixedVariable || kind == LocalDeclarationKind.UsingVariable) { // CONSIDER: The error message is "you must provide an initializer in a fixed // CONSIDER: or using declaration". The error message could be targetted to // CONSIDER: the actual situation. "you must provide an initializer in a // CONSIDER: 'fixed' declaration." if (initializerOpt == null) { Error(localDiagnostics, ErrorCode.ERR_FixedMustInit, declarator); hasErrors = true; } } else if (kind == LocalDeclarationKind.Constant && initializerOpt != null && !localDiagnostics.HasAnyResolvedErrors()) { var constantValueDiagnostics = localSymbol.GetConstantValueDiagnostics(initializerOpt); foreach (var diagnostic in constantValueDiagnostics) { diagnostics.Add(diagnostic); hasErrors = true; } } diagnostics.AddRangeAndFree(localDiagnostics); var boundDeclType = new BoundTypeExpression(typeSyntax, aliasOpt, inferredType: isVar, type: declTypeOpt); return new BoundLocalDeclaration(associatedSyntaxNode, localSymbol, boundDeclType, initializerOpt, arguments, hasErrors); }
internal static void DeclareLocalVariable( SourceLocalSymbol symbol, SyntaxToken identifierToken, TypeSymbol type) { // In the original compiler this // method has many side effects; it sets the type // of the local symbol, it gives errors if the local // is a duplicate, it creates new symbols for lambda // expressions, puts stuff in caches, and so on. Debug.Assert((object)symbol != null); Debug.Assert(symbol.IdentifierToken == identifierToken); symbol.SetTypeSymbol(type); // UNDONE: Can we come up with a way to set the type of a local which does // UNDONE: not duplicate work and does not mutate the symbol? }