提交 805ca66a 编写于 作者: N Neal Gafter

Checkpoint working snapshot.

上级 14711f39
...@@ -187,20 +187,7 @@ private bool ApplicableOperatorIs(MethodSymbol candidate, CSharpSyntaxNode node, ...@@ -187,20 +187,7 @@ private bool ApplicableOperatorIs(MethodSymbol candidate, CSharpSyntaxNode node,
hasErrors = true; hasErrors = true;
} }
bool? knownMatchResult = null; return new BoundConstantPattern(node, expression, constantValueOpt, hasErrors);
if (constantValueOpt != null)
{
if (constantValueOpt.IsNull)
{
knownMatchResult = operandIsNull;
}
else if (operand.ConstantValue != null)
{
knownMatchResult = constantValueOpt.Equals(operand.ConstantValue);
}
}
return new BoundConstantPattern(node, expression, constantValueOpt, knownMatchResult, hasErrors);
} }
internal BoundExpression ConvertPatternExpression(TypeSymbol leftType, CSharpSyntaxNode node, BoundExpression expression, ref ConstantValue constantValue, DiagnosticBag diagnostics) internal BoundExpression ConvertPatternExpression(TypeSymbol leftType, CSharpSyntaxNode node, BoundExpression expression, ref ConstantValue constantValue, DiagnosticBag diagnostics)
...@@ -357,8 +344,7 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol leftType, CSharpSyn ...@@ -357,8 +344,7 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol leftType, CSharpSyn
} }
DeclareLocalVariable(localSymbol, identifier, declType); DeclareLocalVariable(localSymbol, identifier, declType);
bool? knownMatchResult = null; // PROTOTYPE(patterns): TODO: compute if the declaration pattern is irrefutable return new BoundDeclarationPattern(node, localSymbol, boundDeclType, isVar, hasErrors);
return new BoundDeclarationPattern(node, localSymbol, boundDeclType, isVar, knownMatchResult, hasErrors);
} }
} }
} }
...@@ -32,7 +32,7 @@ internal static SwitchBinder Create(Binder next, SwitchStatementSyntax switchSyn ...@@ -32,7 +32,7 @@ internal static SwitchBinder Create(Binder next, SwitchStatementSyntax switchSyn
// compatible with the existing syntax and semantics, we will remove *this* binder // compatible with the existing syntax and semantics, we will remove *this* binder
// and use the new one for binding all switch statements. // and use the new one for binding all switch statements.
return return
((switchSyntax?.SyntaxTree?.Options as CSharpParseOptions)?.IsFeatureEnabled(MessageID.IDS_FeaturePatternMatching) != false) true || ((switchSyntax?.SyntaxTree?.Options as CSharpParseOptions)?.IsFeatureEnabled(MessageID.IDS_FeaturePatternMatching) != false)
? new PatternSwitchBinder(next, switchSyntax) ? new PatternSwitchBinder(next, switchSyntax)
: new SwitchBinder(next, switchSyntax); : new SwitchBinder(next, switchSyntax);
} }
......
...@@ -229,7 +229,7 @@ private BoundPatternSwitchSection BindPatternSwitchSection(BoundExpression bound ...@@ -229,7 +229,7 @@ private BoundPatternSwitchSection BindPatternSwitchSection(BoundExpression bound
case SyntaxKind.DefaultSwitchLabel: case SyntaxKind.DefaultSwitchLabel:
{ {
var defaultLabelSyntax = (DefaultSwitchLabelSyntax)node; var defaultLabelSyntax = (DefaultSwitchLabelSyntax)node;
var pattern = new BoundWildcardPattern(node, knownMatchResult: true); var pattern = new BoundWildcardPattern(node);
bool hasErrors = pattern.HasErrors; bool hasErrors = pattern.HasErrors;
if (defaultLabel != null) if (defaultLabel != null)
{ {
......
...@@ -1450,7 +1450,7 @@ ...@@ -1450,7 +1450,7 @@
</Node> </Node>
<AbstractNode Name="BoundPattern" Base="BoundNode"> <AbstractNode Name="BoundPattern" Base="BoundNode">
<Field Name="KnownMatchResult" Type="bool?" Null="allow"/> <!-- CONSIDER: we might benefit from recording the input (matched value) type in the bound pattern. -->
</AbstractNode> </AbstractNode>
<Node Name="BoundDeclarationPattern" Base="BoundPattern"> <Node Name="BoundDeclarationPattern" Base="BoundPattern">
......
...@@ -16,6 +16,7 @@ partial class BoundPatternSwitchStatement ...@@ -16,6 +16,7 @@ partial class BoundPatternSwitchStatement
{ {
private ImmutableArray<Diagnostic> _decisionTreeDiagnostics; private ImmutableArray<Diagnostic> _decisionTreeDiagnostics;
private DecisionTree _decisionTree; private DecisionTree _decisionTree;
private ImmutableArray<LocalSymbol> _temps;
public ImmutableArray<Diagnostic> DecisionTreeDiagnostics public ImmutableArray<Diagnostic> DecisionTreeDiagnostics
{ {
...@@ -37,6 +38,67 @@ public DecisionTree DecisionTree ...@@ -37,6 +38,67 @@ public DecisionTree DecisionTree
} }
} }
public ImmutableArray<LocalSymbol> Temps
{
get
{
EnsureDecisionTree();
if (_temps.IsDefault)
{
ImmutableInterlocked.InterlockedInitialize(ref _temps, ComputeTemps(_decisionTree));
}
Debug.Assert(!_temps.IsDefault);
return _temps;
}
}
/// <summary>
/// Compute the set of temps needed for the whole decision tree.
/// </summary>
private ImmutableArray<LocalSymbol> ComputeTemps(DecisionTree decisionTree)
{
var builder = ArrayBuilder<LocalSymbol>.GetInstance();
AddTemps(decisionTree, builder);
return builder.ToImmutableAndFree();
}
private void AddTemps(DecisionTree decisionTree, ArrayBuilder<LocalSymbol> builder)
{
if (decisionTree == null) return;
switch (decisionTree.Kind)
{
case DecisionTree.DecisionKind.ByType:
{
var byType = (DecisionTree.ByType)decisionTree;
AddTemps(byType.WhenNull, builder);
foreach (var td in byType.TypeAndDecision)
{
AddTemps(td.Value, builder);
}
AddTemps(byType.Default, builder);
return;
}
case DecisionTree.DecisionKind.ByValue:
{
var byValue = (DecisionTree.ByValue)decisionTree;
foreach (var vd in byValue.ValueAndDecision)
{
AddTemps(vd.Value, builder);
}
AddTemps(byValue.Default, builder);
return;
}
case DecisionTree.DecisionKind.Guarded:
{
var guarded = (DecisionTree.Guarded)decisionTree;
ComputeTemps(guarded.Default);
return;
}
default:
throw ExceptionUtilities.UnexpectedValue(decisionTree.Kind);
}
}
public LabelSymbol ConstantTargetOpt public LabelSymbol ConstantTargetOpt
{ {
get get
...@@ -82,7 +144,7 @@ private LabelSymbol BindConstantTarget(DecisionTree decisionTree) ...@@ -82,7 +144,7 @@ private LabelSymbol BindConstantTarget(DecisionTree decisionTree)
{ {
var type = td.Key; var type = td.Key;
var decision = td.Value; var decision = td.Value;
switch (Binder.Conversions.ExpressionOfTypeMatchesPatternType(expression.Type, type)) switch (Binder.Conversions.ExpressionOfTypeMatchesPatternType(expression.Type.TupleUnderlyingTypeOrSelf(), type))
{ {
case null: case null:
return null; // we don't know if this matches the input return null; // we don't know if this matches the input
...@@ -350,7 +412,7 @@ private ErrorCode CheckSubsumed(BoundPattern pattern, DecisionTree decisionTree, ...@@ -350,7 +412,7 @@ private ErrorCode CheckSubsumed(BoundPattern pattern, DecisionTree decisionTree,
{ {
var type = td.Key; var type = td.Key;
var decision = td.Value; var decision = td.Value;
if (_conversions.ExpressionOfTypeMatchesPatternType(declarationPattern.DeclaredType.Type, type) == true) if (_conversions.ExpressionOfTypeMatchesPatternType(declarationPattern.DeclaredType.Type.TupleUnderlyingTypeOrSelf(), type) == true)
{ {
var error = CheckSubsumed(pattern, decision, false); var error = CheckSubsumed(pattern, decision, false);
if (error != 0) return error; if (error != 0) return error;
...@@ -533,7 +595,7 @@ private DecisionTree AddByValue(DecisionTree.ByType byType, BoundExpression valu ...@@ -533,7 +595,7 @@ private DecisionTree AddByValue(DecisionTree.ByType byType, BoundExpression valu
var kvp = byType.TypeAndDecision[i]; var kvp = byType.TypeAndDecision[i];
var matchedType = kvp.Key; var matchedType = kvp.Key;
var decision = kvp.Value; var decision = kvp.Value;
if (matchedType == value.Type) if (matchedType.TupleUnderlyingTypeOrSelf() == value.Type.TupleUnderlyingTypeOrSelf())
{ {
forType = decision; forType = decision;
break; break;
...@@ -546,9 +608,10 @@ private DecisionTree AddByValue(DecisionTree.ByType byType, BoundExpression valu ...@@ -546,9 +608,10 @@ private DecisionTree AddByValue(DecisionTree.ByType byType, BoundExpression valu
if (forType == null) if (forType == null)
{ {
// PROTOTYPE(patterns): the expression should be a new temp when value.Type != byType.Expression.Type var type = value.Type;
var narrowedExpression = byType.Expression; var localSymbol = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatchingTemp, _syntax, false, RefKind.None);
forType = new DecisionTree.ByValue(narrowedExpression, value.Type); var narrowedExpression = new BoundLocal(_syntax, localSymbol, null, type);
forType = new DecisionTree.ByValue(narrowedExpression, value.Type.TupleUnderlyingTypeOrSelf());
forType.Parent = byType; forType.Parent = byType;
byType.TypeAndDecision.Add(new KeyValuePair<TypeSymbol, DecisionTree>(value.Type, forType)); byType.TypeAndDecision.Add(new KeyValuePair<TypeSymbol, DecisionTree>(value.Type, forType));
} }
...@@ -634,6 +697,7 @@ private DecisionTree AddByType(DecisionTree.ByType byType, TypeSymbol type, Deci ...@@ -634,6 +697,7 @@ private DecisionTree AddByType(DecisionTree.ByType byType, TypeSymbol type, Deci
var result = makeDecision(Expression, type); var result = makeDecision(Expression, type);
result.Parent = byType; result.Parent = byType;
byType.TypeAndDecision.Add(new KeyValuePair<TypeSymbol, DecisionTree>(type, result)); byType.TypeAndDecision.Add(new KeyValuePair<TypeSymbol, DecisionTree>(type, result));
byType.Temps.Add(localSymbol);
if (_conversions.ExpressionOfTypeMatchesPatternType(byType.Type, type) == true && result.MatchIsComplete && byType.WhenNull?.MatchIsComplete == true) if (_conversions.ExpressionOfTypeMatchesPatternType(byType.Type, type) == true && result.MatchIsComplete && byType.WhenNull?.MatchIsComplete == true)
{ {
byType.MatchIsComplete = true; byType.MatchIsComplete = true;
...@@ -694,7 +758,7 @@ private DecisionTree AddByNull(DecisionTree.ByType byType, DecisionMaker makeDec ...@@ -694,7 +758,7 @@ private DecisionTree AddByNull(DecisionTree.ByType byType, DecisionMaker makeDec
private bool NonNullHandled(DecisionTree.ByType byType) private bool NonNullHandled(DecisionTree.ByType byType)
{ {
var inputType = byType.Type.StrippedType(); var inputType = byType.Type.StrippedType().TupleUnderlyingTypeOrSelf();
foreach (var td in byType.TypeAndDecision) foreach (var td in byType.TypeAndDecision)
{ {
var type = td.Key; var type = td.Key;
...@@ -874,6 +938,8 @@ public class ByType : DecisionTree ...@@ -874,6 +938,8 @@ public class ByType : DecisionTree
public DecisionTree WhenNull; public DecisionTree WhenNull;
public readonly ArrayBuilder<KeyValuePair<TypeSymbol, DecisionTree>> TypeAndDecision = public readonly ArrayBuilder<KeyValuePair<TypeSymbol, DecisionTree>> TypeAndDecision =
new ArrayBuilder<KeyValuePair<TypeSymbol, DecisionTree>>(); new ArrayBuilder<KeyValuePair<TypeSymbol, DecisionTree>>();
public readonly ArrayBuilder<LocalSymbol> Temps =
new ArrayBuilder<LocalSymbol>();
public DecisionTree Default; public DecisionTree Default;
public override DecisionKind Kind => DecisionKind.ByType; public override DecisionKind Kind => DecisionKind.ByType;
public ByType(BoundExpression expression, TypeSymbol type) : base(expression, type) { } public ByType(BoundExpression expression, TypeSymbol type) : base(expression, type) { }
......
...@@ -421,6 +421,7 @@ ...@@ -421,6 +421,7 @@
<Compile Include="Lowering\LocalRewriter\LocalRewriter_IndexerAccess.cs" /> <Compile Include="Lowering\LocalRewriter\LocalRewriter_IndexerAccess.cs" />
<Compile Include="Lowering\LocalRewriter\LocalRewriter_IsOperator.cs" /> <Compile Include="Lowering\LocalRewriter\LocalRewriter_IsOperator.cs" />
<Compile Include="Lowering\LocalRewriter\LocalRewriter_LabeledStatement.cs" /> <Compile Include="Lowering\LocalRewriter\LocalRewriter_LabeledStatement.cs" />
<Compile Include="Lowering\LocalRewriter\LocalRewriter_PatternSwitchStatement.cs" />
<Compile Include="Lowering\LocalRewriter\LocalRewriter_TupleCreationExpression.cs" /> <Compile Include="Lowering\LocalRewriter\LocalRewriter_TupleCreationExpression.cs" />
<Compile Include="Lowering\LocalRewriter\LocalRewriter_Literal.cs" /> <Compile Include="Lowering\LocalRewriter\LocalRewriter_Literal.cs" />
<Compile Include="Lowering\LocalRewriter\LocalRewriter_LocalDeclaration.cs" /> <Compile Include="Lowering\LocalRewriter\LocalRewriter_LocalDeclaration.cs" />
......
...@@ -1712,11 +1712,11 @@ private MethodSymbol GetNullableMethod(CSharpSyntaxNode syntax, TypeSymbol nulla ...@@ -1712,11 +1712,11 @@ private MethodSymbol GetNullableMethod(CSharpSyntaxNode syntax, TypeSymbol nulla
{ {
// This handles the case where we have a nullable user-defined struct type compared against null, eg: // This handles the case where we have a nullable user-defined struct type compared against null, eg:
// //
// struct S {} ... S? s = whatever; if (s != null) // struct S {} ... S? s = whatever; if (s != null)
// //
// If S does not define an overloaded != operator then this is lowered to s.HasValue. // If S does not define an overloaded != operator then this is lowered to s.HasValue.
// //
// If the type already has a user-defined or built-in operator then comparing to null is // If the type already has a user-defined or built-in operator then comparing to null is
// treated as a lifted equality operator. // treated as a lifted equality operator.
Debug.Assert(loweredLeft != null); Debug.Assert(loweredLeft != null);
...@@ -1750,7 +1750,7 @@ private MethodSymbol GetNullableMethod(CSharpSyntaxNode syntax, TypeSymbol nulla ...@@ -1750,7 +1750,7 @@ private MethodSymbol GetNullableMethod(CSharpSyntaxNode syntax, TypeSymbol nulla
type: returnType); type: returnType);
} }
// arr?.Length == null // arr?.Length == null
var conditionalAccess = nullable as BoundLoweredConditionalAccess; var conditionalAccess = nullable as BoundLoweredConditionalAccess;
if (conditionalAccess != null && if (conditionalAccess != null &&
(conditionalAccess.WhenNullOpt == null || conditionalAccess.WhenNullOpt.IsDefaultValue())) (conditionalAccess.WhenNullOpt == null || conditionalAccess.WhenNullOpt.IsDefaultValue()))
......
...@@ -11,8 +11,11 @@ namespace Microsoft.CodeAnalysis.CSharp ...@@ -11,8 +11,11 @@ namespace Microsoft.CodeAnalysis.CSharp
{ {
internal partial class LocalRewriter internal partial class LocalRewriter
{ {
// Rewriting for pattern-matching switch statements. // Rewriting for pattern-matching switch statements.
public override BoundNode VisitPatternSwitchStatement(BoundPatternSwitchStatement node) // This is a temporary translation into a series of if-then-else statements.
// Ultimately it will be replaced by a translation based on the decision tree.
private BoundNode VisitPatternSwitchStatement_Ifchain(BoundPatternSwitchStatement node)
{ {
var statements = ArrayBuilder<BoundStatement>.GetInstance(); var statements = ArrayBuilder<BoundStatement>.GetInstance();
......
// 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.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.Collections.Generic;
using System;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
{
internal partial class LocalRewriter
{
// PROTOTYPE(typeswitch): as a temporary hack while this code is in development, we
// only use the new translation machinery when this bool is set to true. If it is false
// then we use the transitional code which translates a pattern switch into a series of
// if-then-else statements. Ultimately we need the new translation to be used to generate
// switch IL instructions for ordinary old-style switch statements.
private static bool useNewTranslation = false;
public override BoundNode VisitPatternSwitchStatement(BoundPatternSwitchStatement node)
{
// Until this is all implemented, we use a dumb series of if-then-else
// statements to translate the switch statement.
if (!useNewTranslation) return VisitPatternSwitchStatement_Ifchain(node);
var usedLabels = new HashSet<LabelSymbol>();
var usedTemps = new HashSet<LocalSymbol>(); // PROTOTYPE(typeswitch): worry about deterministic ordering
var result = ArrayBuilder<BoundStatement>.GetInstance();
// PROTOTYPE(typeswitch): do we need to do anything with node.ConstantTargetOpt, given that we
// have the decision tree? If not, can we remove it from the bound trees?
if (node.DecisionTree.Expression != node.Expression)
{
// Store the input expression into a temp
// PROTOTYPE(typeswitch): do we need to add the temp to the list of used temps?
result.Add(_factory.Assignment(node.DecisionTree.Expression, node.Expression));
}
// output the decision tree part
LowerDecisionTree(node.DecisionTree, usedLabels, usedTemps, result);
result.Add(_factory.Goto(node.BreakLabel));
// output the sections of code (that were reachable)
foreach (var section in node.SwitchSections)
{
ArrayBuilder<BoundStatement> sectionBuilder = null;
foreach (var label in section.SwitchLabels)
{
if (usedLabels.Contains(label.Label))
{
if (sectionBuilder == null)
{
sectionBuilder = ArrayBuilder<BoundStatement>.GetInstance();
}
sectionBuilder.Add(_factory.Label(label.Label));
}
}
if (sectionBuilder != null)
{
sectionBuilder.AddRange(VisitList(section.Statements));
sectionBuilder.Add(_factory.Goto(node.BreakLabel));
result.Add(_factory.Block(section.Locals, sectionBuilder.ToImmutableAndFree()));
}
}
result.Add(_factory.Label(node.BreakLabel));
return _factory.Block(usedTemps.ToImmutableArray().Concat(node.InnerLocals), node.InnerLocalFunctions, result.ToImmutableAndFree());
}
private void LowerDecisionTree(DecisionTree decisionTree, HashSet<LabelSymbol> usedLabels, HashSet<LocalSymbol> usedTemps, ArrayBuilder<BoundStatement> result)
{
if (decisionTree == null) return;
switch (decisionTree.Kind)
{
case DecisionTree.DecisionKind.ByType:
{
LowerDecisionTree((DecisionTree.ByType)decisionTree, usedLabels, usedTemps, result);
return;
}
case DecisionTree.DecisionKind.ByValue:
{
LowerDecisionTree((DecisionTree.ByValue)decisionTree, usedLabels, usedTemps, result);
return;
}
case DecisionTree.DecisionKind.Guarded:
{
LowerDecisionTree((DecisionTree.Guarded)decisionTree, usedLabels, usedTemps, result);
return;
}
default:
throw ExceptionUtilities.UnexpectedValue(decisionTree.Kind);
}
}
private void LowerDecisionTree(DecisionTree.ByType byType, HashSet<LabelSymbol> usedLabels, HashSet<LocalSymbol> usedTemps, ArrayBuilder<BoundStatement> result)
{
var defaultLabel = _factory.GenerateLabel("byTypeDefault");
if (byType.Type.CanBeAssignedNull())
{
var notNullLabel = _factory.GenerateLabel("notNull");
var inputExpression = byType.Expression;
var nullValue = _factory.NullOrDefault(byType.Type);
BoundExpression notNull = byType.Type.IsNullableType()
? this.RewriteNullableNullEquality(_factory.Syntax, BinaryOperatorKind.NullableNullNotEqual, byType.Expression, nullValue, _factory.SpecialType(SpecialType.System_Boolean))
: _factory.ObjectNotEqual(byType.Expression, nullValue);
result.Add(_factory.If(notNull, _factory.Goto(notNullLabel)));
LowerDecisionTree(byType.WhenNull, usedLabels, usedTemps, result);
result.Add(_factory.Goto(defaultLabel));
result.Add(_factory.Label(notNullLabel));
}
else
{
Debug.Assert(byType.WhenNull == null);
}
foreach (var td in byType.TypeAndDecision)
{
var type = td.Key;
var decision = td.Value;
var failLabel = _factory.GenerateLabel("failedDecision");
grep("if there is a fresh temp needed, copy to it and add the temp from ", decision.Expression, " to ", usedTemps);
grep("test for the type ", type, " into that temp and goto failLabel when that fails");
LowerDecisionTree(decision, usedLabels, usedTemps, result);
result.Add(_factory.Label(failLabel));
}
result.Add(_factory.Label(defaultLabel));
LowerDecisionTree(byType.Default, usedLabels, usedTemps, result);
}
private void LowerDecisionTree(DecisionTree.ByValue byValue, HashSet<LabelSymbol> usedLabels, HashSet<LocalSymbol> usedTemps, ArrayBuilder<BoundStatement> result)
{
throw new NotImplementedException();
}
private void LowerDecisionTree(DecisionTree.Guarded guarded, HashSet<LabelSymbol> usedLabels, HashSet<LocalSymbol> usedTemps, ArrayBuilder<BoundStatement> result)
{
usedLabels.Add(guarded.Label.Label);
BoundStatement stmt = _factory.Goto(guarded.Label.Label);
if (guarded.Guard != null)
{
stmt = _factory.If(VisitExpression(guarded.Guard), stmt);
}
result.Add(stmt);
}
private dynamic grep(params object[] args)
{
throw null;
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册