Skip to content

Commit

Permalink
Implement stackalloc initializers
Browse files Browse the repository at this point in the history
  • Loading branch information
alrz committed Jan 26, 2018
1 parent 733a83a commit 9c31503
Show file tree
Hide file tree
Showing 29 changed files with 2,755 additions and 89 deletions.
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/BinderFlagsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,10 @@ public static bool Includes(this BinderFlags self, BinderFlags other)
{
return (self & other) == other;
}

public static bool IncludesAny(this BinderFlags self, BinderFlags other)
{
return (self & other) != 0;
}
}
}
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ private BoundExpression CreateStackAllocConversion(SyntaxNode syntax, BoundExpre
throw ExceptionUtilities.UnexpectedValue(conversion.Kind);
}

var convertedNode = new BoundConvertedStackAllocExpression(syntax, elementType, boundStackAlloc.Count, stackAllocType, boundStackAlloc.HasErrors);
var convertedNode = new BoundConvertedStackAllocExpression(syntax, elementType, boundStackAlloc.Count, boundStackAlloc.InitializerOpt, stackAllocType, boundStackAlloc.HasErrors);

var underlyingConversion = conversion.UnderlyingConversions.Single();
return CreateConversion(syntax, convertedNode, underlyingConversion, isCast, destination, diagnostics);
Expand Down
160 changes: 130 additions & 30 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,8 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, Diagnostic
return BindImplicitArrayCreationExpression((ImplicitArrayCreationExpressionSyntax)node, diagnostics);
case SyntaxKind.StackAllocArrayCreationExpression:
return BindStackAllocArrayCreationExpression((StackAllocArrayCreationExpressionSyntax)node, diagnostics);
case SyntaxKind.ImplicitStackAllocArrayCreationExpression:
return BindImplicitStackAllocArrayCreationExpression((ImplicitStackAllocArrayCreationExpressionSyntax)node, diagnostics);
case SyntaxKind.ObjectCreationExpression:
return BindObjectCreationExpression((ObjectCreationExpressionSyntax)node, diagnostics);
case SyntaxKind.IdentifierName:
Expand Down Expand Up @@ -2714,6 +2716,40 @@ private BoundExpression BindImplicitArrayCreationExpression(ImplicitArrayCreatio
sizes: ImmutableArray<BoundExpression>.Empty, boundInitExprOpt: boundInitializerExpressions);
}


private BoundExpression BindImplicitStackAllocArrayCreationExpression(ImplicitStackAllocArrayCreationExpressionSyntax node, DiagnosticBag diagnostics)
{
bool inLegalPosition = ReportBadStackAllocPosition(node, diagnostics);
bool hasErrors = !inLegalPosition;

InitializerExpressionSyntax initializer = node.Initializer;
ImmutableArray<BoundExpression> boundInitializerExpressions = BindArrayInitializerExpressions(initializer, diagnostics, dimension: 1, rank: 1);

HashSet<DiagnosticInfo> useSiteDiagnostics = null;
TypeSymbol bestType = BestTypeInferrer.InferBestType(boundInitializerExpressions, this.Conversions, out bool hadMultipleCandidates, ref useSiteDiagnostics);
diagnostics.Add(node, useSiteDiagnostics);

if ((object)bestType == null || bestType.SpecialType == SpecialType.System_Void)
{
Error(diagnostics, ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, node);
bestType = CreateErrorType();
}

if (!bestType.IsErrorType() && bestType.IsManagedType)
{
Error(diagnostics, ErrorCode.ERR_ManagedAddr, node, bestType);
}

return BindStackAllocWithInitializer(
node,
initializer,
type: GetStackAllocType(node, bestType, inLegalPosition, diagnostics),
elementType: bestType,
sizeOpt: null,
diagnostics,
hasErrors: hasErrors);
}

// This method binds all the array initializer expressions.
// NOTE: It doesn't convert the bound initializer expressions to array's element type.
// NOTE: This is done separately in ConvertAndBindArrayInitialization method below.
Expand Down Expand Up @@ -3032,22 +3068,11 @@ private BoundArrayCreation BindArrayCreationWithInitializer(
private BoundExpression BindStackAllocArrayCreationExpression(
StackAllocArrayCreationExpressionSyntax node, DiagnosticBag diagnostics)
{
bool hasErrors = false;
var inLegalPosition = (IsInMethodBody || IsLocalFunctionsScopeBinder) && node.IsLegalSpanStackAllocPosition();

if (!inLegalPosition)
{
hasErrors = true;
diagnostics.Add(
ErrorCode.ERR_InvalidExprTerm,
node.StackAllocKeyword.GetLocation(),
SyntaxFacts.GetText(SyntaxKind.StackAllocKeyword));
}
bool inLegalPosition = ReportBadStackAllocPosition(node, diagnostics);
bool hasErrors = !inLegalPosition;

// Check if we're syntactically within a catch or finally clause.
if (this.Flags.Includes(BinderFlags.InCatchBlock) ||
this.Flags.Includes(BinderFlags.InCatchFilter) ||
this.Flags.Includes(BinderFlags.InFinallyBlock))
if (this.Flags.IncludesAny(BinderFlags.InCatchBlock | BinderFlags.InCatchFilter | BinderFlags.InFinallyBlock))
{
Error(diagnostics, ErrorCode.ERR_StackallocInCatchFinally, node);
}
Expand All @@ -3070,24 +3095,22 @@ private BoundExpression BindStackAllocArrayCreationExpression(
TypeSyntax elementTypeSyntax = arrayTypeSyntax.ElementType;
TypeSymbol elementType = BindType(elementTypeSyntax, diagnostics);

bool typeHasErrors = elementType.IsErrorType();
if (!typeHasErrors && elementType.IsManagedType)
if (!elementType.IsErrorType() && elementType.IsManagedType)
{
Error(diagnostics, ErrorCode.ERR_ManagedAddr, elementTypeSyntax, elementType);
typeHasErrors = true;
hasErrors = true;
}

SyntaxList<ArrayRankSpecifierSyntax> rankSpecifiers = arrayTypeSyntax.RankSpecifiers;

if (rankSpecifiers.Count != 1 ||
rankSpecifiers[0].Sizes.Count != 1 ||
rankSpecifiers[0].Sizes[0].Kind() == SyntaxKind.OmittedArraySizeExpression)
rankSpecifiers[0].Sizes.Count != 1)
{
// NOTE: Dev10 reported several parse errors here.
Error(diagnostics, ErrorCode.ERR_BadStackAllocExpr, typeSyntax);

var builder = ArrayBuilder<BoundExpression>.GetInstance();
DiagnosticBag discardedDiagnostics = DiagnosticBag.GetInstance();
var discardedDiagnostics = DiagnosticBag.GetInstance();
foreach (ArrayRankSpecifierSyntax rankSpecifier in rankSpecifiers)
{
foreach (ExpressionSyntax size in rankSpecifier.Sizes)
Expand All @@ -3098,6 +3121,7 @@ private BoundExpression BindStackAllocArrayCreationExpression(
}
}
}

discardedDiagnostics.Free();

return new BoundBadExpression(
Expand All @@ -3108,20 +3132,54 @@ private BoundExpression BindStackAllocArrayCreationExpression(
new PointerTypeSymbol(elementType));
}

TypeSymbol type = GetStackAllocType(node, elementType, inLegalPosition, diagnostics);

ExpressionSyntax countSyntax = rankSpecifiers[0].Sizes[0];
var count = BindValue(countSyntax, diagnostics, BindValueKind.RValue);
if (!count.HasAnyErrors)
BoundExpression count = null;
if (countSyntax.Kind() != SyntaxKind.OmittedArraySizeExpression)
{
// NOTE: this is different from how we would bind an array size (in which case we would allow uint, long, or ulong).
count = GenerateConversionForAssignment(GetSpecialType(SpecialType.System_Int32, diagnostics, node), count, diagnostics);
if (!count.HasAnyErrors && IsNegativeConstantForArraySize(count))
count = BindValue(countSyntax, diagnostics, BindValueKind.RValue);
if (!count.HasAnyErrors)
{
Error(diagnostics, ErrorCode.ERR_NegativeStackAllocSize, countSyntax);
hasErrors = true;
// NOTE: this is different from how we would bind an array size (in which case we would allow uint, long, or ulong).
count = GenerateConversionForAssignment(GetSpecialType(SpecialType.System_Int32, diagnostics, node), count, diagnostics);
if (!count.HasAnyErrors && IsNegativeConstantForArraySize(count))
{
Error(diagnostics, ErrorCode.ERR_NegativeStackAllocSize, countSyntax);
hasErrors = true;
}
}
}
else if (node.Initializer == null)
{
// ERR_MissingArraySize is already reported
count = BadExpression(countSyntax);
hasErrors = true;
}

TypeSymbol type = null;
return node.Initializer == null
? new BoundStackAllocArrayCreation(node, elementType, count, initializerOpt: null, type, hasErrors: hasErrors)
: BindStackAllocWithInitializer(node, node.Initializer, type, elementType, count, diagnostics, hasErrors);
}

private bool ReportBadStackAllocPosition(SyntaxNode node, DiagnosticBag diagnostics)
{
Debug.Assert(node is StackAllocArrayCreationExpressionSyntax || node is ImplicitStackAllocArrayCreationExpressionSyntax);

var inLegalPosition = (IsInMethodBody || IsLocalFunctionsScopeBinder) && node.IsLegalSpanStackAllocPosition();
if (!inLegalPosition)
{
diagnostics.Add(
ErrorCode.ERR_InvalidExprTerm,
node.GetFirstToken().GetLocation(),
SyntaxFacts.GetText(SyntaxKind.StackAllocKeyword));
}

return inLegalPosition;
}

private TypeSymbol GetStackAllocType(SyntaxNode node, TypeSymbol elementType, bool inLegalPosition, DiagnosticBag diagnostics)
{
if (inLegalPosition && !node.IsVariableDeclarationInitialization())
{
CheckFeatureAvailability(node, MessageID.IDS_FeatureRefStructs, diagnostics);
Expand All @@ -3130,11 +3188,53 @@ private BoundExpression BindStackAllocArrayCreationExpression(
var spanType = GetWellKnownType(WellKnownType.System_Span_T, diagnostics, node);
if (!spanType.IsErrorType())
{
type = spanType.Construct(elementType);
return spanType.Construct(elementType);
}
}

return new BoundStackAllocArrayCreation(node, elementType, count, type, hasErrors: hasErrors || typeHasErrors);
return null;
}

private BoundExpression BindStackAllocWithInitializer(
SyntaxNode node,
InitializerExpressionSyntax initSyntax,
TypeSymbol type,
TypeSymbol elementType,
BoundExpression sizeOpt,
DiagnosticBag diagnostics,
bool hasErrors,
ImmutableArray<BoundExpression> boundInitExprOpt = default)
{

if (boundInitExprOpt.IsDefault)
{
boundInitExprOpt = BindArrayInitializerExpressions(initSyntax, diagnostics, dimension: 1, rank: 1)
.SelectAsArray((expr, t) => GenerateConversionForAssignment(t.elementType, expr, t.diagnostics), (elementType, diagnostics));
}

if (sizeOpt != null)
{
int? constantSizeOpt = GetIntegerConstantForArraySize(sizeOpt);
if (!sizeOpt.HasAnyErrors && constantSizeOpt == null)
{
Error(diagnostics, ErrorCode.ERR_ConstantExpected, sizeOpt.Syntax);
hasErrors = true;
}
else if (boundInitExprOpt.Length != constantSizeOpt)
{
Error(diagnostics, ErrorCode.ERR_ArrayInitializerIncorrectLength, node, constantSizeOpt.Value);
hasErrors = true;
}
}
else
{
sizeOpt = new BoundLiteral(
node,
ConstantValue.Create(boundInitExprOpt.Length),
GetSpecialType(SpecialType.System_Int32, diagnostics, node))
{ WasCompilerGenerated = true };
}
return new BoundStackAllocArrayCreation(node, elementType, sizeOpt, new BoundArrayInitialization(initSyntax, boundInitExprOpt), type, hasErrors);
}

private static int? GetIntegerConstantForArraySize(BoundExpression expression)
Expand Down
3 changes: 2 additions & 1 deletion src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1444,7 +1444,8 @@

<Node Name="BoundStackAllocArrayCreation" Base="BoundExpression">
<Field Name="ElementType" Type="TypeSymbol" Null="disallow"/>
<Field Name="Count" Type="BoundExpression"/>
<Field Name="Count" Type="BoundExpression" Null="disallow" />
<Field Name="InitializerOpt" Type="BoundArrayInitialization" Null="allow"/>
</Node>

<Node Name="BoundConvertedStackAllocExpression" Base="BoundStackAllocArrayCreation">
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 @@ -5253,4 +5253,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_InDynamicMethodArg" xml:space="preserve">
<value>Arguments with 'in' modifier cannot be used in dynamically dispatched expessions.</value>
</data>
<data name="IDS_FeatureStackAllocInitializer" xml:space="preserve">
<value>stackalloc initilizer</value>
</data>
</root>
17 changes: 17 additions & 0 deletions src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1889,6 +1889,23 @@ private void EmitConvertedStackAllocExpression(BoundConvertedStackAllocExpressio
{
_builder.EmitOpCode(ILOpCode.Localloc);
}

var initializer = expression.InitializerOpt;
if (initializer != null)
{
if (used)
{
EmitStackAllocInitializers(expression.Type, initializer);
}
else
{
// If not used, just emit initializer elements to preserve possible sideeffects
foreach (var init in initializer.Initializers)
{
EmitExpression(init, used: false);
}
}
}
}

private void EmitObjectCreationExpression(BoundObjectCreationExpression expression, bool used)
Expand Down
Loading

0 comments on commit 9c31503

Please sign in to comment.