提交 199c2e6e 编写于 作者: O Omar Tawfik 提交者: GitHub

Change stackalloc conversion to a converted bound node (#21746)

* fixed breaking changes markdown file

* Ban illegal assignment expressions

* Replacing conversions with a bound converted node

* Clean up

* PR comments

* Code comments
上级 0b32f65c
......@@ -23,5 +23,3 @@ Consider the case where the type of `a` is `System.Func<bool>` and you write `va
- https://github.com/dotnet/roslyn/issues/17963 In C# 7.0 and before C# 7.1, the compiler used to consider tuple element name differences and dynamic-ness differences as significant when using the "as" operator with a nullable tuple type. For instance, `(1, 1) as (int, int x)?` and `(1, new object()) as (int, dynamic)?` would always be considered `null`. The compiler now produces the correct value, instead of `null`, and no "always null" warning.
- https://github.com/dotnet/roslyn/issues/20208 In C# 7.0 and before C# 7.2, the compiler considered a local declared with a `var` type and a tuple literal value to be used. So it would not report a warning if that local was not used. The compiler now produces a diagnostic. For example, `var unused = (1, 2);`.
- https://github.com/dotnet/roslyn/pull/21006 In this PR, stackalloc expressions are target typed, instead of always being of type T* pointer. This affects the semantic model, as getting type info about such nodes will return null types. Users can still access the implicit conversion to know the new target type.
......@@ -1887,7 +1887,7 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin
case BoundKind.Local:
return ((BoundLocal)expr).LocalSymbol.ValEscapeScope;
case BoundKind.StackAllocArrayCreation:
case BoundKind.ConvertedStackAllocExpression:
return Binder.TopLevelScope;
case BoundKind.ConditionalOperator:
......@@ -2054,7 +2054,7 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint
}
return true;
case BoundKind.StackAllocArrayCreation:
case BoundKind.ConvertedStackAllocExpression:
if (escapeTo < Binder.TopLevelScope)
{
Error(diagnostics, ErrorCode.ERR_EscapeStackAlloc, node, expr.Type);
......
// 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.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
......@@ -93,6 +95,11 @@ internal partial class Binder
return CreateAnonymousFunctionConversion(syntax, source, conversion, isCast, destination, diagnostics);
}
if (conversion.IsStackAlloc)
{
return CreateStackAllocConversion(syntax, source, conversion, isCast, destination, diagnostics);
}
if (conversion.IsTupleLiteralConversion ||
(conversion.IsNullable && conversion.UnderlyingConversions[0].IsTupleLiteralConversion))
{
......@@ -312,6 +319,32 @@ private BoundExpression CreateMethodGroupConversion(SyntaxNode syntax, BoundExpr
return new BoundConversion(syntax, group, conversion, @checked: false, explicitCastInCode: isCast, constantValueOpt: ConstantValue.NotAvailable, type: destination, hasErrors: hasErrors) { WasCompilerGenerated = source.WasCompilerGenerated };
}
private BoundExpression CreateStackAllocConversion(SyntaxNode syntax, BoundExpression source, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics)
{
Debug.Assert(conversion.IsStackAlloc);
var boundStackAlloc = (BoundStackAllocArrayCreation)source;
var elementType = boundStackAlloc.ElementType;
TypeSymbol stackAllocType;
switch (conversion.Kind)
{
case ConversionKind.StackAllocToPointerType:
stackAllocType = new PointerTypeSymbol(elementType);
break;
case ConversionKind.StackAllocToSpanType:
stackAllocType = Compilation.GetWellKnownType(WellKnownType.System_Span_T).Construct(elementType);
break;
default:
throw ExceptionUtilities.UnexpectedValue(conversion.Kind);
}
var convertedNode = new BoundConvertedStackAllocExpression(syntax, elementType, boundStackAlloc.Count, conversion.Kind, stackAllocType, boundStackAlloc.HasErrors);
var underlyingConversion = conversion.UnderlyingConversions.Single();
return CreateConversion(syntax, convertedNode, underlyingConversion, isCast, destination, diagnostics);
}
private BoundExpression CreateTupleLiteralConversion(SyntaxNode syntax, BoundTupleLiteral sourceTuple, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics)
{
// We have a successful tuple conversion; rather than producing a separate conversion node
......
......@@ -3102,7 +3102,7 @@ private void BindArrayInitializerExpressions(InitializerExpressionSyntax initial
}
}
return new BoundStackAllocArrayCreation(node, default(ConversionKind), elementType, count, null, hasErrors || typeHasErrors);
return new BoundStackAllocArrayCreation(node, elementType, count, type: null, hasErrors: hasErrors || typeHasErrors);
}
private static int? GetIntegerConstantForArraySize(BoundExpression expression)
......
......@@ -2193,21 +2193,10 @@ internal bool IsNonMoveableVariable(BoundExpression expr, out Symbol accessedLoc
}
case BoundKind.PointerIndirectionOperator: //Covers ->, since the receiver will be one of these.
case BoundKind.PointerElementAccess:
case BoundKind.StackAllocArrayCreation:
case BoundKind.ConvertedStackAllocExpression:
{
return true;
}
case BoundKind.Conversion:
{
switch (expr.GetConversion().Kind)
{
case ConversionKind.StackAllocToPointerType:
case ConversionKind.StackAllocToSpanType:
return true;
default:
return false;
}
}
case BoundKind.PropertyAccess: // Never a variable.
case BoundKind.IndexerAccess: // Never a variable.
default:
......
......@@ -709,12 +709,11 @@ private TypeSymbol BindVariableType(CSharpSyntaxNode declarationNode, Diagnostic
BindValueKind valueKind;
ExpressionSyntax value;
IsInitializerRefKindValid(initializer, initializer, refKind, diagnostics, out valueKind, out value); // The return value isn't important here; we just want the diagnostics and the BindValueKind
return BindInferredVariableInitializer(diagnostics, value, valueKind, errorSyntax);
return BindInferredVariableInitializer(diagnostics, value, valueKind, refKind, errorSyntax);
}
// The location where the error is reported might not be the initializer.
protected BoundExpression BindInferredVariableInitializer(DiagnosticBag diagnostics, ExpressionSyntax initializer, BindValueKind valueKind,
CSharpSyntaxNode errorSyntax)
protected BoundExpression BindInferredVariableInitializer(DiagnosticBag diagnostics, ExpressionSyntax initializer, BindValueKind valueKind, RefKind refKind, CSharpSyntaxNode errorSyntax)
{
if (initializer == null)
{
......@@ -736,6 +735,12 @@ private TypeSymbol BindVariableType(CSharpSyntaxNode declarationNode, Diagnostic
BoundExpression expression = BindValue(initializer, diagnostics, valueKind);
if (expression is BoundStackAllocArrayCreation boundStackAlloc)
{
var type = new PointerTypeSymbol(boundStackAlloc.ElementType);
expression = GenerateConversionForAssignment(type, boundStackAlloc, diagnostics, refKind: refKind);
}
// 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())
......@@ -860,7 +865,7 @@ private TypeSymbol BindVariableType(CSharpSyntaxNode declarationNode, Diagnostic
{
aliasOpt = null;
initializerOpt = BindInferredVariableInitializer(diagnostics, value, valueKind, declarator);
initializerOpt = BindInferredVariableInitializer(diagnostics, value, valueKind, localSymbol.RefKind, declarator);
// If we got a good result then swap the inferred type for the "var"
if ((object)initializerOpt?.Type != null)
......@@ -883,11 +888,6 @@ private TypeSymbol BindVariableType(CSharpSyntaxNode declarationNode, Diagnostic
}
}
}
else if (initializerOpt is BoundStackAllocArrayCreation boundStackAlloc)
{
declTypeOpt = new PointerTypeSymbol(boundStackAlloc.ElementType);
initializerOpt = GenerateConversionForAssignment(declTypeOpt, boundStackAlloc, diagnostics, false, localSymbol.RefKind);
}
else
{
declTypeOpt = CreateErrorType("var");
......@@ -906,7 +906,7 @@ private TypeSymbol BindVariableType(CSharpSyntaxNode declarationNode, Diagnostic
initializerOpt = BindPossibleArrayInitializer(value, declTypeOpt, valueKind, diagnostics);
if (kind != LocalDeclarationKind.FixedVariable)
{
// If this is for a fixed statement, we'll do our own conversion since there are some special cases.
// If this is for a fixed statement, we'll do our own conversion since there are some special cases.
initializerOpt = GenerateConversionForAssignment(declTypeOpt, initializerOpt, localDiagnostics, refKind: localSymbol.RefKind);
}
}
......@@ -1055,14 +1055,13 @@ private bool IsValidFixedVariableInitializer(TypeSymbol declType, SourceLocalSym
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 is BoundConversion conversion && (
conversion.Operand.IsLiteralNull() ||
conversion.Operand.Kind == BoundKind.DefaultExpression ||
conversion.Operand.Kind == BoundKind.StackAllocArrayCreation),
// Dev10 just reports the assignment conversion error, which must occur, except for these cases:
Debug.Assert(
initializerOpt is BoundConvertedStackAllocExpression ||
initializerOpt is BoundConversion conversion && (conversion.Operand.IsLiteralNull() || conversion.Operand.Kind == BoundKind.DefaultExpression),
"All other typeless expressions should have conversion errors");
// CONSIDER: this is a very confusing error message, but it's what Dev10 reports.
......
......@@ -457,6 +457,18 @@ public bool IsIdentity
}
}
/// <summary>
/// Returns true if the conversion is a stackalloc conversion.
/// PROTOTYPE(span) review public API change
/// </summary>
public bool IsStackAlloc
{
get
{
return Kind == ConversionKind.StackAllocToPointerType || Kind == ConversionKind.StackAllocToSpanType;
}
}
/// <summary>
/// Returns true if the conversion is an implicit numeric conversion or explicit numeric conversion.
/// </summary>
......
......@@ -44,12 +44,6 @@ public static bool IsDefaultValue(this BoundExpression node)
public static bool HasExpressionType(this BoundExpression node)
{
if (node.Kind == BoundKind.StackAllocArrayCreation)
{
// stackalloc expressions have no 'Type', but an 'ElementType' that is target-typed based on context.
return true;
}
// null literal, method group, and anonymous function expressions have no type.
return (object)node.Type != null;
}
......
......@@ -1431,11 +1431,19 @@
</Node>
<Node Name="BoundStackAllocArrayCreation" Base="BoundExpression">
<Field Name="ConversionKind" Type="ConversionKind" Null="allow"/>
<Field Name="ElementType" Type="TypeSymbol" Null="disallow"/>
<Field Name="Count" Type="BoundExpression"/>
</Node>
<Node Name="BoundConvertedStackAllocExpression" Base="BoundExpression">
<!-- Non-null type is required for this node kind -->
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
<Field Name="ElementType" Type="TypeSymbol" Null="disallow"/>
<Field Name="Count" Type="BoundExpression"/>
<Field Name="ConversionKind" Type="ConversionKind" Null="allow"/>
</Node>
<Node Name="BoundFieldAccess" Base="BoundExpression">
<!-- Non-null type is required for this node kind -->
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
......
......@@ -1964,6 +1964,23 @@ public override void Accept(OperationVisitor visitor)
}
}
internal partial class BoundConvertedStackAllocExpression
{
protected override OperationKind ExpressionKind => OperationKind.None;
protected override ImmutableArray<IOperation> Children => ImmutableArray.Create<IOperation>(this.Count);
public override void Accept(OperationVisitor visitor)
{
visitor.VisitNoneOperation(this);
}
public override TResult Accept<TArgument, TResult>(OperationVisitor<TArgument, TResult> visitor, TArgument argument)
{
return visitor.VisitNoneOperation(this, argument);
}
}
internal partial class BoundDynamicObjectCreationExpression
{
protected override OperationKind ExpressionKind => OperationKind.None;
......
......@@ -149,7 +149,7 @@ internal partial class BoundStackAllocArrayCreation
{
public override object Display
{
get { return string.Format(MessageID.IDS_StackAllocExpression.Localize().ToString(), ElementType); }
get { return string.Format(MessageID.IDS_StackAllocExpression.Localize().ToString(), ElementType, Count.Syntax); }
}
}
}
......@@ -10955,7 +10955,7 @@ internal static string ERR_RefReturnParameter
}
/// <summary>
/// Looks up a localized string similar to stackalloc expression of type &apos;{0}&apos;.
/// Looks up a localized string similar to stackalloc {0}[{1}].
/// </summary>
internal static string IDS_StackAllocExpression {
get {
......
......@@ -5196,6 +5196,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<value>Conversion of a stackalloc expression of type '{0}' to type '{1}' is not possible.</value>
</data>
<data name="IDS_StackAllocExpression" xml:space="preserve">
<value>stackalloc expression of type '{0}'</value>
<value>stackalloc {0}[{1}]</value>
</data>
</root>
\ No newline at end of file
......@@ -83,10 +83,6 @@ private void EmitConversion(BoundConversion conversion)
case ConversionKind.PointerToVoid:
case ConversionKind.PointerToPointer:
return; //no-op since they all have the same runtime representation
case ConversionKind.StackAllocToPointerType:
case ConversionKind.StackAllocToSpanType:
// no-op since operand (which contains the lowered expression) is already emitted
return;
case ConversionKind.PointerToInteger:
case ConversionKind.IntegerToPointer:
var fromType = conversion.Operand.Type;
......
......@@ -105,8 +105,8 @@ private void EmitExpressionCore(BoundExpression expression, bool used)
EmitArrayCreationExpression((BoundArrayCreation)expression, used);
break;
case BoundKind.StackAllocArrayCreation:
EmitStackAllocArrayCreationExpression((BoundStackAllocArrayCreation)expression, used);
case BoundKind.ConvertedStackAllocExpression:
EmitConvertedStackAllocExpression((BoundConvertedStackAllocExpression)expression, used);
break;
case BoundKind.Conversion:
......@@ -1826,7 +1826,7 @@ private void EmitArrayCreationExpression(BoundArrayCreation expression, bool use
EmitPopIfUnused(used);
}
private void EmitStackAllocArrayCreationExpression(BoundStackAllocArrayCreation expression, bool used)
private void EmitConvertedStackAllocExpression(BoundConvertedStackAllocExpression expression, bool used)
{
EmitExpression(expression.Count, used: true);
_builder.EmitOpCode(ILOpCode.Localloc);
......
......@@ -1571,11 +1571,11 @@ public override BoundNode VisitCatchBlock(BoundCatchBlock node)
return node.Update(node.Locals, exceptionSourceOpt, exceptionTypeOpt, boundFilter, boundBlock, node.IsSynthesizedAsyncCatchAll);
}
public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreation node)
public override BoundNode VisitConvertedStackAllocExpression(BoundConvertedStackAllocExpression node)
{
// CLI spec section 3.47 requires that the stack be empty when localloc occurs.
EnsureOnlyEvalStack();
return base.VisitStackAllocArrayCreation(node);
return base.VisitConvertedStackAllocExpression(node);
}
public override BoundNode VisitArrayInitialization(BoundArrayInitialization node)
......
......@@ -1923,17 +1923,6 @@ private static bool IsUserDefinedTrueOrFalse(BoundUnaryOperator @operator)
type = ((BoundConvertedTupleLiteral)boundExpr).NaturalTypeOpt;
break;
}
case BoundKind.StackAllocArrayCreation:
{
Debug.Assert(boundExpr.Type is null);
Debug.Assert(highestBoundExpr.Kind == BoundKind.Conversion);
var boundConversion = (BoundConversion)highestBoundExpr;
type = null;
convertedType = boundConversion.Type;
conversion = boundConversion.Conversion;
break;
}
}
}
......
......@@ -224,17 +224,6 @@ public override BoundNode Visit(BoundNode node)
/// <param name="currentBoundNode">The bound node.</param>
private bool ShouldAddNode(BoundNode currentBoundNode)
{
// stackalloc conversion nodes should always be added, as information about their types exist on the conversion, not the operand node.
if (currentBoundNode.Kind == BoundKind.Conversion)
{
switch (((BoundConversion)currentBoundNode).Conversion.Kind)
{
case ConversionKind.StackAllocToPointerType:
case ConversionKind.StackAllocToSpanType:
return true;
}
}
// Do not add compiler generated nodes.
if (currentBoundNode.WasCompilerGenerated)
{
......
......@@ -2572,6 +2572,12 @@ public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreat
return null;
}
public override BoundNode VisitConvertedStackAllocExpression(BoundConvertedStackAllocExpression node)
{
VisitRvalue(node.Count);
return null;
}
public override BoundNode VisitAnonymousObjectCreationExpression(BoundAnonymousObjectCreationExpression node)
{
// visit arguments as r-values
......
......@@ -147,6 +147,7 @@ internal enum BoundKind: byte
ArrayCreation,
ArrayInitialization,
StackAllocArrayCreation,
ConvertedStackAllocExpression,
FieldAccess,
HoistedFieldAccess,
PropertyAccess,
......@@ -5227,21 +5228,18 @@ public BoundArrayInitialization Update(ImmutableArray<BoundExpression> initializ
internal sealed partial class BoundStackAllocArrayCreation : BoundExpression
{
public BoundStackAllocArrayCreation(SyntaxNode syntax, ConversionKind conversionKind, TypeSymbol elementType, BoundExpression count, TypeSymbol type, bool hasErrors = false)
public BoundStackAllocArrayCreation(SyntaxNode syntax, TypeSymbol elementType, BoundExpression count, TypeSymbol type, bool hasErrors = false)
: base(BoundKind.StackAllocArrayCreation, syntax, type, hasErrors || count.HasErrors())
{
Debug.Assert(elementType != null, "Field 'elementType' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(count != null, "Field 'count' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
this.ConversionKind = conversionKind;
this.ElementType = elementType;
this.Count = count;
}
public ConversionKind ConversionKind { get; }
public TypeSymbol ElementType { get; }
public BoundExpression Count { get; }
......@@ -5251,11 +5249,50 @@ public override BoundNode Accept(BoundTreeVisitor visitor)
return visitor.VisitStackAllocArrayCreation(this);
}
public BoundStackAllocArrayCreation Update(ConversionKind conversionKind, TypeSymbol elementType, BoundExpression count, TypeSymbol type)
public BoundStackAllocArrayCreation Update(TypeSymbol elementType, BoundExpression count, TypeSymbol type)
{
if (elementType != this.ElementType || count != this.Count || type != this.Type)
{
var result = new BoundStackAllocArrayCreation(this.Syntax, elementType, count, type, this.HasErrors);
result.WasCompilerGenerated = this.WasCompilerGenerated;
return result;
}
return this;
}
}
internal sealed partial class BoundConvertedStackAllocExpression : BoundExpression
{
public BoundConvertedStackAllocExpression(SyntaxNode syntax, TypeSymbol elementType, BoundExpression count, ConversionKind conversionKind, TypeSymbol type, bool hasErrors = false)
: base(BoundKind.ConvertedStackAllocExpression, syntax, type, hasErrors || count.HasErrors())
{
Debug.Assert(elementType != null, "Field 'elementType' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(count != null, "Field 'count' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
Debug.Assert(type != null, "Field 'type' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
this.ElementType = elementType;
this.Count = count;
this.ConversionKind = conversionKind;
}
public TypeSymbol ElementType { get; }
public BoundExpression Count { get; }
public ConversionKind ConversionKind { get; }
public override BoundNode Accept(BoundTreeVisitor visitor)
{
return visitor.VisitConvertedStackAllocExpression(this);
}
public BoundConvertedStackAllocExpression Update(TypeSymbol elementType, BoundExpression count, ConversionKind conversionKind, TypeSymbol type)
{
if (conversionKind != this.ConversionKind || elementType != this.ElementType || count != this.Count || type != this.Type)
if (elementType != this.ElementType || count != this.Count || conversionKind != this.ConversionKind || type != this.Type)
{
var result = new BoundStackAllocArrayCreation(this.Syntax, conversionKind, elementType, count, type, this.HasErrors);
var result = new BoundConvertedStackAllocExpression(this.Syntax, elementType, count, conversionKind, type, this.HasErrors);
result.WasCompilerGenerated = this.WasCompilerGenerated;
return result;
}
......@@ -6350,6 +6387,8 @@ internal R VisitInternal(BoundNode node, A arg)
return VisitArrayInitialization(node as BoundArrayInitialization, arg);
case BoundKind.StackAllocArrayCreation:
return VisitStackAllocArrayCreation(node as BoundStackAllocArrayCreation, arg);
case BoundKind.ConvertedStackAllocExpression:
return VisitConvertedStackAllocExpression(node as BoundConvertedStackAllocExpression, arg);
case BoundKind.FieldAccess:
return VisitFieldAccess(node as BoundFieldAccess, arg);
case BoundKind.HoistedFieldAccess:
......@@ -6910,6 +6949,10 @@ public virtual R VisitStackAllocArrayCreation(BoundStackAllocArrayCreation node,
{
return this.DefaultVisit(node, arg);
}
public virtual R VisitConvertedStackAllocExpression(BoundConvertedStackAllocExpression node, A arg)
{
return this.DefaultVisit(node, arg);
}
public virtual R VisitFieldAccess(BoundFieldAccess node, A arg)
{
return this.DefaultVisit(node, arg);
......@@ -7510,6 +7553,10 @@ public virtual BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreati
{
return this.DefaultVisit(node);
}
public virtual BoundNode VisitConvertedStackAllocExpression(BoundConvertedStackAllocExpression node)
{
return this.DefaultVisit(node);
}
public virtual BoundNode VisitFieldAccess(BoundFieldAccess node)
{
return this.DefaultVisit(node);
......@@ -8259,6 +8306,11 @@ public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreat
this.Visit(node.Count);
return null;
}
public override BoundNode VisitConvertedStackAllocExpression(BoundConvertedStackAllocExpression node)
{
this.Visit(node.Count);
return null;
}
public override BoundNode VisitFieldAccess(BoundFieldAccess node)
{
this.Visit(node.ReceiverOpt);
......@@ -9116,7 +9168,14 @@ public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreat
BoundExpression count = (BoundExpression)this.Visit(node.Count);
TypeSymbol elementType = this.VisitType(node.ElementType);
TypeSymbol type = this.VisitType(node.Type);
return node.Update(node.ConversionKind, elementType, count, type);
return node.Update(elementType, count, type);
}
public override BoundNode VisitConvertedStackAllocExpression(BoundConvertedStackAllocExpression node)
{
BoundExpression count = (BoundExpression)this.Visit(node.Count);
TypeSymbol elementType = this.VisitType(node.ElementType);
TypeSymbol type = this.VisitType(node.Type);
return node.Update(elementType, count, node.ConversionKind, type);
}
public override BoundNode VisitFieldAccess(BoundFieldAccess node)
{
......@@ -10566,13 +10625,23 @@ public override TreeDumperNode VisitStackAllocArrayCreation(BoundStackAllocArray
{
return new TreeDumperNode("stackAllocArrayCreation", null, new TreeDumperNode[]
{
new TreeDumperNode("conversionKind", node.ConversionKind, null),
new TreeDumperNode("elementType", node.ElementType, null),
new TreeDumperNode("count", null, new TreeDumperNode[] { Visit(node.Count, null) }),
new TreeDumperNode("type", node.Type, null)
}
);
}
public override TreeDumperNode VisitConvertedStackAllocExpression(BoundConvertedStackAllocExpression node, object arg)
{
return new TreeDumperNode("convertedStackAllocExpression", null, new TreeDumperNode[]
{
new TreeDumperNode("elementType", node.ElementType, null),
new TreeDumperNode("count", null, new TreeDumperNode[] { Visit(node.Count, null) }),
new TreeDumperNode("conversionKind", node.ConversionKind, null),
new TreeDumperNode("type", node.Type, null)
}
);
}
public override TreeDumperNode VisitFieldAccess(BoundFieldAccess node, object arg)
{
return new TreeDumperNode("fieldAccess", null, new TreeDumperNode[]
......
......@@ -181,11 +181,9 @@ private BoundExpression VisitExpressionImpl(BoundExpression node)
// statement means that this constraint is not violated).
// Dynamic type will be erased in emit phase. It is considered equivalent to Object in lowered bound trees.
// Unused deconstructions are lowered to produce a return value that isn't a tuple type.
// stackalloc bound nodes have no type, and they are updated to the appropriate type after lowering
Debug.Assert(visited == null || visited.HasErrors || ReferenceEquals(visited.Type, node.Type) ||
visited.Type.Equals(node.Type, TypeCompareKind.IgnoreDynamicAndTupleNames) ||
IsUnusedDeconstruction(node) ||
node.Kind == BoundKind.StackAllocArrayCreation);
IsUnusedDeconstruction(node));
return visited;
}
......
......@@ -21,17 +21,10 @@ public override BoundNode VisitConversion(BoundConversion node)
}
var rewrittenType = VisitType(node.Type);
var rewrittenOperand = node.Operand;
if (rewrittenOperand is BoundStackAllocArrayCreation boundStackAlloc)
{
// Update operand node with the successful conversion kind
rewrittenOperand = boundStackAlloc.Update(node.ConversionKind, boundStackAlloc.ElementType, boundStackAlloc.Count, boundStackAlloc.Type);
}
bool wasInExpressionLambda = _inExpressionLambda;
_inExpressionLambda = _inExpressionLambda || (node.ConversionKind == ConversionKind.AnonymousFunction && !wasInExpressionLambda && rewrittenType.IsExpressionTree());
rewrittenOperand = VisitExpression(rewrittenOperand);
var rewrittenOperand = VisitExpression(node.Operand);
_inExpressionLambda = wasInExpressionLambda;
var result = MakeConversionNode(node, node.Syntax, rewrittenOperand, node.Conversion, node.Checked, node.ExplicitCastInCode, node.ConstantValue, rewrittenType);
......@@ -204,13 +197,6 @@ private static bool IsFloatPointExpressionOfUnknownPrecision(BoundExpression rew
}
break;
case ConversionKind.StackAllocToPointerType:
case ConversionKind.StackAllocToSpanType:
{
var underlyingConversion = conversion.UnderlyingConversions.Single();
return MakeConversionNode(oldNode, syntax, rewrittenOperand, underlyingConversion, @checked, explicitCastInCode, constantValueOpt, rewrittenType);
}
case ConversionKind.DefaultOrNullLiteral:
if (!_inExpressionLambda || !explicitCastInCode)
{
......
......@@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp
{
internal sealed partial class LocalRewriter
{
public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreation stackAllocNode)
public override BoundNode VisitConvertedStackAllocExpression(BoundConvertedStackAllocExpression stackAllocNode)
{
var rewrittenCount = VisitExpression(stackAllocNode.Count);
......@@ -21,17 +21,17 @@ public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreat
case ConversionKind.StackAllocToPointerType:
{
var stackSize = RewriteStackAllocCountToSize(rewrittenCount, elementType);
var resultType = new PointerTypeSymbol(elementType);
return stackAllocNode.Update(conversionKind, elementType, stackSize, resultType);
return stackAllocNode.Update(elementType, stackSize, conversionKind, stackAllocNode.Type);
}
case ConversionKind.StackAllocToSpanType:
{
BoundLocal countTemp = _factory.StoreToTemp(rewrittenCount, out BoundAssignmentOperator countTempAssignment);
BoundExpression stackSize = RewriteStackAllocCountToSize(countTemp, elementType);
stackAllocNode = stackAllocNode.Update(conversionKind, elementType, stackSize, stackAllocNode.Type);
Debug.Assert(stackAllocNode.Type.IsSpanType());
var spanType = (NamedTypeSymbol)stackAllocNode.Type;
var countTemp = _factory.StoreToTemp(rewrittenCount, out BoundAssignmentOperator countTempAssignment);
var stackSize = RewriteStackAllocCountToSize(countTemp, elementType);
stackAllocNode = stackAllocNode.Update(elementType, stackSize, conversionKind, spanType);
var spanType = _compilation.GetWellKnownType(WellKnownType.System_Span_T).Construct(elementType);
var spanCtor = (MethodSymbol)_compilation.GetWellKnownTypeMember(WellKnownMember.System_Span_T__ctor).SymbolAsMember(spanType);
var ctorCall = _factory.New(spanCtor, stackAllocNode, countTemp);
......
......@@ -389,7 +389,7 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
NeedsProxy(leftLocal.LocalSymbol))
{
Debug.Assert(!proxies.ContainsKey(leftLocal.LocalSymbol));
Debug.Assert(!IsStackAlloc(originalRight));
Debug.Assert(originalRight.Kind != BoundKind.ConvertedStackAllocExpression);
//spilling ref local variables
throw ExceptionUtilities.Unreachable;
}
......@@ -411,7 +411,7 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
// If the receiver of the field is on the stack when the stackalloc happens,
// popping it will free the memory (?) or otherwise cause verification issues.
// DevDiv Bugs 59454
if (rewrittenLeft.Kind != BoundKind.Local && IsStackAlloc(originalRight))
if (rewrittenLeft.Kind != BoundKind.Local && originalRight.Kind == BoundKind.ConvertedStackAllocExpression)
{
// From ILGENREC::genAssign:
// DevDiv Bugs 59454: Handle hoisted local initialized with a stackalloc
......@@ -436,13 +436,6 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
return node.Update(rewrittenLeft, rewrittenRight, node.RefKind, rewrittenType);
}
private static bool IsStackAlloc(BoundExpression expr)
{
return
expr.Kind == BoundKind.StackAllocArrayCreation ||
expr.Kind == BoundKind.Conversion && ((BoundConversion)expr).Operand.Kind == BoundKind.StackAllocArrayCreation;
}
public override BoundNode VisitFieldInfo(BoundFieldInfo node)
{
var rewrittenField = ((FieldSymbol)node.Field.OriginalDefinition)
......
......@@ -3,3 +3,4 @@ Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax.ReadOnlyKeyword.get -> Micros
Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken refKeyword, Microsoft.CodeAnalysis.SyntaxToken readOnlyKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type) -> Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax.WithReadOnlyKeyword(Microsoft.CodeAnalysis.SyntaxToken readOnlyKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.RefType(Microsoft.CodeAnalysis.SyntaxToken refKeyword, Microsoft.CodeAnalysis.SyntaxToken readOnlyKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type) -> Microsoft.CodeAnalysis.CSharp.Syntax.RefTypeSyntax
Microsoft.CodeAnalysis.CSharp.Conversion.IsStackAlloc.get -> bool
\ No newline at end of file
......@@ -150,15 +150,18 @@ internal static bool IsLegalSpanStackAllocPosition(this SyntaxNode node)
switch (parentNode.Kind())
{
// In case of a declaration of a Span<T> variable
case SyntaxKind.EqualsValueClause:
{
// In case of a declaration of a Span<T> variable
return parentNode.Parent.IsKind(SyntaxKind.VariableDeclarator);
SyntaxNode variableDeclarator = parentNode.Parent;
return variableDeclarator.IsKind(SyntaxKind.VariableDeclarator) &&
variableDeclarator.Parent.IsKind(SyntaxKind.VariableDeclaration);
}
// In case of reassignment to a Span<T> variable
case SyntaxKind.SimpleAssignmentExpression:
{
// In case of reassignment to a Span<T> variable
return true;
return parentNode.Parent.IsKind(SyntaxKind.ExpressionStatement);
}
}
......
......@@ -15668,5 +15668,173 @@ .maxstack 1
}");
}
[Fact]
public void CorrectOverloadOfStackAllocSpanChosen()
{
var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
unsafe public static void Main()
{
bool condition = false;
var span1 = condition ? stackalloc int[1] : new Span<int>(null, 2);
Console.Write(span1.Length);
var span2 = condition ? new Span<int>(null, 3) : stackalloc int[4];
Console.Write(span2.Length);
}
}", TestOptions.UnsafeReleaseExe);
CompileAndVerify(comp, expectedOutput: "24");
}
[Fact]
public void StackAllocExpressionIL()
{
var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
public static void Main()
{
Span<int> x = stackalloc int[10];
Console.WriteLine(x.Length);
}
}", TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: "10", verify: false).VerifyIL("Test.Main", @"
{
// Code size 29 (0x1d)
.maxstack 2
.locals init (System.Span<int> V_0, //x
int V_1)
IL_0000: ldc.i4.s 10
IL_0002: stloc.1
IL_0003: ldloc.1
IL_0004: conv.u
IL_0005: ldc.i4.4
IL_0006: mul.ovf.un
IL_0007: localloc
IL_0009: ldloc.1
IL_000a: newobj ""System.Span<int>..ctor(void*, int)""
IL_000f: stloc.0
IL_0010: ldloca.s V_0
IL_0012: call ""int System.Span<int>.Length.get""
IL_0017: call ""void System.Console.WriteLine(int)""
IL_001c: ret
}");
}
[Fact]
public void StackAllocSpanLengthNotEvaluatedTwice()
{
var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
private static int length = 0;
private static int GetLength()
{
return ++length;
}
public static void Main()
{
for (int i = 0; i < 5; i++)
{
Span<int> x = stackalloc int[GetLength()];
Console.Write(x.Length);
}
}
}", TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: "12345", verify: false).VerifyIL("Test.Main", @"
{
// Code size 44 (0x2c)
.maxstack 2
.locals init (int V_0, //i
System.Span<int> V_1, //x
int V_2)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: br.s IL_0027
IL_0004: call ""int Test.GetLength()""
IL_0009: stloc.2
IL_000a: ldloc.2
IL_000b: conv.u
IL_000c: ldc.i4.4
IL_000d: mul.ovf.un
IL_000e: localloc
IL_0010: ldloc.2
IL_0011: newobj ""System.Span<int>..ctor(void*, int)""
IL_0016: stloc.1
IL_0017: ldloca.s V_1
IL_0019: call ""int System.Span<int>.Length.get""
IL_001e: call ""void System.Console.Write(int)""
IL_0023: ldloc.0
IL_0024: ldc.i4.1
IL_0025: add
IL_0026: stloc.0
IL_0027: ldloc.0
IL_0028: ldc.i4.5
IL_0029: blt.s IL_0004
IL_002b: ret
}");
}
[Fact]
public void ImplicitCastOperatorOnStackAllocIsLoweredCorrectly()
{
var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
unsafe class Test
{
public static void Main()
{
Test obj1 = stackalloc int[10];
Console.Write(""|"");
Test obj2 = stackalloc double[10];
}
public static implicit operator Test(Span<int> value)
{
Console.Write(""SpanOpCalled"");
return default(Test);
}
public static implicit operator Test(double* value)
{
Console.Write(""PointerOpCalled"");
return default(Test);
}
}", TestOptions.UnsafeReleaseExe);
CompileAndVerify(comp, expectedOutput: "SpanOpCalled|PointerOpCalled", verify: false);
}
[Fact]
public void ExplicitCastOperatorOnStackAllocIsLoweredCorrectly()
{
var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
unsafe class Test
{
public static void Main()
{
Test obj1 = (Test)stackalloc int[10];
}
public static explicit operator Test(Span<int> value)
{
Console.Write(""SpanOpCalled"");
return default(Test);
}
}", TestOptions.UnsafeReleaseExe);
CompileAndVerify(comp, expectedOutput: "SpanOpCalled", verify: false);
}
}
}
......@@ -29,6 +29,7 @@ public void Method()
var obj2 = stackalloc int[10];
Span<int> obj3 = stackalloc int[10];
int* obj4 = stackalloc int[10];
double* obj5 = stackalloc int[10];
}
public static implicit operator Test(int* value)
......@@ -37,51 +38,56 @@ public void Method()
}
}", TestOptions.UnsafeReleaseDll);
comp.VerifyDiagnostics();
comp.VerifyDiagnostics(
// (11,24): error CS8520: Conversion of a stackalloc expression of type 'int' to type 'double*' is not possible.
// double* obj5 = stackalloc int[10];
Diagnostic(ErrorCode.ERR_StackAllocConversionNotPossible, "stackalloc int[10]").WithArguments("int", "double*").WithLocation(11, 24));
var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var variables = tree.GetCompilationUnitRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>();
Assert.Equal(4, variables.Count());
Assert.Equal(5, variables.Count());
var obj1 = variables.ElementAt(0);
Assert.Equal("obj1", obj1.Identifier.Text);
var obj1Value = model.GetSemanticInfoSummary(obj1.Initializer.Value);
Assert.Null(obj1Value.Type);
Assert.Equal(SpecialType.System_Int32, ((PointerTypeSymbol)obj1Value.Type).PointedAtType.SpecialType);
Assert.Equal("Test", obj1Value.ConvertedType.Name);
Assert.Equal(ConversionKind.StackAllocToPointerType, obj1Value.ImplicitConversion.Kind);
Assert.Equal(ConversionKind.ImplicitUserDefined, obj1Value.ImplicitConversion.UnderlyingConversions.Single().Kind);
Assert.Equal(ConversionKind.ImplicitUserDefined, obj1Value.ImplicitConversion.Kind);
var obj2 = variables.ElementAt(1);
Assert.Equal("obj2", obj2.Identifier.Text);
var obj2Value = model.GetSemanticInfoSummary(obj2.Initializer.Value);
Assert.Null(obj2Value.Type);
Assert.True(obj2Value.ConvertedType is PointerTypeSymbol);
Assert.Equal("Int32", ((PointerTypeSymbol)obj2Value.ConvertedType).PointedAtType.Name);
Assert.Equal(ConversionKind.StackAllocToPointerType, obj2Value.ImplicitConversion.Kind);
Assert.Equal(ConversionKind.Identity, obj2Value.ImplicitConversion.UnderlyingConversions.Single().Kind);
Assert.Equal(SpecialType.System_Int32, ((PointerTypeSymbol)obj2Value.Type).PointedAtType.SpecialType);
Assert.Equal(SpecialType.System_Int32, ((PointerTypeSymbol)obj2Value.ConvertedType).PointedAtType.SpecialType);
Assert.Equal(ConversionKind.Identity, obj2Value.ImplicitConversion.Kind);
var obj3 = variables.ElementAt(2);
Assert.Equal("obj3", obj3.Identifier.Text);
var obj3Value = model.GetSemanticInfoSummary(obj3.Initializer.Value);
Assert.Null(obj3Value.Type);
Assert.Equal("Span", obj3Value.Type.Name);
Assert.Equal("Span", obj3Value.ConvertedType.Name);
Assert.Equal(ConversionKind.StackAllocToSpanType, obj3Value.ImplicitConversion.Kind);
Assert.Equal(ConversionKind.Identity, obj3Value.ImplicitConversion.UnderlyingConversions.Single().Kind);
Assert.Equal(ConversionKind.Identity, obj3Value.ImplicitConversion.Kind);
var obj4 = variables.ElementAt(3);
Assert.Equal("obj4", obj4.Identifier.Text);
var obj4Value = model.GetSemanticInfoSummary(obj4.Initializer.Value);
Assert.Null(obj4Value.Type);
Assert.True(obj4Value.ConvertedType is PointerTypeSymbol);
Assert.Equal("Int32", ((PointerTypeSymbol)obj4Value.ConvertedType).PointedAtType.Name);
Assert.Equal(ConversionKind.StackAllocToPointerType, obj4Value.ImplicitConversion.Kind);
Assert.Equal(ConversionKind.Identity, obj4Value.ImplicitConversion.UnderlyingConversions.Single().Kind);
Assert.Equal(SpecialType.System_Int32, ((PointerTypeSymbol)obj4Value.Type).PointedAtType.SpecialType);
Assert.Equal(SpecialType.System_Int32, ((PointerTypeSymbol)obj4Value.ConvertedType).PointedAtType.SpecialType);
Assert.Equal(ConversionKind.Identity, obj4Value.ImplicitConversion.Kind);
var obj5 = variables.ElementAt(4);
Assert.Equal("obj5", obj5.Identifier.Text);
var obj5Value = model.GetSemanticInfoSummary(obj5.Initializer.Value);
Assert.Null(obj5Value.Type);
Assert.Equal(SpecialType.System_Double, ((PointerTypeSymbol)obj5Value.ConvertedType).PointedAtType.SpecialType);
Assert.Equal(ConversionKind.NoConversion, obj5Value.ImplicitConversion.Kind);
}
[Fact]
......@@ -97,6 +103,7 @@ public void Method()
var obj2 = stackalloc int[10];
Span<int> obj3 = stackalloc int[10];
int* obj4 = stackalloc int[10];
double* obj5 = stackalloc int[10];
}
public static explicit operator Test(Span<int> value)
......@@ -105,52 +112,57 @@ public void Method()
}
}", TestOptions.UnsafeReleaseDll);
comp.VerifyDiagnostics();
comp.VerifyDiagnostics(
// (11,24): error CS8520: Conversion of a stackalloc expression of type 'int' to type 'double*' is not possible.
// double* obj5 = stackalloc int[10];
Diagnostic(ErrorCode.ERR_StackAllocConversionNotPossible, "stackalloc int[10]").WithArguments("int", "double*").WithLocation(11, 24));
var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var variables = tree.GetCompilationUnitRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>();
Assert.Equal(4, variables.Count());
Assert.Equal(5, variables.Count());
var obj1 = variables.ElementAt(0);
Assert.Equal("obj1", obj1.Identifier.Text);
Assert.Equal(SyntaxKind.CastExpression, obj1.Initializer.Value.Kind());
var obj1Value = model.GetSemanticInfoSummary(((CastExpressionSyntax)obj1.Initializer.Value).Expression);
Assert.Null(obj1Value.Type);
Assert.Equal("Span", obj1Value.Type.Name);
Assert.Equal("Span", obj1Value.ConvertedType.Name);
Assert.Equal(ConversionKind.StackAllocToSpanType, obj1Value.ImplicitConversion.Kind);
Assert.Equal(ConversionKind.Identity, obj1Value.ImplicitConversion.UnderlyingConversions.Single().Kind);
Assert.Equal(ConversionKind.Identity, obj1Value.ImplicitConversion.Kind);
var obj2 = variables.ElementAt(1);
Assert.Equal("obj2", obj2.Identifier.Text);
var obj2Value = model.GetSemanticInfoSummary(obj2.Initializer.Value);
Assert.Null(obj2Value.Type);
Assert.True(obj2Value.ConvertedType is PointerTypeSymbol);
Assert.Equal("Int32", ((PointerTypeSymbol)obj2Value.ConvertedType).PointedAtType.Name);
Assert.Equal(ConversionKind.StackAllocToPointerType, obj2Value.ImplicitConversion.Kind);
Assert.Equal(ConversionKind.Identity, obj2Value.ImplicitConversion.UnderlyingConversions.Single().Kind);
Assert.Equal(SpecialType.System_Int32, ((PointerTypeSymbol)obj2Value.Type).PointedAtType.SpecialType);
Assert.Equal(SpecialType.System_Int32, ((PointerTypeSymbol)obj2Value.ConvertedType).PointedAtType.SpecialType);
Assert.Equal(ConversionKind.Identity, obj2Value.ImplicitConversion.Kind);
var obj3 = variables.ElementAt(2);
Assert.Equal("obj3", obj3.Identifier.Text);
var obj3Value = model.GetSemanticInfoSummary(obj3.Initializer.Value);
Assert.Null(obj3Value.Type);
Assert.Equal("Span", obj3Value.Type.Name);
Assert.Equal("Span", obj3Value.ConvertedType.Name);
Assert.Equal(ConversionKind.StackAllocToSpanType, obj3Value.ImplicitConversion.Kind);
Assert.Equal(ConversionKind.Identity, obj3Value.ImplicitConversion.UnderlyingConversions.Single().Kind);
Assert.Equal(ConversionKind.Identity, obj3Value.ImplicitConversion.Kind);
var obj4 = variables.ElementAt(3);
Assert.Equal("obj4", obj4.Identifier.Text);
var obj4Value = model.GetSemanticInfoSummary(obj4.Initializer.Value);
Assert.Null(obj4Value.Type);
Assert.True(obj4Value.ConvertedType is PointerTypeSymbol);
Assert.Equal("Int32", ((PointerTypeSymbol)obj4Value.ConvertedType).PointedAtType.Name);
Assert.Equal(ConversionKind.StackAllocToPointerType, obj4Value.ImplicitConversion.Kind);
Assert.Equal(ConversionKind.Identity, obj4Value.ImplicitConversion.UnderlyingConversions.Single().Kind);
Assert.Equal(SpecialType.System_Int32, ((PointerTypeSymbol)obj4Value.Type).PointedAtType.SpecialType);
Assert.Equal(SpecialType.System_Int32, ((PointerTypeSymbol)obj4Value.ConvertedType).PointedAtType.SpecialType);
Assert.Equal(ConversionKind.Identity, obj4Value.ImplicitConversion.Kind);
var obj5 = variables.ElementAt(4);
Assert.Equal("obj5", obj5.Identifier.Text);
var obj5Value = model.GetSemanticInfoSummary(obj5.Initializer.Value);
Assert.Null(obj5Value.Type);
Assert.Equal(SpecialType.System_Double, ((PointerTypeSymbol)obj5Value.ConvertedType).PointedAtType.SpecialType);
Assert.Equal(ConversionKind.NoConversion, obj5Value.ImplicitConversion.Kind);
}
[Fact]
......@@ -225,9 +237,9 @@ void M()
var x = true ? stackalloc int [10] : stackalloc int [5];
}
}", TestOptions.UnsafeReleaseDll).VerifyDiagnostics(
// (6,17): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'stackalloc expression of type 'int'' and 'stackalloc expression of type 'int''
// (6,17): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'stackalloc int[10]' and 'stackalloc int[5]'
// var x = true ? stackalloc int [10] : stackalloc int [5];
Diagnostic(ErrorCode.ERR_InvalidQM, "true ? stackalloc int [10] : stackalloc int [5]").WithArguments("stackalloc expression of type 'int'", "stackalloc expression of type 'int'").WithLocation(6, 17));
Diagnostic(ErrorCode.ERR_InvalidQM, "true ? stackalloc int [10] : stackalloc int [5]").WithArguments("stackalloc int[10]", "stackalloc int[5]").WithLocation(6, 17));
}
[Fact]
......@@ -289,9 +301,9 @@ void M()
var x = true ? stackalloc int [10] : a;
}
}", TestOptions.UnsafeReleaseDll).VerifyDiagnostics(
// (8,17): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'stackalloc expression of type 'int'' and 'Span<short>'
// (8,17): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'stackalloc int[10]' and 'Span<short>'
// var x = true ? stackalloc int [10] : a;
Diagnostic(ErrorCode.ERR_InvalidQM, "true ? stackalloc int [10] : a").WithArguments("stackalloc expression of type 'int'", "System.Span<short>").WithLocation(8, 17));
Diagnostic(ErrorCode.ERR_InvalidQM, "true ? stackalloc int [10] : a").WithArguments("stackalloc int[10]", "System.Span<short>").WithLocation(8, 17));
}
[Fact]
......@@ -305,9 +317,9 @@ void M()
if(stackalloc int[10] == stackalloc int[10]) { }
}
}", TestOptions.UnsafeReleaseDll).VerifyDiagnostics(
// (6,12): error CS0019: Operator '==' cannot be applied to operands of type 'stackalloc expression of type 'int'' and 'stackalloc expression of type 'int''
// (6,12): error CS0019: Operator '==' cannot be applied to operands of type 'stackalloc int[10]' and 'stackalloc int[10]'
// if(stackalloc int[10] == stackalloc int[10]) { }
Diagnostic(ErrorCode.ERR_BadBinaryOps, "stackalloc int[10] == stackalloc int[10]").WithArguments("==", "stackalloc expression of type 'int'", "stackalloc expression of type 'int'").WithLocation(6, 12));
Diagnostic(ErrorCode.ERR_BadBinaryOps, "stackalloc int[10] == stackalloc int[10]").WithArguments("==", "stackalloc int[10]", "stackalloc int[10]").WithLocation(6, 12));
}
[Fact]
......@@ -463,9 +475,9 @@ void M()
}
";
CreateCompilationWithMscorlibAndSpan(test, TestOptions.ReleaseDll).VerifyDiagnostics(
// (6,22): error CS0023: Operator '.' cannot be applied to operand of type 'stackalloc expression of type 'int''
// (6,22): error CS0023: Operator '.' cannot be applied to operand of type 'stackalloc int[10]'
// int length = (stackalloc int [10]).Length;
Diagnostic(ErrorCode.ERR_BadUnaryOp, "(stackalloc int [10]).Length").WithArguments(".", "stackalloc expression of type 'int'").WithLocation(6, 22));
Diagnostic(ErrorCode.ERR_BadUnaryOp, "(stackalloc int [10]).Length").WithArguments(".", "stackalloc int[10]").WithLocation(6, 22));
}
[Fact]
......@@ -487,9 +499,9 @@ static void Main()
}
";
CreateCompilationWithMscorlibAndSpan(test, TestOptions.UnsafeReleaseExe).VerifyDiagnostics(
// (7,16): error CS1503: Argument 1: cannot convert from 'stackalloc expression of type 'int'' to 'Span<short>'
// (7,16): error CS1503: Argument 1: cannot convert from 'stackalloc int[10]' to 'Span<short>'
// Invoke(stackalloc int [10]);
Diagnostic(ErrorCode.ERR_BadArgType, "stackalloc int [10]").WithArguments("1", "stackalloc expression of type 'int'", "System.Span<short>").WithLocation(7, 16));
Diagnostic(ErrorCode.ERR_BadArgType, "stackalloc int [10]").WithArguments("1", "stackalloc int[10]", "System.Span<short>").WithLocation(7, 16));
}
}
}
......@@ -2061,8 +2061,7 @@ unsafe void Test()
";
var expected = @"
No, TypeExpression 'int*' is not a non-moveable variable
Yes, Conversion 'stackalloc int[1]' is a non-moveable variable
Yes, StackAllocArrayCreation 'stackalloc int[1]' is a non-moveable variable
Yes, ConvertedStackAllocExpression 'stackalloc int[1]' is a non-moveable variable
No, Literal '1' is not a non-moveable variable
".Trim();
......@@ -7620,10 +7619,9 @@ unsafe static void Main()
var countSyntax = arrayTypeSyntax.RankSpecifiers.Single().Sizes.Single();
var stackAllocSummary = model.GetSemanticInfoSummary(stackAllocSyntax);
Assert.Null(stackAllocSummary.Type);
Assert.Equal(SpecialType.System_Char, ((PointerTypeSymbol)stackAllocSummary.Type).PointedAtType.SpecialType);
Assert.Equal(SpecialType.System_Void, ((PointerTypeSymbol)stackAllocSummary.ConvertedType).PointedAtType.SpecialType);
Assert.Equal(ConversionKind.StackAllocToPointerType, stackAllocSummary.ImplicitConversion.Kind);
Assert.Equal(ConversionKind.PointerToVoid, stackAllocSummary.ImplicitConversion.UnderlyingConversions.Single().Kind);
Assert.Equal(Conversion.PointerToVoid, stackAllocSummary.ImplicitConversion);
Assert.Null(stackAllocSummary.Symbol);
Assert.Equal(0, stackAllocSummary.CandidateSymbols.Length);
Assert.Equal(CandidateReason.None, stackAllocSummary.CandidateReason);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册