diff --git a/docs/compilers/Design/Bound Node Design.md b/docs/compilers/Design/Bound Node Design.md new file mode 100644 index 0000000000000000000000000000000000000000..2a75b290cd253485d5aa3f3696163d6e0eaf16ff --- /dev/null +++ b/docs/compilers/Design/Bound Node Design.md @@ -0,0 +1,18 @@ +Bound Node Design +================= + +This document discusses design principles for the bound nodes. + +### The shape of the bound tree should correspond to the shape of the program's static semantics + +Generally speaking, that means that there is an isomorphism between the syntax and bound nodes, except when there is a mismatch between the shape of the syntax and semantics, in which case they model the shape of the semantics. When possible, we prefer the correspondence be as direct as possible. Here are two examples that illustrate this: +1. Parenthesized expressions do not appear in the bound nodes because they have no semantic meaning. +2. Query expressions are given a semantic meaning by correspondence to a translated form, so the bound nodes may model the translated form. + +### Bound nodes should capture all semantic information embedded in the syntax + +A consumer of the bound nodes should not need to examine the syntax from which they were produced to understand the meaning of the bound nodes. All relevant semantic information that comes from the syntax should be summarized in the fields of the bound node. If a consumer of a bound node needs to refer to the syntax to affect the meaning of the code, that is a design smell. + +### Every bound node type is either abstract or sealed + +In `BoundNodes.xml`, a `Node` should have a base that is `BoundNode` or a node type declared `AbstractNode`. Do not inherit a concrete node type from another concrete node type. diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index f840395a473f2741205bf7bc9e35971aefdf999e..62bc48dde8acc7422687f3383319b8d990cb8e73 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -300,6 +300,12 @@ internal BoundExpression BindToNaturalType(BoundExpression expression, Diagnosti hasErrors: true).WithSuppression(defaultExpr.IsSuppressed); } break; + case BoundStackAllocArrayCreation { Type: null } boundStackAlloc: + // This is a context in which the stackalloc could be either a pointer + // or a span. For backward compatibility we treat it as a pointer. + var type = new PointerTypeSymbol(TypeWithAnnotations.Create(boundStackAlloc.ElementType)); + result = GenerateConversionForAssignment(type, boundStackAlloc, diagnostics); + break; default: result = expression; break; @@ -3516,26 +3522,56 @@ private TypeSymbol GetStackAllocType(SyntaxNode node, TypeWithAnnotations elemen { var inLegalPosition = ReportBadStackAllocPosition(node, diagnostics); hasErrors = !inLegalPosition; - if (inLegalPosition && !node.IsLocalVariableDeclarationInitializationForPointerStackalloc()) + if (inLegalPosition && !isStackallocTargetTyped(node)) { CheckFeatureAvailability(node, MessageID.IDS_FeatureRefStructs, diagnostics); var spanType = GetWellKnownType(WellKnownType.System_Span_T, diagnostics, node); - if (!spanType.IsErrorType()) - { - return ConstructNamedType( - type: spanType, - typeSyntax: node.Kind() == SyntaxKind.StackAllocArrayCreationExpression - ? ((StackAllocArrayCreationExpressionSyntax)node).Type - : node, - typeArgumentsSyntax: default, - typeArguments: ImmutableArray.Create(elementTypeWithAnnotations), - basesBeingResolved: null, - diagnostics: diagnostics); - } + return ConstructNamedType( + type: spanType, + typeSyntax: node.Kind() == SyntaxKind.StackAllocArrayCreationExpression + ? ((StackAllocArrayCreationExpressionSyntax)node).Type + : node, + typeArgumentsSyntax: default, + typeArguments: ImmutableArray.Create(elementTypeWithAnnotations), + basesBeingResolved: null, + diagnostics: diagnostics); } + // We treat the stackalloc as target-typed, so we give it a null type for now. return null; + + // Is this a context in which a stackalloc expression could be converted to the corresponding pointer + // type? The only context that permits it is the initialization of a local variable declaration (when + // the declaration appears as a statement or as the first part of a for loop). + static bool isStackallocTargetTyped(SyntaxNode node) + { + Debug.Assert(node != null); + + SyntaxNode equalsValueClause = node.Parent; + + if (!equalsValueClause.IsKind(SyntaxKind.EqualsValueClause)) + { + return false; + } + + SyntaxNode variableDeclarator = equalsValueClause.Parent; + + if (!variableDeclarator.IsKind(SyntaxKind.VariableDeclarator)) + { + return false; + } + + SyntaxNode variableDeclaration = variableDeclarator.Parent; + if (!variableDeclaration.IsKind(SyntaxKind.VariableDeclaration)) + { + return false; + } + + return + variableDeclaration.Parent.IsKind(SyntaxKind.LocalDeclarationStatement) || + variableDeclaration.Parent.IsKind(SyntaxKind.ForStatement); + } } private BoundExpression BindStackAllocWithInitializer( @@ -3548,6 +3584,8 @@ private TypeSymbol GetStackAllocType(SyntaxNode node, TypeWithAnnotations elemen bool hasErrors, ImmutableArray boundInitExprOpt = default) { + Debug.Assert(node.IsKind(SyntaxKind.ImplicitStackAllocArrayCreationExpression) || node.IsKind(SyntaxKind.StackAllocArrayCreationExpression)); + if (boundInitExprOpt.IsDefault) { boundInitExprOpt = BindArrayInitializerExpressions(initSyntax, diagnostics, dimension: 1, rank: 1); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 2cef13079547722d6aa071f757122d353da4984b..0a1ebd368b4f3917877936ab7747329914ee823d 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -844,16 +844,6 @@ protected BoundExpression BindInferredVariableInitializer(DiagnosticBag diagnost BoundExpression expression = BindToNaturalType(BindValue(initializer, diagnostics, valueKind), diagnostics); - if (expression is BoundStackAllocArrayCreation boundStackAlloc && - initializer.IsLocalVariableDeclarationInitializationForPointerStackalloc() && - (initializer.Kind() == SyntaxKind.StackAllocArrayCreationExpression || initializer.Kind() == SyntaxKind.ImplicitStackAllocArrayCreationExpression)) - { - var type = new PointerTypeSymbol(TypeWithAnnotations.Create(boundStackAlloc.ElementType)); - expression = GenerateConversionForAssignment(type, boundStackAlloc, diagnostics, isRefAssignment: refKind != RefKind.None); - } - - expression = BindToNaturalType(expression, diagnostics); - // Certain expressions (null literals, method groups and anonymous functions) have no type of // their own and therefore cannot be the initializer of an implicitly typed local. if (!expression.HasAnyErrors && !expression.HasExpressionType()) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs index 1de5281d084b1675ed085b74baaecc7392512993..9fecb09ea44e573e283be36f0a8b1a0f8f9c660e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs @@ -304,7 +304,7 @@ private static Conversion ToConversion(OverloadResolutionResult re public override Conversion GetStackAllocConversion(BoundStackAllocArrayCreation sourceExpression, TypeSymbol destination, ref HashSet useSiteDiagnostics) { - if (sourceExpression.Syntax.IsLocalVariableDeclarationInitializationForPointerStackalloc()) + if (sourceExpression.NeedsToBeConverted()) { Debug.Assert((object)sourceExpression.Type == null); Debug.Assert((object)sourceExpression.ElementType != null); diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs index 8b0adbbbc9bd0a9cf75897b348c272c803a5b0c5..ffce86633764dceb68484d25fe6ebe7106bdc117 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs @@ -55,6 +55,11 @@ internal bool NeedsToBeConverted() case BoundKind.TupleLiteral: case BoundKind.UnconvertedSwitchExpression: return true; + case BoundKind.StackAllocArrayCreation: + // A BoundStackAllocArrayCreation is given a null type when it is in a + // syntactic context where it could be either a pointer or a span, and + // in that case it requires conversion to one or the other. + return this.Type is null; #if DEBUG case BoundKind.Local when !WasConverted: case BoundKind.Parameter when !WasConverted: diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 59156dfde7d8f0f07e52e98069a195e6bfb4afb6..9fbe25a98feac33fb9b8227caa8ccc2ee2b95c1d 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -1763,13 +1763,23 @@ - + + + + + + + - + diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index 7243fcf1a0c90730dcf5339bee59e02cd2127eba..1174ceac3dadf39f3b0cceb3d07de30cc3b151c7 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -124,13 +124,16 @@ internal partial class BoundFixedLocalCollectionInitializer protected override ImmutableArray Children => ImmutableArray.Create(this.Expression); } - internal partial class BoundStackAllocArrayCreation + internal partial class BoundStackAllocArrayCreationBase { internal static ImmutableArray GetChildInitializers(BoundArrayInitialization arrayInitializer) { return arrayInitializer?.Initializers ?? ImmutableArray.Empty; } + } + internal partial class BoundStackAllocArrayCreation + { protected override ImmutableArray Children => StaticCast.From(GetChildInitializers(this.InitializerOpt).Insert(0, this.Count)); } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index a9b73cd60c6c3b0911898eda82778174444c1f56..d8e7eb203a7b311f4352eb3c1facec7ad3ffbe9f 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -2769,7 +2769,7 @@ public override BoundNode VisitSizeOfOperator(BoundSizeOfOperator node) return null; } - public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreation node) + private BoundNode VisitStackAllocArrayCreationBase(BoundStackAllocArrayCreationBase node) { VisitRvalue(node.Count); @@ -2784,10 +2784,14 @@ public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreat return null; } + public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreation node) + { + return VisitStackAllocArrayCreationBase(node); + } + public override BoundNode VisitConvertedStackAllocExpression(BoundConvertedStackAllocExpression node) { - VisitStackAllocArrayCreation(node); - return null; + return VisitStackAllocArrayCreationBase(node); } public override BoundNode VisitAnonymousObjectCreationExpression(BoundAnonymousObjectCreationExpression node) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 27e26c8ebeb32a024c6849c8ed3cb7c86a77f317..301687b39bc2e64efdb9736513fcb266cffd2f24 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -7574,7 +7574,7 @@ private void SetUnknownResultNullability(BoundExpression expression) public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreation node) { var result = base.VisitStackAllocArrayCreation(node); - Debug.Assert(node.Type is null || node.Type.IsPointerType() || node.Type.IsRefLikeType); + Debug.Assert(node.Type is null || node.Type.IsErrorType() || node.Type.IsPointerType() || node.Type.IsRefLikeType); SetNotNullResult(node); return result; } diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index 3866fdfdeb6586f12f91e39e0eff0c85026377af..2ff010fb591a1eed9c7bcb69b639e8d06c26f850 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -6210,9 +6210,9 @@ public BoundArrayInitialization Update(ImmutableArray initializ } } - internal partial class BoundStackAllocArrayCreation : BoundExpression + internal abstract partial class BoundStackAllocArrayCreationBase : BoundExpression { - protected BoundStackAllocArrayCreation(BoundKind kind, SyntaxNode syntax, TypeSymbol elementType, BoundExpression count, BoundArrayInitialization? initializerOpt, TypeSymbol? type, bool hasErrors = false) + protected BoundStackAllocArrayCreationBase(BoundKind kind, SyntaxNode syntax, TypeSymbol elementType, BoundExpression count, BoundArrayInitialization? initializerOpt, TypeSymbol? type, bool hasErrors = false) : base(kind, syntax, type, hasErrors) { @@ -6224,24 +6224,29 @@ protected BoundStackAllocArrayCreation(BoundKind kind, SyntaxNode syntax, TypeSy this.InitializerOpt = initializerOpt; } + + public new TypeSymbol? Type => base.Type!; + + public TypeSymbol ElementType { get; } + + public BoundExpression Count { get; } + + public BoundArrayInitialization? InitializerOpt { get; } + } + + internal sealed partial class BoundStackAllocArrayCreation : BoundStackAllocArrayCreationBase + { public BoundStackAllocArrayCreation(SyntaxNode syntax, TypeSymbol elementType, BoundExpression count, BoundArrayInitialization? initializerOpt, TypeSymbol? type, bool hasErrors = false) - : base(BoundKind.StackAllocArrayCreation, syntax, type, hasErrors || count.HasErrors() || initializerOpt.HasErrors()) + : base(BoundKind.StackAllocArrayCreation, syntax, elementType, count, initializerOpt, type, hasErrors || count.HasErrors() || initializerOpt.HasErrors()) { Debug.Assert(elementType is object, "Field 'elementType' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); Debug.Assert(count is object, "Field 'count' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); - this.ElementType = elementType; - this.Count = count; - this.InitializerOpt = initializerOpt; } - public TypeSymbol ElementType { get; } - - public BoundExpression Count { get; } - - public BoundArrayInitialization? InitializerOpt { get; } + public new TypeSymbol? Type => base.Type!; [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitStackAllocArrayCreation(this); @@ -6257,7 +6262,7 @@ public BoundStackAllocArrayCreation Update(TypeSymbol elementType, BoundExpressi } } - internal sealed partial class BoundConvertedStackAllocExpression : BoundStackAllocArrayCreation + internal sealed partial class BoundConvertedStackAllocExpression : BoundStackAllocArrayCreationBase { public BoundConvertedStackAllocExpression(SyntaxNode syntax, TypeSymbol elementType, BoundExpression count, BoundArrayInitialization? initializerOpt, TypeSymbol type, bool hasErrors = false) : base(BoundKind.ConvertedStackAllocExpression, syntax, elementType, count, initializerOpt, type, hasErrors || count.HasErrors() || initializerOpt.HasErrors()) @@ -6274,7 +6279,7 @@ public BoundConvertedStackAllocExpression(SyntaxNode syntax, TypeSymbol elementT [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitConvertedStackAllocExpression(this); - public new BoundConvertedStackAllocExpression Update(TypeSymbol elementType, BoundExpression count, BoundArrayInitialization? initializerOpt, TypeSymbol type) + public BoundConvertedStackAllocExpression Update(TypeSymbol elementType, BoundExpression count, BoundArrayInitialization? initializerOpt, TypeSymbol type) { if (!TypeSymbol.Equals(elementType, this.ElementType, TypeCompareKind.ConsiderEverything) || count != this.Count || initializerOpt != this.InitializerOpt || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StackAlloc.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StackAlloc.cs index 37e61e66ba4169c596b3fd3ae1ffb92beef0345b..bf50b6e18cead879489389199260352fcf2dbffe 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StackAlloc.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StackAlloc.cs @@ -12,10 +12,15 @@ internal sealed partial class LocalRewriter { public override BoundNode VisitConvertedStackAllocExpression(BoundConvertedStackAllocExpression stackAllocNode) { - return VisitStackAllocArrayCreation(stackAllocNode); + return VisitStackAllocArrayCreationBase(stackAllocNode); } public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreation stackAllocNode) + { + return VisitStackAllocArrayCreationBase(stackAllocNode); + } + + private BoundNode VisitStackAllocArrayCreationBase(BoundStackAllocArrayCreationBase stackAllocNode) { var rewrittenCount = VisitExpression(stackAllocNode.Count); var type = stackAllocNode.Type; diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs index 73c0b4416bfff882739fe41e19ffa56df5c9bd0f..bb8e79056e397298e85cba272c63df6e79144406 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs @@ -109,40 +109,6 @@ internal static bool IsValidScopeDesignator(this ExpressionSyntax expression) } } - /// - /// Is this a context in which a stackalloc expression could be converted to the corresponding pointer - /// type? The only context that permits it is the initialization of a local variable declaration (when - /// the declaration appears as a statement or as the first part of a for loop). - /// - internal static bool IsLocalVariableDeclarationInitializationForPointerStackalloc(this SyntaxNode node) - { - Debug.Assert(node != null); - - SyntaxNode equalsValueClause = node.Parent; - - if (!equalsValueClause.IsKind(SyntaxKind.EqualsValueClause)) - { - return false; - } - - SyntaxNode variableDeclarator = equalsValueClause.Parent; - - if (!variableDeclarator.IsKind(SyntaxKind.VariableDeclarator)) - { - return false; - } - - SyntaxNode variableDeclaration = variableDeclarator.Parent; - if (!variableDeclaration.IsKind(SyntaxKind.VariableDeclaration)) - { - return false; - } - - return - variableDeclaration.Parent.IsKind(SyntaxKind.LocalDeclarationStatement) || - variableDeclaration.Parent.IsKind(SyntaxKind.ForStatement); - } - /// /// Because the instruction cannot have any values on the stack before CLR execution /// we limited it to assignments and conditional expressions in C# 7. diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs index 3a6f09d864573f0f21f80837cc43d9988d4ea9a0..56b5e9b2c0781a6d930947dc49d0933c73b704eb 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs @@ -594,24 +594,24 @@ static void Method1() }"; CreateCompilationWithMscorlibAndSpan(source, TestOptions.ReleaseDll, parseOptions: TestOptions.Regular7_3) .VerifyDiagnostics( - // (6,15): error CS8652: The feature 'stackalloc in nested expressions' is not available in C# 7.3. Please use language version 8.0 or greater. + // (6,15): error CS8370: Feature 'stackalloc in nested expressions' is not available in C# 7.3. Please use language version 8.0 or greater. // lock (stackalloc int[3] { 1, 2, 3 }) {} Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "stackalloc").WithArguments("stackalloc in nested expressions", "8.0").WithLocation(6, 15), - // (6,15): error CS0185: 'stackalloc int[3]' is not a reference type as required by the lock statement + // (6,15): error CS0185: 'int*' is not a reference type as required by the lock statement // lock (stackalloc int[3] { 1, 2, 3 }) {} - Diagnostic(ErrorCode.ERR_LockNeedsReference, "stackalloc int[3] { 1, 2, 3 }").WithArguments("stackalloc int[3]").WithLocation(6, 15), - // (7,15): error CS8652: The feature 'stackalloc in nested expressions' is not available in C# 7.3. Please use language version 8.0 or greater. + Diagnostic(ErrorCode.ERR_LockNeedsReference, "stackalloc int[3] { 1, 2, 3 }").WithArguments("int*").WithLocation(6, 15), + // (7,15): error CS8370: Feature 'stackalloc in nested expressions' is not available in C# 7.3. Please use language version 8.0 or greater. // lock (stackalloc int[ ] { 1, 2, 3 }) {} Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "stackalloc").WithArguments("stackalloc in nested expressions", "8.0").WithLocation(7, 15), - // (7,15): error CS0185: 'stackalloc int[]' is not a reference type as required by the lock statement + // (7,15): error CS0185: 'int*' is not a reference type as required by the lock statement // lock (stackalloc int[ ] { 1, 2, 3 }) {} - Diagnostic(ErrorCode.ERR_LockNeedsReference, "stackalloc int[ ] { 1, 2, 3 }").WithArguments("stackalloc int[]").WithLocation(7, 15), - // (8,15): error CS8652: The feature 'stackalloc in nested expressions' is not available in C# 7.3. Please use language version 8.0 or greater. + Diagnostic(ErrorCode.ERR_LockNeedsReference, "stackalloc int[ ] { 1, 2, 3 }").WithArguments("int*").WithLocation(7, 15), + // (8,15): error CS8370: Feature 'stackalloc in nested expressions' is not available in C# 7.3. Please use language version 8.0 or greater. // lock (stackalloc [ ] { 1, 2, 3 }) {} Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "stackalloc").WithArguments("stackalloc in nested expressions", "8.0").WithLocation(8, 15), - // (8,15): error CS0185: 'stackalloc int[]' is not a reference type as required by the lock statement + // (8,15): error CS0185: 'int*' is not a reference type as required by the lock statement // lock (stackalloc [ ] { 1, 2, 3 }) {} - Diagnostic(ErrorCode.ERR_LockNeedsReference, "stackalloc [ ] { 1, 2, 3 }").WithArguments("stackalloc int[]").WithLocation(8, 15) + Diagnostic(ErrorCode.ERR_LockNeedsReference, "stackalloc [ ] { 1, 2, 3 }").WithArguments("int*").WithLocation(8, 15) ); CreateCompilationWithMscorlibAndSpan(source, TestOptions.ReleaseDll, parseOptions: TestOptions.Regular8) .VerifyDiagnostics( @@ -1056,7 +1056,7 @@ public void Method3() Assert.Equal("obj5", obj5.Identifier.Text); var obj5Value = model.GetSemanticInfoSummary(obj5.Initializer.Value); - Assert.Null(obj5Value.Type); + Assert.Equal(SpecialType.System_Int32, ((IPointerTypeSymbol)obj5Value.Type).PointedAtType.SpecialType); Assert.Equal(SpecialType.System_Double, ((IPointerTypeSymbol)obj5Value.ConvertedType).PointedAtType.SpecialType); Assert.Equal(ConversionKind.NoConversion, obj5Value.ImplicitConversion.Kind); } @@ -1159,7 +1159,7 @@ public void Method3() Assert.Equal("obj5", obj5.Identifier.Text); var obj5Value = model.GetSemanticInfoSummary(obj5.Initializer.Value); - Assert.Null(obj5Value.Type); + Assert.Equal(SpecialType.System_Int32, ((IPointerTypeSymbol)obj5Value.Type).PointedAtType.SpecialType); Assert.Equal(SpecialType.System_Double, ((IPointerTypeSymbol)obj5Value.ConvertedType).PointedAtType.SpecialType); Assert.Equal(ConversionKind.NoConversion, obj5Value.ImplicitConversion.Kind); } @@ -2542,7 +2542,7 @@ public void Method1() var stackallocInfo = model.GetSemanticInfoSummary(@stackalloc); Assert.Null(stackallocInfo.Symbol); - Assert.Null(stackallocInfo.Type); + Assert.Equal("System.Double*", stackallocInfo.Type.ToTestDisplayString()); Assert.Equal("System.Int16*", stackallocInfo.ConvertedType.ToTestDisplayString()); Assert.Equal(Conversion.NoConversion, stackallocInfo.ImplicitConversion); @@ -2570,7 +2570,7 @@ public void Method1() stackallocInfo = model.GetSemanticInfoSummary(@stackalloc); Assert.Null(stackallocInfo.Symbol); - Assert.Null(stackallocInfo.Type); + Assert.Equal("System.Double*", stackallocInfo.Type.ToTestDisplayString()); Assert.Equal("System.Span", stackallocInfo.ConvertedType.ToTestDisplayString()); Assert.Equal(Conversion.NoConversion, stackallocInfo.ImplicitConversion); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocSpanExpressionsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocSpanExpressionsTests.cs index 02914228db749a8edd0400027be528e15a4c43db..9356de3c19f83dfaab28a1a9db367d938e3cb31a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocSpanExpressionsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocSpanExpressionsTests.cs @@ -85,7 +85,7 @@ public void Method() Assert.Equal("obj5", obj5.Identifier.Text); var obj5Value = model.GetSemanticInfoSummary(obj5.Initializer.Value); - Assert.Null(obj5Value.Type); + Assert.Equal(SpecialType.System_Int32, ((IPointerTypeSymbol)obj5Value.Type).PointedAtType.SpecialType); Assert.Equal(SpecialType.System_Double, ((IPointerTypeSymbol)obj5Value.ConvertedType).PointedAtType.SpecialType); Assert.Equal(ConversionKind.NoConversion, obj5Value.ImplicitConversion.Kind); } @@ -160,7 +160,7 @@ public void Method() Assert.Equal("obj5", obj5.Identifier.Text); var obj5Value = model.GetSemanticInfoSummary(obj5.Initializer.Value); - Assert.Null(obj5Value.Type); + Assert.Equal(SpecialType.System_Int32, ((IPointerTypeSymbol)obj5Value.Type).PointedAtType.SpecialType); Assert.Equal(SpecialType.System_Double, ((IPointerTypeSymbol)obj5Value.ConvertedType).PointedAtType.SpecialType); Assert.Equal(ConversionKind.NoConversion, obj5Value.ImplicitConversion.Kind); } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs index b4345ac4e7d525f495d222ac8a05b512f08884aa..e69bafc8b3dd26c4709aaaaeb13aa008815225b4 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs @@ -4816,10 +4816,7 @@ unsafe public class Test CreateCompilation(test, options: TestOptions.UnsafeDebugDll).VerifyDiagnostics( // (4,14): error CS0518: Predefined type 'System.Span`1' is not defined or imported // int* p = stackalloc int[1]; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "stackalloc int[1]").WithArguments("System.Span`1").WithLocation(4, 14), - // (4,14): error CS8346: Conversion of a stackalloc expression of type 'int' to type 'int*' is not possible. - // int* p = stackalloc int[1]; - Diagnostic(ErrorCode.ERR_StackAllocConversionNotPossible, "stackalloc int[1]").WithArguments("int", "int*").WithLocation(4, 14) + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "stackalloc int[1]").WithArguments("System.Span`1").WithLocation(4, 14) ); } @@ -4839,10 +4836,7 @@ void M() CreateCompilation(test, options: TestOptions.UnsafeDebugDll).VerifyDiagnostics( // (6,33): error CS0518: Predefined type 'System.Span`1' is not defined or imported // int*[] p = new int*[] { stackalloc int[1] }; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "stackalloc int[1]").WithArguments("System.Span`1").WithLocation(6, 33), - // (6,33): error CS8346: Conversion of a stackalloc expression of type 'int' to type 'int*' is not possible. - // int*[] p = new int*[] { stackalloc int[1] }; - Diagnostic(ErrorCode.ERR_StackAllocConversionNotPossible, "stackalloc int[1]").WithArguments("int", "int*").WithLocation(6, 33) + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "stackalloc int[1]").WithArguments("System.Span`1").WithLocation(6, 33) ); } @@ -4905,10 +4899,7 @@ unsafe public static void Main() CreateCompilation(test, options: TestOptions.ReleaseDll.WithAllowUnsafe(true)).VerifyDiagnostics( // (6,39): error CS0518: Predefined type 'System.Span`1' is not defined or imported // using (System.IDisposable v = stackalloc int[1]) - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "stackalloc int[1]").WithArguments("System.Span`1").WithLocation(6, 39), - // (6,39): error CS8346: Conversion of a stackalloc expression of type 'int' to type 'IDisposable' is not possible. - // using (System.IDisposable v = stackalloc int[1]) - Diagnostic(ErrorCode.ERR_StackAllocConversionNotPossible, "stackalloc int[1]").WithArguments("int", "System.IDisposable").WithLocation(6, 39) + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "stackalloc int[1]").WithArguments("System.Span`1").WithLocation(6, 39) ); }