Skip to content

Commit

Permalink
merge features/nested-stackalloc (#35385)
Browse files Browse the repository at this point in the history
* Permit stackalloc in nested contexts. (#28969)
Fixes #26759
  • Loading branch information
Neal Gafter authored May 1, 2019
1 parent 36e5d9a commit 7517f5d
Show file tree
Hide file tree
Showing 34 changed files with 530 additions and 260 deletions.
22 changes: 12 additions & 10 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3413,14 +3413,17 @@ private BoundExpression BindStackAllocArrayCreationExpression(
private bool ReportBadStackAllocPosition(SyntaxNode node, DiagnosticBag diagnostics)
{
Debug.Assert(node is StackAllocArrayCreationExpressionSyntax || node is ImplicitStackAllocArrayCreationExpressionSyntax);
bool inLegalPosition = true;

var inLegalPosition = (IsInMethodBody || IsLocalFunctionsScopeBinder) && node.IsLegalSpanStackAllocPosition();
if (!inLegalPosition)
// If we are using a language version that does not restrict the position of a stackalloc expression, skip that test.
LanguageVersion requiredVersion = MessageID.IDS_FeatureNestedStackalloc.RequiredVersion();
if (requiredVersion > Compilation.LanguageVersion)
{
diagnostics.Add(
ErrorCode.ERR_InvalidExprTerm,
node.GetFirstToken().GetLocation(),
SyntaxFacts.GetText(SyntaxKind.StackAllocKeyword));
inLegalPosition = (IsInMethodBody || IsLocalFunctionsScopeBinder) && node.IsLegalCSharp73SpanStackAllocPosition();
if (!inLegalPosition)
{
MessageID.IDS_FeatureNestedStackalloc.CheckFeatureAvailability(diagnostics, node.GetFirstToken().GetLocation());
}
}

// Check if we're syntactically within a catch or finally clause.
Expand All @@ -3436,7 +3439,7 @@ private TypeSymbol GetStackAllocType(SyntaxNode node, TypeWithAnnotations elemen
{
var inLegalPosition = ReportBadStackAllocPosition(node, diagnostics);
hasErrors = !inLegalPosition;
if (inLegalPosition && !node.IsVariableDeclarationInitialization())
if (inLegalPosition && !node.IsLocalVariableDeclarationInitializationForPointerStackalloc())
{
CheckFeatureAvailability(node, MessageID.IDS_FeatureRefStructs, diagnostics);

Expand Down Expand Up @@ -5809,9 +5812,8 @@ private BoundExpression BindMemberAccessWithBoundLeft(
}
default:
{
// Can't dot into the null literal or stackalloc expressions.
if ((boundLeft.Kind == BoundKind.Literal && ((BoundLiteral)boundLeft).ConstantValueOpt == ConstantValue.Null) ||
boundLeft.Kind == BoundKind.StackAllocArrayCreation)
// Can't dot into the null literal
if (boundLeft.Kind == BoundKind.Literal && ((BoundLiteral)boundLeft).ConstantValueOpt == ConstantValue.Null)
{
if (!boundLeft.HasAnyErrors)
{
Expand Down
4 changes: 3 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,9 @@ protected BoundExpression BindInferredVariableInitializer(DiagnosticBag diagnost

BoundExpression expression = BindValue(initializer, diagnostics, valueKind);

if (expression is BoundStackAllocArrayCreation boundStackAlloc)
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ private static Conversion ToConversion(OverloadResolutionResult<MethodSymbol> re

public override Conversion GetStackAllocConversion(BoundStackAllocArrayCreation sourceExpression, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
if (sourceExpression.Syntax.IsVariableDeclarationInitialization())
if (sourceExpression.Syntax.IsLocalVariableDeclarationInitializationForPointerStackalloc())
{
Debug.Assert((object)sourceExpression.Type == null);
Debug.Assert((object)sourceExpression.ElementType != null);
Expand Down
30 changes: 30 additions & 0 deletions src/Compilers/CSharp/Portable/BoundTree/BoundSpillSequence.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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.Immutable;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.CSharp
{
internal partial class BoundSpillSequence
{
public BoundSpillSequence(
SyntaxNode syntax,
ImmutableArray<LocalSymbol> locals,
ImmutableArray<BoundExpression> sideEffects,
BoundExpression value,
TypeSymbol type,
bool hasErrors = false)
: this(syntax, locals, MakeStatements(sideEffects), value, type, hasErrors)
{
}

private static ImmutableArray<BoundStatement> MakeStatements(ImmutableArray<BoundExpression> expressions)
{
return expressions.SelectAsArray<BoundExpression, BoundStatement>(
expression => new BoundExpressionStatement(expression.Syntax, expression, expression.HasErrors));
}
}
}
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/BoundTree/Formatting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ public override object Display
internal partial class BoundStackAllocArrayCreation
{
public override object Display
=> FormattableStringFactory.Create("stackalloc {0}[{1}]", ElementType, Count.WasCompilerGenerated ? null : Count.Syntax.ToString());
=> (Type is null) ? FormattableStringFactory.Create("stackalloc {0}[{1}]", ElementType, Count.WasCompilerGenerated ? null : Count.Syntax.ToString()) : base.Display;
}

internal partial class BoundPassByCopy
Expand Down
9 changes: 9 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -5916,4 +5916,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="WRN_NullabilityMismatchInConstraintsOnPartialImplementation_Title" xml:space="preserve">
<value>Partial method declarations have inconsistent nullability in constraints for type parameter</value>
</data>
<data name="IDS_FeatureNestedStackalloc" xml:space="preserve">
<value>stackalloc in nested expressions</value>
</data>
</root>
2 changes: 2 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/MessageID.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ internal enum MessageID
IDS_DefaultInterfaceImplementation = MessageBase + 12760,
IDS_BaseTypeInBaseExpression = MessageBase + 12761,
IDS_OverrideWithConstraints = MessageBase + 12762,
IDS_FeatureNestedStackalloc = MessageBase + 12763,
}

// Message IDs may refer to strings that need to be localized.
Expand Down Expand Up @@ -289,6 +290,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature)
case MessageID.IDS_DefaultInterfaceImplementation: // semantic check
case MessageID.IDS_BaseTypeInBaseExpression:
case MessageID.IDS_OverrideWithConstraints: // semantic check
case MessageID.IDS_FeatureNestedStackalloc: // semantic check
return LanguageVersion.CSharp8;

// C# 7.3 features.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,12 @@ private BoundExpression RewriteAwaitExpression(BoundExpression rewrittenAwait, b
// The resulting nodes will be "spilled" to move such statements to the top
// level (i.e. into the enclosing statement list).
_needsSpilling = true;
BoundLocal replacement = _factory.StoreToTemp(
rewrittenAwait, out BoundAssignmentOperator saveToTemp, kind: SynthesizedLocalKind.Spill, syntaxOpt: rewrittenAwait.Syntax);
return new BoundSpillSequence(
syntax: rewrittenAwait.Syntax,
locals: ImmutableArray.Create<LocalSymbol>(replacement.LocalSymbol),
sideEffects: ImmutableArray.Create<BoundStatement>(_factory.ExpressionStatement(saveToTemp)),
value: replacement,
type: replacement.Type);
locals: ImmutableArray<LocalSymbol>.Empty,
sideEffects: ImmutableArray<BoundStatement>.Empty,
value: rewrittenAwait,
type: rewrittenAwait.Type);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1326,12 +1326,16 @@ private BoundExpression LowerLiftedBinaryArithmeticOperator(
type: type);
}

private BoundExpression CaptureExpressionInTempIfNeeded(BoundExpression operand, ArrayBuilder<BoundExpression> sideeffects, ArrayBuilder<LocalSymbol> locals)
private BoundExpression CaptureExpressionInTempIfNeeded(
BoundExpression operand,
ArrayBuilder<BoundExpression> sideeffects,
ArrayBuilder<LocalSymbol> locals,
SynthesizedLocalKind kind = SynthesizedLocalKind.LoweringTemp)
{
if (CanChangeValueBetweenReads(operand))
{
BoundAssignmentOperator tempAssignment;
var tempAccess = _factory.StoreToTemp(operand, out tempAssignment);
var tempAccess = _factory.StoreToTemp(operand, out tempAssignment, kind: kind);
sideeffects.Add(tempAssignment);
locals.Add(tempAccess.LocalSymbol);
operand = tempAccess;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreat
var spanType = (NamedTypeSymbol)stackAllocNode.Type;
var sideEffects = ArrayBuilder<BoundExpression>.GetInstance();
var locals = ArrayBuilder<LocalSymbol>.GetInstance();
var countTemp = CaptureExpressionInTempIfNeeded(rewrittenCount, sideEffects, locals);
var countTemp = CaptureExpressionInTempIfNeeded(rewrittenCount, sideEffects, locals, SynthesizedLocalKind.Spill);
var stackSize = RewriteStackAllocCountToSize(countTemp, elementType);
stackAllocNode = new BoundConvertedStackAllocExpression(stackAllocNode.Syntax, elementType, stackSize, initializerOpt, spanType);

Expand All @@ -63,7 +63,8 @@ public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreat
type: ErrorTypeSymbol.UnknownResultType);
}

return new BoundSequence(
_needsSpilling = true;
return new BoundSpillSequence(
syntax: stackAllocNode.Syntax,
locals: locals.ToImmutableAndFree(),
sideEffects: sideEffects.ToImmutableAndFree(),
Expand Down
27 changes: 21 additions & 6 deletions src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,12 @@ internal static bool IsValidScopeDesignator(this ExpressionSyntax expression)
}
}

internal static bool IsVariableDeclarationInitialization(this SyntaxNode node)
/// <summary>
/// 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).
/// </summary>
internal static bool IsLocalVariableDeclarationInitializationForPointerStackalloc(this SyntaxNode node)
{
Debug.Assert(node != null);

Expand All @@ -128,15 +133,25 @@ internal static bool IsVariableDeclarationInitialization(this SyntaxNode node)
return false;
}

return variableDeclarator.Parent.IsKind(SyntaxKind.VariableDeclaration);
SyntaxNode variableDeclaration = variableDeclarator.Parent;
if (!variableDeclaration.IsKind(SyntaxKind.VariableDeclaration))
{
return false;
}

return
variableDeclaration.Parent.IsKind(SyntaxKind.LocalDeclarationStatement) ||
variableDeclaration.Parent.IsKind(SyntaxKind.ForStatement);
}

/// <summary>
/// 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
/// Because the instruction cannot have any values on the stack before CLR execution
/// we limited it to assignments and conditional expressions in C# 7.
/// See https://github.com/dotnet/roslyn/issues/22046.
/// In C# 8 we relaxed
/// that by rewriting the code to move it to the statement level where the stack is empty.
/// </summary>
internal static bool IsLegalSpanStackAllocPosition(this SyntaxNode node)
internal static bool IsLegalCSharp73SpanStackAllocPosition(this SyntaxNode node)
{
Debug.Assert(node != null);

Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,11 @@
<target state="translated">skrývání názvů ve vnořených funkcích</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNestedStackalloc">
<source>stackalloc in nested expressions</source>
<target state="new">stackalloc in nested expressions</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNullableReferenceTypes">
<source>nullable reference types</source>
<target state="translated">typy odkazů s možnou hodnotou null</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,11 @@
<target state="translated">Namensshadowing in geschachtelten Funktionen</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNestedStackalloc">
<source>stackalloc in nested expressions</source>
<target state="new">stackalloc in nested expressions</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNullableReferenceTypes">
<source>nullable reference types</source>
<target state="translated">Nullable-Verweistypen</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,11 @@
<target state="translated">sombreado de nombres en funciones anidadas</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNestedStackalloc">
<source>stackalloc in nested expressions</source>
<target state="new">stackalloc in nested expressions</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNullableReferenceTypes">
<source>nullable reference types</source>
<target state="translated">tipos de referencia que aceptan valores NULL</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,11 @@
<target state="translated">ombrage des noms dans les fonctions imbriquées</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNestedStackalloc">
<source>stackalloc in nested expressions</source>
<target state="new">stackalloc in nested expressions</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNullableReferenceTypes">
<source>nullable reference types</source>
<target state="translated">types référence Nullable</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,11 @@
<target state="translated">shadowing dei nomi nelle funzioni annidate</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNestedStackalloc">
<source>stackalloc in nested expressions</source>
<target state="new">stackalloc in nested expressions</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNullableReferenceTypes">
<source>nullable reference types</source>
<target state="translated">tipi riferimento nullable</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,11 @@
<target state="translated">入れ子になった関数での名前シャドウイング</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNestedStackalloc">
<source>stackalloc in nested expressions</source>
<target state="new">stackalloc in nested expressions</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNullableReferenceTypes">
<source>nullable reference types</source>
<target state="translated">Null 許容参照型</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,11 @@
<target state="translated">중첩된 함수의 이름 섀도잉</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNestedStackalloc">
<source>stackalloc in nested expressions</source>
<target state="new">stackalloc in nested expressions</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNullableReferenceTypes">
<source>nullable reference types</source>
<target state="translated">nullable 참조 형식</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,11 @@
<target state="translated">zasłanianie nazw w funkcjach zagnieżdżonych</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNestedStackalloc">
<source>stackalloc in nested expressions</source>
<target state="new">stackalloc in nested expressions</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNullableReferenceTypes">
<source>nullable reference types</source>
<target state="translated">typy referencyjne dopuszczające wartość null</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,11 @@
<target state="translated">sombreamento de nome em funções aninhadas</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNestedStackalloc">
<source>stackalloc in nested expressions</source>
<target state="new">stackalloc in nested expressions</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNullableReferenceTypes">
<source>nullable reference types</source>
<target state="translated">tipos de referência que permitem valor nulo</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,11 @@
<target state="translated">скрытие имен во вложенных функциях</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNestedStackalloc">
<source>stackalloc in nested expressions</source>
<target state="new">stackalloc in nested expressions</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNullableReferenceTypes">
<source>nullable reference types</source>
<target state="translated">ссылочные типы, допускающие значение NULL</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,11 @@
<target state="translated">iç içe işlevlerde ad gölgeleme</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNestedStackalloc">
<source>stackalloc in nested expressions</source>
<target state="new">stackalloc in nested expressions</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNullableReferenceTypes">
<source>nullable reference types</source>
<target state="translated">boş değer atanabilir başvuru türleri</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,11 @@
<target state="translated">在嵌套函数中的名称映射</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNestedStackalloc">
<source>stackalloc in nested expressions</source>
<target state="new">stackalloc in nested expressions</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNullableReferenceTypes">
<source>nullable reference types</source>
<target state="translated">可为 null 的引用类型</target>
Expand Down
Loading

0 comments on commit 7517f5d

Please sign in to comment.