提交 b7c20b22 编写于 作者: J Jonathon Marolf

Merge remote-tracking branch 'upstream/dev15-preview-4' into master

# Conflicts:
#	src/Tools/SignRoslyn/BinaryData.json
......@@ -236,44 +236,13 @@ private void LowerDecisionTree(DecisionTree.ByValue byValue)
return;
}
switch (byValue.Type.SpecialType)
if (byValue.Type.SpecialType == SpecialType.System_Boolean)
{
case SpecialType.System_Byte:
case SpecialType.System_Char:
case SpecialType.System_Int16:
case SpecialType.System_Int32:
case SpecialType.System_Int64:
case SpecialType.System_SByte:
case SpecialType.System_UInt16:
case SpecialType.System_UInt32:
case SpecialType.System_UInt64:
case SpecialType.System_String: // switch on a string
// switch on an integral or string type
LowerBasicSwitch(byValue);
return;
case SpecialType.System_Boolean: // switch on a boolean
LowerBooleanSwitch(byValue);
return;
// switch on a type requiring sequential comparisons. Note that we use constant.Equals(value), depending if
// possible on the one from IEquatable<T>. If that does not exist, we use instance method object.Equals(object)
// with the (now boxed) constant on the left.
case SpecialType.System_Decimal:
case SpecialType.System_Double:
case SpecialType.System_Single:
LowerOtherSwitch(byValue);
return;
default:
if (byValue.Type.TypeKind == TypeKind.Enum)
{
LowerBasicSwitch(byValue);
return;
}
// There are no other types of constants that could be used as patterns.
throw ExceptionUtilities.UnexpectedValue(byValue.Type);
LowerBooleanSwitch(byValue);
}
else
{
LowerBasicSwitch(byValue);
}
}
......@@ -392,26 +361,120 @@ private void LowerBasicSwitch(DecisionTree.ByValue byValue)
var rewrittenSections = switchSections.ToImmutableAndFree();
MethodSymbol stringEquality = null;
if (byValue.Type.SpecialType == SpecialType.System_String)
if (underlyingSwitchType.SpecialType == SpecialType.System_String)
{
LocalRewriter.EnsureStringHashFunction(rewrittenSections, _factory.Syntax);
stringEquality = LocalRewriter.GetSpecialTypeMethod(_factory.Syntax, SpecialMember.System_String__op_Equality);
}
// Emit requires a constant target when there are no sections, so we accomodate that here.
// CONSIDER: can we get better code generated by giving a constant target more often here,
// e.g. when the switch expression is a constant?
// The BoundSwitchStatement requires a constant target when there are no sections, so we accomodate that here.
var constantTarget = rewrittenSections.IsEmpty ? noValueMatches : null;
var switchStatement = new BoundSwitchStatement(
_factory.Syntax, null, _factory.Convert(underlyingSwitchType, byValue.Expression),
constantTarget,
ImmutableArray<LocalSymbol>.Empty, ImmutableArray<LocalFunctionSymbol>.Empty,
rewrittenSections, noValueMatches, stringEquality);
_loweredDecisionTree.Add(switchStatement);
// The bound switch statement implicitly defines the label noValueMatches at the end, so we do not add it explicitly.
switch (underlyingSwitchType.SpecialType)
{
case SpecialType.System_Boolean:
// boolean switch is handled in LowerBooleanSwitch, not here.
throw ExceptionUtilities.Unreachable;
case SpecialType.System_String:
case SpecialType.System_Byte:
case SpecialType.System_Char:
case SpecialType.System_Int16:
case SpecialType.System_Int32:
case SpecialType.System_Int64:
case SpecialType.System_SByte:
case SpecialType.System_UInt16:
case SpecialType.System_UInt32:
case SpecialType.System_UInt64:
{
// emit knows how to efficiently generate code for these kinds of switches.
_loweredDecisionTree.Add(switchStatement);
break;
}
default:
{
// other types, such as float, double, and decimal, are not currently
// handled by emit and must be lowered here.
_loweredDecisionTree.Add(LowerNonprimitiveSwitch(switchStatement));
break;
}
}
LowerDecisionTree(byValue.Expression, byValue.Default);
}
private BoundStatement LowerNonprimitiveSwitch(BoundSwitchStatement switchStatement)
{
// Here we handle "other" types, such as float, double, and decimal, by
// lowering the BoundSwitchStatement.
// We compare the constant values using value.Equals(input), using ordinary
// overload resolution. Note that we cannot and do not rely on switching
// on the hash code, as it may not be consistent with the behavior of Equals;
// see https://github.com/dotnet/coreclr/issues/6237. Also, the hash code is
// not guaranteed to be the same on the compilation platform as the runtime
// platform.
// CONSIDER: can we improve the quality of the code using comparisons, like
// we do for other numeric types, by generating a series of tests
// that use divide-and-conquer to efficiently find a matching value?
// If so, we should use the BoundSwitchStatement and do that in emit.
// Moreover, we should be able to use `==` rather than `.Equals`
// for cases (such as non-NaN) where we know the result to be the same.
var rewrittenSections = switchStatement.SwitchSections;
var expression = switchStatement.Expression;
var noValueMatches = switchStatement.BreakLabel;
Debug.Assert(switchStatement.LoweredPreambleOpt == null);
Debug.Assert(switchStatement.InnerLocals.IsDefaultOrEmpty);
Debug.Assert(switchStatement.InnerLocalFunctions.IsDefaultOrEmpty);
Debug.Assert(switchStatement.StringEquality == null);
LabelSymbol nextLabel = null;
var builder = ArrayBuilder<BoundStatement>.GetInstance();
foreach (var section in rewrittenSections)
{
foreach (var boundSwitchLabel in section.SwitchLabels)
{
if (nextLabel != null)
{
builder.Add(_factory.Label(nextLabel));
}
nextLabel = _factory.GenerateLabel("failcase+" + section.SwitchLabels[0].ConstantValueOpt.Value);
Debug.Assert(boundSwitchLabel.ConstantValueOpt != null);
// generate (if (value.Equals(input)) goto label;
var literal = LocalRewriter.MakeLiteral(_factory.Syntax, boundSwitchLabel.ConstantValueOpt, expression.Type);
var condition = _factory.InstanceCall(literal, "Equals", expression);
if (!condition.HasErrors && condition.Type.SpecialType != SpecialType.System_Boolean)
{
var call = (BoundCall)condition;
// '{1} {0}' has the wrong return type
_factory.Diagnostics.Add(ErrorCode.ERR_BadRetType, boundSwitchLabel.Syntax.GetLocation(), call.Method, call.Type);
}
builder.Add(_factory.ConditionalGoto(condition, boundSwitchLabel.Label, true));
builder.Add(_factory.Goto(nextLabel));
}
foreach (var boundSwitchLabel in section.SwitchLabels)
{
builder.Add(_factory.Label(boundSwitchLabel.Label));
}
builder.Add(_factory.Block(section.Statements));
// this location should not be reachable.
}
Debug.Assert(nextLabel != null);
builder.Add(_factory.Label(nextLabel));
builder.Add(_factory.Label(noValueMatches));
return _factory.Block(builder.ToImmutableAndFree());
}
private void LowerBooleanSwitch(DecisionTree.ByValue byValue)
{
switch (byValue.ValueAndDecision.Count)
......@@ -462,16 +525,6 @@ private void LowerBooleanSwitch(DecisionTree.ByValue byValue)
throw ExceptionUtilities.UnexpectedValue(byValue.ValueAndDecision.Count);
}
}
/// <summary>
/// We handle "other" types, such as float, double, and decimal here. We compare the constant values using IEquatable.
/// For other value types, since there is no literal notation, there will be no constants to test.
/// </summary>
private void LowerOtherSwitch(DecisionTree.ByValue byValue)
{
this.LocalRewriter._diagnostics.Add(ErrorCode.ERR_FeatureIsUnimplemented, _factory.Syntax.GetLocation(), "switch on float, double, or decimal");
throw new NotImplementedException();
}
}
}
}
......@@ -566,6 +566,11 @@ public BoundObjectCreationExpression New(MethodSymbol ctor, params BoundExpressi
return new BoundObjectCreationExpression(Syntax, ctor, args) { WasCompilerGenerated = true };
}
public BoundExpression InstanceCall(BoundExpression receiver, string name, BoundExpression arg)
{
return MakeInvocationExpression(BinderFlags.None, this.Syntax, receiver, name, ImmutableArray.Create(arg), this.Diagnostics);
}
public BoundExpression StaticCall(TypeSymbol receiver, string name, params BoundExpression[] args)
{
return MakeInvocationExpression(BinderFlags.None, this.Syntax, this.Type(receiver), name, args.ToImmutableArray(), this.Diagnostics);
......@@ -851,6 +856,11 @@ public BoundLiteral Literal(string value)
return StringLiteral(stringConst);
}
public BoundLiteral Literal(ConstantValue value, TypeSymbol type)
{
return new CSharp.BoundLiteral(Syntax, value, type) { WasCompilerGenerated = true };
}
public BoundLiteral StringLiteral(ConstantValue stringConst)
{
Debug.Assert(stringConst.IsString || stringConst.IsNull);
......
......@@ -865,5 +865,111 @@ public static void M(object o)
sasquatch";
var comp = CompileAndVerify(compilation, expectedOutput: expectedOutput);
}
[Fact]
public void WhenClause02()
{
var source =
@"using System;
class Program
{
public static void Main()
{
M(0.0);
M(-0.0);
M(2.1);
M(1.0);
M(double.NaN);
M(-double.NaN);
M(0.0f);
M(-0.0f);
M(2.1f);
M(1.0f);
M(float.NaN);
M(-float.NaN);
M(0.0m);
M(0m);
M(2.1m);
M(1.0m);
M(null);
}
public static void M(object o)
{
switch (o)
{
case 0.0f:
Console.WriteLine(""0.0f !"");
break;
case 0.0d:
Console.WriteLine(""0.0d !"");
break;
case 0.0m:
Console.WriteLine(""0.0m !"");
break;
case 1.0f:
Console.WriteLine(""1.0f !"");
break;
case 1.0d:
Console.WriteLine(""1.0d !"");
break;
case 1.0m:
Console.WriteLine(""1.0m !"");
break;
case 2.0f:
Console.WriteLine(""2.0f !"");
break;
case 2.0d:
Console.WriteLine(""2.0d !"");
break;
case 2.0m:
Console.WriteLine(""2.0m !"");
break;
case float.NaN:
Console.WriteLine(""float.NaN !"");
break;
case double.NaN:
Console.WriteLine(""double.NaN !"");
break;
case float f when f is float g:
Console.WriteLine(""float "" + g);
break;
case double d when d is double e:
Console.WriteLine(""double "" + e);
break;
case decimal d when d is decimal e:
Console.WriteLine(""decimal "" + e);
break;
case null:
Console.WriteLine(""null"");
break;
case object k:
Console.WriteLine(k.GetType() + "" + "" + k);
break;
}
}
}";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: s_patternParseOptions);
compilation.VerifyDiagnostics();
var expectedOutput =
@"0.0d !
0.0d !
double 2.1
1.0d !
double.NaN !
double.NaN !
0.0f !
0.0f !
float 2.1
1.0f !
float.NaN !
float.NaN !
0.0m !
0.0m !
decimal 2.1
1.0m !
null";
var comp = CompileAndVerify(compilation, expectedOutput: expectedOutput);
}
}
}
......@@ -49,6 +49,8 @@
"Roslyn.Hosting.Diagnostics.dll",
"Roslyn.VisualStudio.DiagnosticsWindow.dll",
"Roslyn.VisualStudio.InteractiveComponents.dll",
"Roslyn.VisualStudio.Setup.Interactive.dll",
"Roslyn.VisualStudio.Test.Utilities.dll",
"SDK\\Roslyn.SyntaxVisualizer.DgmlHelper.dll",
"SDK\\Roslyn.SyntaxVisualizer.Control.dll",
"SDK\\Roslyn.SyntaxVisualizer.Extension.dll",
......@@ -93,7 +95,6 @@
"Roslyn.Deployment.Full.Next.vsix",
"Roslyn.VisualStudio.DiagnosticsWindow.vsix",
"Roslyn.VisualStudio.InteractiveComponents.vsix",
"Roslyn.VisualStudio.Setup.Interactive.dll",
"Roslyn.VisualStudio.Setup.Interactive.vsix",
"Roslyn.VisualStudio.Setup.Next.vsix",
"Roslyn.VisualStudio.Setup.vsix",
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册