Skip to content

Commit

Permalink
List patterns: IOperation (#56008)
Browse files Browse the repository at this point in the history
  • Loading branch information
alrz authored Oct 28, 2021
1 parent 06beb83 commit 59a0608
Show file tree
Hide file tree
Showing 9 changed files with 669 additions and 4 deletions.
15 changes: 11 additions & 4 deletions src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2269,8 +2269,12 @@ private IOperation CreateBoundTypePatternOperation(BoundTypePattern boundTypePat

private IOperation CreateBoundSlicePatternOperation(BoundSlicePattern boundNode)
{
// PROTOTYPE(list-patterns) IOperation
return new DiscardPatternOperation(
return new SlicePatternOperation(
sliceSymbol: boundNode.Pattern is null ? null :
(boundNode.InputType.IsSZArray()
? (Symbol?)_semanticModel.Compilation.CommonGetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__GetSubArray_T)
: (Symbol?)boundNode.SliceMethod ?? boundNode.IndexerAccess?.Indexer).GetPublicSymbol(),
pattern: (IPatternOperation?)Create(boundNode.Pattern),
inputType: boundNode.InputType.GetPublicSymbol(),
narrowedType: boundNode.NarrowedType.GetPublicSymbol(),
_semanticModel,
Expand All @@ -2280,8 +2284,11 @@ private IOperation CreateBoundSlicePatternOperation(BoundSlicePattern boundNode)

private IOperation CreateBoundListPatternOperation(BoundListPattern boundNode)
{
// PROTOTYPE(list-patterns) IOperation
return new DiscardPatternOperation(
return new ListPatternOperation(
lengthSymbol: boundNode.LengthProperty.GetPublicSymbol(),
indexerSymbol: (boundNode.IndexerSymbol ?? boundNode.IndexerAccess?.Indexer).GetPublicSymbol(),
patterns: boundNode.Subpatterns.SelectAsArray((p, fac) => (IPatternOperation)fac.Create(p), this),
declaredSymbol: boundNode.Variable.GetPublicSymbol(),
inputType: boundNode.InputType.GetPublicSymbol(),
narrowedType: boundNode.NarrowedType.GetPublicSymbol(),
_semanticModel,
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -255,5 +255,9 @@ public enum OperationKind
RelationalPattern = 0x70,
/// <summary>Indicates an <see cref="IWithOperation"/>.</summary>
With = 0x71,
/// <summary>Indicates an <see cref="IListPatternOperation"/>.</summary>
ListPattern = 0x72,
/// <summary>Indicates an <see cref="ISlicePatternOperation"/>.</summary>
SlicePattern = 0x73,
}
}
148 changes: 148 additions & 0 deletions src/Compilers/Core/Portable/Generated/Operations.Generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3320,6 +3320,60 @@ public interface IWithOperation : IOperation
/// </summary>
IObjectOrCollectionInitializerOperation Initializer { get; }
}
/// <summary>
/// Represents a C# list pattern.
/// </summary>
/// <remarks>
/// <para>This node is associated with the following operation kinds:</para>
/// <list type="bullet">
/// <item><description><see cref="OperationKind.ListPattern"/></description></item>
/// </list>
/// <para>This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.</para>
/// </remarks>
public interface IListPatternOperation : IPatternOperation
{
/// <summary>
/// The <c>Length</c> or <c>Count</c> property that is being used to fetch the length value.
/// Returns <c>null</c> if no such property is found.
/// </summary>
ISymbol? LengthSymbol { get; }
/// <summary>
/// The indexer that is being used to fetch elements.
/// Returns <c>null</c> for an array input.
/// </summary>
ISymbol? IndexerSymbol { get; }
/// <summary>
/// Returns subpatterns contained within the list pattern.
/// </summary>
ImmutableArray<IPatternOperation> Patterns { get; }
/// <summary>
/// Symbol declared by the pattern, if any.
/// </summary>
ISymbol? DeclaredSymbol { get; }
}
/// <summary>
/// Represents a C# slice pattern.
/// </summary>
/// <remarks>
/// <para>This node is associated with the following operation kinds:</para>
/// <list type="bullet">
/// <item><description><see cref="OperationKind.SlicePattern"/></description></item>
/// </list>
/// <para>This interface is reserved for implementation by its associated APIs. We reserve the right to
/// change it in the future.</para>
/// </remarks>
public interface ISlicePatternOperation : IPatternOperation
{
/// <summary>
/// The range indexer or the <c>Slice</c> method used to fetch the slice value.
/// </summary>
ISymbol? SliceSymbol { get; }
/// <summary>
/// The pattern that the slice value is matched with, if any.
/// </summary>
IPatternOperation? Pattern { get; }
}
#endregion

#region Implementations
Expand Down Expand Up @@ -7602,6 +7656,86 @@ protected override (bool hasNext, int nextSlot, int nextIndex) MoveNext(int prev
public override void Accept(OperationVisitor visitor) => visitor.VisitWith(this);
public override TResult? Accept<TArgument, TResult>(OperationVisitor<TArgument, TResult> visitor, TArgument argument) where TResult : default => visitor.VisitWith(this, argument);
}
internal sealed partial class ListPatternOperation : BasePatternOperation, IListPatternOperation
{
internal ListPatternOperation(ISymbol? lengthSymbol, ISymbol? indexerSymbol, ImmutableArray<IPatternOperation> patterns, ISymbol? declaredSymbol, ITypeSymbol inputType, ITypeSymbol narrowedType, SemanticModel? semanticModel, SyntaxNode syntax, bool isImplicit)
: base(inputType, narrowedType, semanticModel, syntax, isImplicit)
{
LengthSymbol = lengthSymbol;
IndexerSymbol = indexerSymbol;
Patterns = SetParentOperation(patterns, this);
DeclaredSymbol = declaredSymbol;
}
public ISymbol? LengthSymbol { get; }
public ISymbol? IndexerSymbol { get; }
public ImmutableArray<IPatternOperation> Patterns { get; }
public ISymbol? DeclaredSymbol { get; }
protected override IOperation GetCurrent(int slot, int index)
=> slot switch
{
0 when index < Patterns.Length
=> Patterns[index],
_ => throw ExceptionUtilities.UnexpectedValue((slot, index)),
};
protected override (bool hasNext, int nextSlot, int nextIndex) MoveNext(int previousSlot, int previousIndex)
{
switch (previousSlot)
{
case -1:
if (!Patterns.IsEmpty) return (true, 0, 0);
else goto case 0;
case 0 when previousIndex + 1 < Patterns.Length:
return (true, 0, previousIndex + 1);
case 0:
case 1:
return (false, 1, 0);
default:
throw ExceptionUtilities.UnexpectedValue((previousSlot, previousIndex));
}
}
public override ITypeSymbol? Type => null;
internal override ConstantValue? OperationConstantValue => null;
public override OperationKind Kind => OperationKind.ListPattern;
public override void Accept(OperationVisitor visitor) => visitor.VisitListPattern(this);
public override TResult? Accept<TArgument, TResult>(OperationVisitor<TArgument, TResult> visitor, TArgument argument) where TResult : default => visitor.VisitListPattern(this, argument);
}
internal sealed partial class SlicePatternOperation : BasePatternOperation, ISlicePatternOperation
{
internal SlicePatternOperation(ISymbol? sliceSymbol, IPatternOperation? pattern, ITypeSymbol inputType, ITypeSymbol narrowedType, SemanticModel? semanticModel, SyntaxNode syntax, bool isImplicit)
: base(inputType, narrowedType, semanticModel, syntax, isImplicit)
{
SliceSymbol = sliceSymbol;
Pattern = SetParentOperation(pattern, this);
}
public ISymbol? SliceSymbol { get; }
public IPatternOperation? Pattern { get; }
protected override IOperation GetCurrent(int slot, int index)
=> slot switch
{
0 when Pattern != null
=> Pattern,
_ => throw ExceptionUtilities.UnexpectedValue((slot, index)),
};
protected override (bool hasNext, int nextSlot, int nextIndex) MoveNext(int previousSlot, int previousIndex)
{
switch (previousSlot)
{
case -1:
if (Pattern != null) return (true, 0, 0);
else goto case 0;
case 0:
case 1:
return (false, 1, 0);
default:
throw ExceptionUtilities.UnexpectedValue((previousSlot, previousIndex));
}
}
public override ITypeSymbol? Type => null;
internal override ConstantValue? OperationConstantValue => null;
public override OperationKind Kind => OperationKind.SlicePattern;
public override void Accept(OperationVisitor visitor) => visitor.VisitSlicePattern(this);
public override TResult? Accept<TArgument, TResult>(OperationVisitor<TArgument, TResult> visitor, TArgument argument) where TResult : default => visitor.VisitSlicePattern(this, argument);
}
#endregion
#region Cloner
internal sealed partial class OperationCloner : OperationVisitor<object?, IOperation>
Expand Down Expand Up @@ -8160,6 +8294,16 @@ public override IOperation VisitWith(IWithOperation operation, object? argument)
var internalOperation = (WithOperation)operation;
return new WithOperation(Visit(internalOperation.Operand), internalOperation.CloneMethod, Visit(internalOperation.Initializer), internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.Type, internalOperation.IsImplicit);
}
public override IOperation VisitListPattern(IListPatternOperation operation, object? argument)
{
var internalOperation = (ListPatternOperation)operation;
return new ListPatternOperation(internalOperation.LengthSymbol, internalOperation.IndexerSymbol, VisitArray(internalOperation.Patterns), internalOperation.DeclaredSymbol, internalOperation.InputType, internalOperation.NarrowedType, internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.IsImplicit);
}
public override IOperation VisitSlicePattern(ISlicePatternOperation operation, object? argument)
{
var internalOperation = (SlicePatternOperation)operation;
return new SlicePatternOperation(internalOperation.SliceSymbol, Visit(internalOperation.Pattern), internalOperation.InputType, internalOperation.NarrowedType, internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.IsImplicit);
}
}
#endregion

Expand Down Expand Up @@ -8289,6 +8433,8 @@ internal virtual void VisitNoneOperation(IOperation operation) { /* no-op */ }
public virtual void VisitTypePattern(ITypePatternOperation operation) => DefaultVisit(operation);
public virtual void VisitRelationalPattern(IRelationalPatternOperation operation) => DefaultVisit(operation);
public virtual void VisitWith(IWithOperation operation) => DefaultVisit(operation);
public virtual void VisitListPattern(IListPatternOperation operation) => DefaultVisit(operation);
public virtual void VisitSlicePattern(ISlicePatternOperation operation) => DefaultVisit(operation);
}
public abstract partial class OperationVisitor<TArgument, TResult>
{
Expand Down Expand Up @@ -8415,6 +8561,8 @@ public abstract partial class OperationVisitor<TArgument, TResult>
public virtual TResult? VisitTypePattern(ITypePatternOperation operation, TArgument argument) => DefaultVisit(operation, argument);
public virtual TResult? VisitRelationalPattern(IRelationalPatternOperation operation, TArgument argument) => DefaultVisit(operation, argument);
public virtual TResult? VisitWith(IWithOperation operation, TArgument argument) => DefaultVisit(operation, argument);
public virtual TResult? VisitListPattern(IListPatternOperation operation, TArgument argument) => DefaultVisit(operation, argument);
public virtual TResult? VisitSlicePattern(ISlicePatternOperation operation, TArgument argument) => DefaultVisit(operation, argument);
}
#endregion
}
26 changes: 26 additions & 0 deletions src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6907,6 +6907,32 @@ public override IOperation VisitDeclarationPattern(IDeclarationPatternOperation
IsImplicit(operation));
}

public override IOperation VisitSlicePattern(ISlicePatternOperation operation, int? argument)
{
return new SlicePatternOperation(
operation.SliceSymbol,
(IPatternOperation?)Visit(operation.Pattern),
operation.InputType,
operation.NarrowedType,
semanticModel: null,
operation.Syntax,
IsImplicit(operation));
}

public override IOperation VisitListPattern(IListPatternOperation operation, int? argument)
{
return new ListPatternOperation(
operation.LengthSymbol,
operation.IndexerSymbol,
operation.Patterns.SelectAsArray((p, @this) => (IPatternOperation)@this.VisitRequired(p), this),
operation.DeclaredSymbol,
operation.InputType,
operation.NarrowedType,
semanticModel: null,
operation.Syntax,
IsImplicit(operation));
}

public override IOperation VisitRecursivePattern(IRecursivePatternOperation operation, int? argument)
{
return new RecursivePatternOperation(
Expand Down
54 changes: 54 additions & 0 deletions src/Compilers/Core/Portable/Operations/OperationInterfaces.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3057,4 +3057,58 @@
</Comments>
</Property>
</Node>
<Node Name="IListPatternOperation" Base="IPatternOperation">
<Comments>
<summary>Represents a C# list pattern.</summary>
</Comments>
<Property Name="LengthSymbol" Type="ISymbol?">
<Comments>
<summary>
The <c>Length</c> or <c>Count</c> property that is being used to fetch the length value.
Returns <c>null</c> if no such property is found.
</summary>
</Comments>
</Property>
<Property Name="IndexerSymbol" Type="ISymbol?">
<Comments>
<summary>
The indexer that is being used to fetch elements.
Returns <c>null</c> for an array input.
</summary>
</Comments>
</Property>
<Property Name="Patterns" Type="ImmutableArray&lt;IPatternOperation&gt;">
<Comments>
<summary>
Returns subpatterns contained within the list pattern.
</summary>
</Comments>
</Property>
<Property Name="DeclaredSymbol" Type="ISymbol?">
<Comments>
<summary>Symbol declared by the pattern, if any.</summary>
</Comments>
</Property>
</Node>
<Node Name="ISlicePatternOperation" Base="IPatternOperation">
<Comments>
<summary>
Represents a C# slice pattern.
</summary>
</Comments>
<Property Name="SliceSymbol" Type="ISymbol?">
<Comments>
<summary>
The range indexer or the <c>Slice</c> method used to fetch the slice value.
</summary>
</Comments>
</Property>
<Property Name="Pattern" Type="IPatternOperation?">
<Comments>
<summary>
The pattern that the slice value is matched with, if any.
</summary>
</Comments>
</Property>
</Node>
</Tree>
Original file line number Diff line number Diff line change
Expand Up @@ -1904,6 +1904,8 @@ propertyReference.Parent is ISimpleAssignmentOperation simpleAssignment &&
case OperationKind.NegatedPattern:
case OperationKind.BinaryPattern:
case OperationKind.TypePattern:
case OperationKind.SlicePattern:
case OperationKind.ListPattern:
return true;
}

Expand Down
23 changes: 23 additions & 0 deletions src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1843,6 +1843,29 @@ public override void VisitDeclarationPattern(IDeclarationPatternOperation operat
LogNewLine();
}

public override void VisitSlicePattern(ISlicePatternOperation operation)
{
LogString(nameof(ISlicePatternOperation));
LogPatternProperties(operation);
LogSymbol(operation.SliceSymbol, $", {nameof(operation.SliceSymbol)}");
LogNewLine();

Visit(operation.Pattern, $"{nameof(operation.Pattern)}");
}

public override void VisitListPattern(IListPatternOperation operation)
{
LogString(nameof(IListPatternOperation));
LogPatternProperties(operation);
LogSymbol(operation.DeclaredSymbol, $", {nameof(operation.DeclaredSymbol)}");
LogSymbol(operation.LengthSymbol, $", {nameof(operation.LengthSymbol)}");
LogSymbol(operation.IndexerSymbol, $", {nameof(operation.IndexerSymbol)}");
LogString(")");
LogNewLine();

VisitArray(operation.Patterns, $"{nameof(operation.Patterns)} ", true, true);
}

public override void VisitRecursivePattern(IRecursivePatternOperation operation)
{
LogString(nameof(IRecursivePatternOperation));
Expand Down
33 changes: 33 additions & 0 deletions src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,39 @@ public override void VisitDeclarationPattern(IDeclarationPatternOperation operat
Assert.Empty(operation.Children);
}

public override void VisitSlicePattern(ISlicePatternOperation operation)
{
Assert.Equal(OperationKind.SlicePattern, operation.Kind);
VisitPatternCommon(operation);

if (operation.Pattern != null)
{
Assert.Same(operation.Pattern, operation.Children.Single());
}
else
{
Assert.Empty(operation.Children);
}
}

public override void VisitListPattern(IListPatternOperation operation)
{
Assert.Equal(OperationKind.ListPattern, operation.Kind);
VisitPatternCommon(operation);
var designation = (operation.Syntax as CSharp.Syntax.ListPatternSyntax)?.Designation;
if (designation.IsKind(CSharp.SyntaxKind.SingleVariableDesignation))
{
Assert.NotNull(operation.DeclaredSymbol);
}
else
{
Assert.Null(operation.DeclaredSymbol);
}

IEnumerable<IOperation> children = operation.Patterns.Cast<IOperation>();
AssertEx.Equal(children, operation.Children);
}

public override void VisitRecursivePattern(IRecursivePatternOperation operation)
{
Assert.Equal(OperationKind.RecursivePattern, operation.Kind);
Expand Down

0 comments on commit 59a0608

Please sign in to comment.