Skip to content

Commit

Permalink
Add flag to constrained array accesses
Browse files Browse the repository at this point in the history
  • Loading branch information
jjonescz committed Nov 24, 2023
1 parent cd172aa commit 7f0f874
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 49 deletions.
21 changes: 21 additions & 0 deletions src/Compilers/CSharp/Portable/BoundTree/BoundArrayAccess.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CSharp.Symbols;

namespace Microsoft.CodeAnalysis.CSharp;

internal partial class BoundArrayAccess
{
public BoundArrayAccess(SyntaxNode syntax, BoundExpression expression, ImmutableArray<BoundExpression> indices, TypeSymbol type, bool hasErrors = false)
: this(syntax, expression, indices, inCompoundAssignmentReceiver: false, type, hasErrors)
{
}

public BoundArrayAccess Update(BoundExpression expression, ImmutableArray<BoundExpression> indices, TypeSymbol type)
{
return this.Update(expression, indices, inCompoundAssignmentReceiver: false, type);
}
}
1 change: 1 addition & 0 deletions src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,7 @@

<Field Name="Expression" Type="BoundExpression"/>
<Field Name="Indices" Type="ImmutableArray&lt;BoundExpression&gt;"/>
<Field Name="InCompoundAssignmentReceiver" Type="bool" />
</Node>

<!--
Expand Down
3 changes: 2 additions & 1 deletion src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,8 @@ private void EmitArrayElementAddress(BoundArrayAccess arrayAccess, AddressKind a

private bool ShouldEmitReadOnlyPrefix(BoundArrayAccess arrayAccess, AddressKind addressKind)
{
if (addressKind == AddressKind.Constrained)
if (addressKind == AddressKind.Constrained ||
arrayAccess.InCompoundAssignmentReceiver)
{
Debug.Assert(arrayAccess.Type.TypeKind == TypeKind.TypeParameter, "constrained call should only be used with type parameter types");
return true;
Expand Down
11 changes: 3 additions & 8 deletions src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2530,7 +2530,7 @@ private void EmitAssignmentExpression(BoundAssignmentOperator assignmentOperator
// * Post-storage: If we stashed away the duplicated value in the temporary, we need to restore it back to the stack.

bool lhsUsesStack = EmitAssignmentPreamble(assignmentOperator);
EmitAssignmentValue(assignmentOperator, useKind);
EmitAssignmentValue(assignmentOperator);
LocalDefinition temp = EmitAssignmentDuplication(assignmentOperator, useKind, lhsUsesStack);
EmitStore(assignmentOperator);
EmitAssignmentPostfix(assignmentOperator, temp, useKind);
Expand Down Expand Up @@ -2954,7 +2954,7 @@ private bool EmitAssignmentPreamble(BoundAssignmentOperator assignmentOperator)
return lhsUsesStack;
}

private void EmitAssignmentValue(BoundAssignmentOperator assignmentOperator, UseKind useKind)
private void EmitAssignmentValue(BoundAssignmentOperator assignmentOperator)
{
if (!assignmentOperator.IsRef)
{
Expand All @@ -2967,12 +2967,7 @@ private void EmitAssignmentValue(BoundAssignmentOperator assignmentOperator, Use

// NOTE: passing "ReadOnlyStrict" here.
// we should not get an address of a copy if at all possible
LocalDefinition temp = EmitAddress(assignmentOperator.Right,
lhs.GetRefKind() is RefKind.RefReadOnly or RefKindExtensions.StrictIn or RefKind.RefReadOnlyParameter
? AddressKind.ReadOnlyStrict
: useKind == UseKind.Unused && assignmentOperator.Right.Type is { TypeKind: TypeKind.TypeParameter, IsValueType: false }
? AddressKind.Constrained
: AddressKind.Writeable);
LocalDefinition temp = EmitAddress(assignmentOperator.Right, lhs.GetRefKind() is RefKind.RefReadOnly or RefKindExtensions.StrictIn or RefKind.RefReadOnlyParameter ? AddressKind.ReadOnlyStrict : AddressKind.Writeable);

// Generally taking a ref for the purpose of ref assignment should not be done on homeless values
// however, there are very rare cases when we need to get a ref off a temp in synthetic code.
Expand Down

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

Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ internal sealed partial class LocalRewriter : BoundTreeRewriterWithStackGuard
private bool _sawAwait;
private bool _sawAwaitInExceptionHandler;
private bool _needsSpilling;
private bool _inCompoundAssignmentReceiver;
private readonly BindingDiagnosticBag _diagnostics;
private readonly BoundStatement _rootStatement;

Expand Down Expand Up @@ -773,38 +774,48 @@ public override BoundNode VisitArrayAccess(BoundArrayAccess node)
// The last two are only supported on SZArrays. For those cases we need to
// lower into the appropriate helper methods.

BoundNode resultExpr;
if (node.Indices.Length != 1)
{
return base.VisitArrayAccess(node)!;
resultExpr = base.VisitArrayAccess(node)!;
}

var indexType = VisitType(node.Indices[0].Type);
var F = _factory;

BoundNode resultExpr;
if (TypeSymbol.Equals(
indexType,
_compilation.GetWellKnownType(WellKnownType.System_Range),
TypeCompareKind.ConsiderEverything))
else
{
// array[Range] is compiled to:
// System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray(array, Range)

Debug.Assert(node.Expression.Type is { TypeKind: TypeKind.Array });
var elementType = ((ArrayTypeSymbol)node.Expression.Type).ElementTypeWithAnnotations;

resultExpr = F.Call(
receiver: null,
F.WellKnownMethod(WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__GetSubArray_T)
.Construct(ImmutableArray.Create(elementType)),
ImmutableArray.Create(
VisitExpression(node.Expression),
VisitExpression(node.Indices[0])));
var indexType = VisitType(node.Indices[0].Type);
var F = _factory;

if (TypeSymbol.Equals(
indexType,
_compilation.GetWellKnownType(WellKnownType.System_Range),
TypeCompareKind.ConsiderEverything))
{
// array[Range] is compiled to:
// System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray(array, Range)

Debug.Assert(node.Expression.Type is { TypeKind: TypeKind.Array });
var elementType = ((ArrayTypeSymbol)node.Expression.Type).ElementTypeWithAnnotations;

resultExpr = F.Call(
receiver: null,
F.WellKnownMethod(WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__GetSubArray_T)
.Construct(ImmutableArray.Create(elementType)),
ImmutableArray.Create(
VisitExpression(node.Expression),
VisitExpression(node.Indices[0])));
}
else
{
resultExpr = base.VisitArrayAccess(node)!;
}
}
else

if (_inCompoundAssignmentReceiver &&
node.Expression.Type is ArrayTypeSymbol { ElementType: { TypeKind: TypeKind.TypeParameter, IsValueType: false } } &&
resultExpr is BoundArrayAccess arr)
{
resultExpr = base.VisitArrayAccess(node)!;
resultExpr = arr.Update(arr.Expression, arr.Indices, inCompoundAssignmentReceiver: true, arr.Type);
}

return resultExpr;
}

Expand Down
Loading

0 comments on commit 7f0f874

Please sign in to comment.