提交 8f2b6657 编写于 作者: V Vladimir Sadov 提交者: GitHub

Merge pull request #22162 from VSadov/stackallocType

Making default type of stackalloc expression to be Span<T> when not directly in an initializer
......@@ -1887,6 +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;
......@@ -2054,6 +2055,7 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint
}
return true;
case BoundKind.StackAllocArrayCreation:
case BoundKind.ConvertedStackAllocExpression:
if (escapeTo < Binder.TopLevelScope)
{
......
......@@ -339,7 +339,7 @@ private BoundExpression CreateStackAllocConversion(SyntaxNode syntax, BoundExpre
throw ExceptionUtilities.UnexpectedValue(conversion.Kind);
}
var convertedNode = new BoundConvertedStackAllocExpression(syntax, elementType, boundStackAlloc.Count, conversion.Kind, stackAllocType, boundStackAlloc.HasErrors);
var convertedNode = new BoundConvertedStackAllocExpression(syntax, elementType, boundStackAlloc.Count, stackAllocType, boundStackAlloc.HasErrors);
var underlyingConversion = conversion.UnderlyingConversions.Single();
return CreateConversion(syntax, convertedNode, underlyingConversion, isCast, destination, diagnostics);
......
......@@ -3016,8 +3016,9 @@ private void BindArrayInitializerExpressions(InitializerExpressionSyntax initial
StackAllocArrayCreationExpressionSyntax node, DiagnosticBag diagnostics)
{
bool hasErrors = false;
var inLegalPosition = (IsInMethodBody || IsLocalFunctionsScopeBinder) && node.IsLegalSpanStackAllocPosition();
if (!IsInMethodBody && !IsLocalFunctionsScopeBinder)
if (!inLegalPosition)
{
hasErrors = true;
diagnostics.Add(
......@@ -3103,7 +3104,20 @@ private void BindArrayInitializerExpressions(InitializerExpressionSyntax initial
}
}
return new BoundStackAllocArrayCreation(node, elementType, count, type: null, hasErrors: hasErrors || typeHasErrors);
TypeSymbol type = null;
if (inLegalPosition && !node.IsVariableDeclarationInitialization())
{
CheckFeatureAvailability(node, MessageID.IDS_FeatureRefStructs, diagnostics);
GetWellKnownTypeMember(Compilation, WellKnownMember.System_Span_T__ctor, diagnostics, syntax: node);
var spanType = GetWellKnownType(WellKnownType.System_Span_T, diagnostics, node);
if (!spanType.IsErrorType())
{
type = spanType.Construct(elementType);
}
}
return new BoundStackAllocArrayCreation(node, elementType, count, type, hasErrors: hasErrors || typeHasErrors);
}
private static int? GetIntegerConstantForArraySize(BoundExpression expression)
......
......@@ -287,46 +287,36 @@ private static Conversion ToConversion(OverloadResolutionResult<MethodSymbol> re
public override Conversion GetStackAllocConversion(BoundStackAllocArrayCreation sourceExpression, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
Debug.Assert((object)sourceExpression.Type == null);
Debug.Assert(sourceExpression.ElementType != null);
var pointerConversion = default(Conversion);
if (sourceExpression.Syntax.IsVariableDeclarationInitialization())
{
Debug.Assert((object)sourceExpression.Type == null);
Debug.Assert(sourceExpression.ElementType != null);
var pointerConversion = default(Conversion);
var sourceAsPointer = new PointerTypeSymbol(sourceExpression.ElementType);
pointerConversion = ClassifyImplicitConversionFromType(sourceAsPointer, destination, ref useSiteDiagnostics);
}
if (pointerConversion.IsValid)
{
// Report unsafe errors
_binder.ReportUnsafeIfNotAllowed(sourceExpression.Syntax.Location, ref useSiteDiagnostics);
return Conversion.MakeStackAllocToPointerType(pointerConversion);
}
else
{
var spanType = _binder.GetWellKnownType(WellKnownType.System_Span_T, ref useSiteDiagnostics).Construct(sourceExpression.ElementType);
var spanConversion = ClassifyImplicitConversionFromType(spanType, destination, ref useSiteDiagnostics);
if (pointerConversion.IsValid)
{
// Report unsafe errors
_binder.ReportUnsafeIfNotAllowed(sourceExpression.Syntax.Location, ref useSiteDiagnostics);
if (spanConversion.Exists)
return Conversion.MakeStackAllocToPointerType(pointerConversion);
}
else
{
// Report errors if Span ctor is missing, or using an older C# version
Binder.CheckFeatureAvailability(sourceExpression.Syntax, MessageID.IDS_FeatureRefStructs, ref useSiteDiagnostics);
Binder.GetWellKnownTypeMember(_binder.Compilation, WellKnownMember.System_Span_T__ctor, out DiagnosticInfo memberDiagnosticInfo);
HashSetExtensions.InitializeAndAdd(ref useSiteDiagnostics, memberDiagnosticInfo);
var spanType = _binder.GetWellKnownType(WellKnownType.System_Span_T, ref useSiteDiagnostics).Construct(sourceExpression.ElementType);
var spanConversion = ClassifyImplicitConversionFromType(spanType, destination, ref useSiteDiagnostics);
if (!sourceExpression.Syntax.IsLegalSpanStackAllocPosition())
if (spanConversion.Exists)
{
// Because the instruction cannot have any values on the stack before CLR execution.
// Limit it to assignments and conditional expressions for now.
// https://github.com/dotnet/roslyn/issues/22046
// Report errors if Span ctor is missing, or using an older C# version
Binder.CheckFeatureAvailability(sourceExpression.Syntax, MessageID.IDS_FeatureRefStructs, ref useSiteDiagnostics);
Binder.GetWellKnownTypeMember(_binder.Compilation, WellKnownMember.System_Span_T__ctor, out DiagnosticInfo memberDiagnosticInfo);
HashSetExtensions.InitializeAndAdd(ref useSiteDiagnostics, memberDiagnosticInfo);
HashSetExtensions.InitializeAndAdd(ref useSiteDiagnostics, new CSDiagnosticInfo(ErrorCode.ERR_InvalidExprTerm, SyntaxFacts.GetText(SyntaxKind.StackAllocKeyword)));
return Conversion.MakeStackAllocToSpanType(spanConversion);
}
return Conversion.MakeStackAllocToSpanType(spanConversion);
}
}
......
......@@ -1435,13 +1435,9 @@
<Field Name="Count" Type="BoundExpression"/>
</Node>
<Node Name="BoundConvertedStackAllocExpression" Base="BoundExpression">
<Node Name="BoundConvertedStackAllocExpression" Base="BoundStackAllocArrayCreation">
<!-- 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">
......
......@@ -5226,8 +5226,19 @@ public BoundArrayInitialization Update(ImmutableArray<BoundExpression> initializ
}
}
internal sealed partial class BoundStackAllocArrayCreation : BoundExpression
internal partial class BoundStackAllocArrayCreation : BoundExpression
{
protected BoundStackAllocArrayCreation(BoundKind kind, SyntaxNode syntax, TypeSymbol elementType, BoundExpression count, TypeSymbol type, bool hasErrors = false)
: base(kind, syntax, type, 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.ElementType = elementType;
this.Count = count;
}
public BoundStackAllocArrayCreation(SyntaxNode syntax, TypeSymbol elementType, BoundExpression count, TypeSymbol type, bool hasErrors = false)
: base(BoundKind.StackAllocArrayCreation, syntax, type, hasErrors || count.HasErrors())
{
......@@ -5261,38 +5272,29 @@ public BoundStackAllocArrayCreation Update(TypeSymbol elementType, BoundExpressi
}
}
internal sealed partial class BoundConvertedStackAllocExpression : BoundExpression
internal sealed partial class BoundConvertedStackAllocExpression : BoundStackAllocArrayCreation
{
public BoundConvertedStackAllocExpression(SyntaxNode syntax, TypeSymbol elementType, BoundExpression count, ConversionKind conversionKind, TypeSymbol type, bool hasErrors = false)
: base(BoundKind.ConvertedStackAllocExpression, syntax, type, hasErrors || count.HasErrors())
public BoundConvertedStackAllocExpression(SyntaxNode syntax, TypeSymbol elementType, BoundExpression count, TypeSymbol type, bool hasErrors = false)
: base(BoundKind.ConvertedStackAllocExpression, syntax, elementType, count, 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)
public new BoundConvertedStackAllocExpression Update(TypeSymbol elementType, BoundExpression count, TypeSymbol type)
{
if (elementType != this.ElementType || count != this.Count || conversionKind != this.ConversionKind || type != this.Type)
if (elementType != this.ElementType || count != this.Count || type != this.Type)
{
var result = new BoundConvertedStackAllocExpression(this.Syntax, elementType, count, conversionKind, type, this.HasErrors);
var result = new BoundConvertedStackAllocExpression(this.Syntax, elementType, count, type, this.HasErrors);
result.WasCompilerGenerated = this.WasCompilerGenerated;
return result;
}
......@@ -9175,7 +9177,7 @@ public override BoundNode VisitConvertedStackAllocExpression(BoundConvertedStack
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);
return node.Update(elementType, count, type);
}
public override BoundNode VisitFieldAccess(BoundFieldAccess node)
{
......@@ -10637,7 +10639,6 @@ public override TreeDumperNode VisitConvertedStackAllocExpression(BoundConverted
{
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)
}
);
......
......@@ -10,48 +10,48 @@ namespace Microsoft.CodeAnalysis.CSharp
internal sealed partial class LocalRewriter
{
public override BoundNode VisitConvertedStackAllocExpression(BoundConvertedStackAllocExpression stackAllocNode)
{
return VisitStackAllocArrayCreation(stackAllocNode);
}
public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreation stackAllocNode)
{
var rewrittenCount = VisitExpression(stackAllocNode.Count);
var type = stackAllocNode.Type;
if (rewrittenCount.ConstantValue?.Int32Value == 0)
{
// either default(span) or nullptr
return _factory.Default(stackAllocNode.Type);
return _factory.Default(type);
}
var conversionKind = stackAllocNode.ConversionKind;
var elementType = stackAllocNode.ElementType;
switch (conversionKind)
if (type.IsPointerType())
{
case ConversionKind.StackAllocToPointerType:
{
var stackSize = RewriteStackAllocCountToSize(rewrittenCount, elementType);
return stackAllocNode.Update(elementType, stackSize, conversionKind, stackAllocNode.Type);
}
case ConversionKind.StackAllocToSpanType:
{
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 spanCtor = (MethodSymbol)_compilation.GetWellKnownTypeMember(WellKnownMember.System_Span_T__ctor).SymbolAsMember(spanType);
var ctorCall = _factory.New(spanCtor, stackAllocNode, countTemp);
return new BoundSequence(
syntax: stackAllocNode.Syntax,
locals: ImmutableArray.Create(countTemp.LocalSymbol),
sideEffects: ImmutableArray.Create<BoundExpression>(countTempAssignment),
value: ctorCall,
type: spanType);
}
default:
{
throw ExceptionUtilities.UnexpectedValue(conversionKind);
}
var stackSize = RewriteStackAllocCountToSize(rewrittenCount, elementType);
return new BoundConvertedStackAllocExpression(stackAllocNode.Syntax, elementType, stackSize, stackAllocNode.Type);
}
else if (type.IsSpanType())
{
var spanType = (NamedTypeSymbol)stackAllocNode.Type;
var countTemp = _factory.StoreToTemp(rewrittenCount, out BoundAssignmentOperator countTempAssignment);
var stackSize = RewriteStackAllocCountToSize(countTemp, elementType);
stackAllocNode = new BoundConvertedStackAllocExpression(stackAllocNode.Syntax, elementType, stackSize, spanType);
var spanCtor = (MethodSymbol)_compilation.GetWellKnownTypeMember(WellKnownMember.System_Span_T__ctor).SymbolAsMember(spanType);
var ctorCall = _factory.New(spanCtor, stackAllocNode, countTemp);
return new BoundSequence(
syntax: stackAllocNode.Syntax,
locals: ImmutableArray.Create(countTemp.LocalSymbol),
sideEffects: ImmutableArray.Create<BoundExpression>(countTempAssignment),
value: ctorCall,
type: spanType);
}
else
{
throw ExceptionUtilities.UnexpectedValue(type);
}
}
......
......@@ -1053,7 +1053,7 @@ unsafe public static void Main()
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];
var span2 = condition ? stackalloc int[1] : stackalloc int[4];
Console.Write(span2.Length);
}
}", TestOptions.UnsafeReleaseExe);
......
......@@ -227,7 +227,7 @@ void M()
}
[Fact]
public void ConditionalExpressionOnSpan_NonConvertible()
public void ConditionalExpressionOnSpan_BothStackallocSpans()
{
CreateCompilationWithMscorlibAndSpan(@"
class Test
......@@ -236,10 +236,7 @@ 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 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 int[10]", "stackalloc int[5]").WithLocation(6, 17));
}", TestOptions.UnsafeReleaseDll).VerifyDiagnostics();
}
[Fact]
......@@ -317,9 +314,13 @@ 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 int[10]' and 'stackalloc int[10]'
// (6,12): error CS1525: Invalid expression term 'stackalloc'
// if(stackalloc int[10] == stackalloc int[10]) { }
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "stackalloc").WithArguments("stackalloc").WithLocation(6, 12),
// (6,34): error CS1525: Invalid expression term 'stackalloc'
// if(stackalloc int[10] == stackalloc int[10]) { }
Diagnostic(ErrorCode.ERR_BadBinaryOps, "stackalloc int[10] == stackalloc int[10]").WithArguments("==", "stackalloc int[10]", "stackalloc int[10]").WithLocation(6, 12));
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "stackalloc").WithArguments("stackalloc").WithLocation(6, 34)
);
}
[Fact]
......@@ -435,9 +436,10 @@ void M()
}
";
CreateCompilationWithMscorlibAndSpan(test, options: TestOptions.ReleaseDll.WithAllowUnsafe(true)).VerifyDiagnostics(
// (7,31): error CS1510: A ref or out value must be an assignable variable
// (7,31): error CS1525: Invalid expression term 'stackalloc'
// ref Span<int> p = ref stackalloc int[1];
Diagnostic(ErrorCode.ERR_RefLvalueExpected, "stackalloc int[1]").WithLocation(7, 31));
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "stackalloc").WithArguments("stackalloc").WithLocation(7, 31)
);
}
[Fact]
......@@ -457,9 +459,10 @@ void N(Span<int> span)
}
";
CreateCompilationWithMscorlibAndSpan(test, options: TestOptions.ReleaseDll.WithAllowUnsafe(true)).VerifyDiagnostics(
// (7,9): error CS1525: Invalid expression term 'stackalloc'
// (7,11): error CS1525: Invalid expression term 'stackalloc'
// N(stackalloc int[1]);
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "N").WithArguments("stackalloc").WithLocation(7, 9));
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "stackalloc").WithArguments("stackalloc").WithLocation(7, 11)
);
}
[Fact]
......@@ -475,9 +478,10 @@ void M()
}
";
CreateCompilationWithMscorlibAndSpan(test, TestOptions.ReleaseDll).VerifyDiagnostics(
// (6,22): error CS0023: Operator '.' cannot be applied to operand of type 'stackalloc int[10]'
// (6,23): error CS1525: Invalid expression term 'stackalloc'
// int length = (stackalloc int [10]).Length;
Diagnostic(ErrorCode.ERR_BadUnaryOp, "(stackalloc int [10]).Length").WithArguments(".", "stackalloc int[10]").WithLocation(6, 22));
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "stackalloc").WithArguments("stackalloc").WithLocation(6, 23)
);
}
[Fact]
......@@ -499,9 +503,10 @@ static void Main()
}
";
CreateCompilationWithMscorlibAndSpan(test, TestOptions.UnsafeReleaseExe).VerifyDiagnostics(
// (7,16): error CS1503: Argument 1: cannot convert from 'stackalloc int[10]' to 'Span<short>'
// (7,16): error CS1525: Invalid expression term 'stackalloc'
// Invoke(stackalloc int [10]);
Diagnostic(ErrorCode.ERR_BadArgType, "stackalloc int [10]").WithArguments("1", "stackalloc int[10]", "System.Span<short>").WithLocation(7, 16));
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "stackalloc").WithArguments("stackalloc").WithLocation(7, 16)
);
}
}
}
......@@ -7560,7 +7560,8 @@ void M(int* p = stackalloc int[1])
CreateStandardCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(
// (4,21): error CS1525: Invalid expression term 'stackalloc'
// void M(int* p = stackalloc int[1])
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "stackalloc").WithArguments("stackalloc").WithLocation(4, 21));
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "stackalloc").WithArguments("stackalloc").WithLocation(4, 21)
);
}
[Fact]
......
......@@ -2876,7 +2876,8 @@ static void Main(string[] args)
CreateStandardCompilation(text, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true)).VerifyDiagnostics(
// (4,30): error CS1525: Invalid expression term 'stackalloc'
// int* property { get; } = stackalloc int[256];
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "stackalloc").WithArguments("stackalloc").WithLocation(4, 30));
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "stackalloc").WithArguments("stackalloc").WithLocation(4, 30)
);
}
[Fact]
public void RefPropertyWithoutGetter()
......
......@@ -4616,6 +4616,9 @@ unsafe public static int Main()
// (7,34): error CS1002: ; expected
// int *pp = stackalloc int 30;
Diagnostic(ErrorCode.ERR_SemicolonExpected, "30").WithLocation(7, 34),
// (6,18): error CS1525: Invalid expression term 'stackalloc'
// int *p = stackalloc int (30);
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "stackalloc").WithArguments("stackalloc").WithLocation(6, 18),
// (6,29): error CS1575: A stackalloc expression requires [] after type
// int *p = stackalloc int (30);
Diagnostic(ErrorCode.ERR_BadStackAllocExpr, "int").WithLocation(6, 29),
......@@ -4624,7 +4627,8 @@ unsafe public static int Main()
Diagnostic(ErrorCode.ERR_BadStackAllocExpr, "int").WithLocation(7, 30),
// (7,34): error CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement
// int *pp = stackalloc int 30;
Diagnostic(ErrorCode.ERR_IllegalStatement, "30").WithLocation(7, 34));
Diagnostic(ErrorCode.ERR_IllegalStatement, "30").WithLocation(7, 34)
);
}
[Fact]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册