/// <summary> /// Return the boolean expression to be evaluated for the given test. Returns `null` if the test is trivially true. /// </summary> protected BoundExpression LowerTest(BoundDagTest test) { _factory.Syntax = test.Syntax; BoundExpression input = _tempAllocator.GetTemp(test.Input); switch (test) { case BoundDagNonNullTest d: return(_localRewriter.MakeNullCheck(d.Syntax, input, input.Type.IsNullableType() ? BinaryOperatorKind.NullableNullNotEqual : BinaryOperatorKind.NotEqual)); case BoundDagTypeTest d: // Note that this tests for non-null as a side-effect. We depend on that to sometimes avoid the null check. return(_factory.Is(input, d.Type)); case BoundDagNullTest d: return(_localRewriter.MakeNullCheck(d.Syntax, input, input.Type.IsNullableType() ? BinaryOperatorKind.NullableNullEqual : BinaryOperatorKind.Equal)); case BoundDagValueTest d: Debug.Assert(!input.Type.IsNullableType()); return(MakeEqual(_localRewriter.MakeLiteral(d.Syntax, d.Value, input.Type), input)); default: throw ExceptionUtilities.UnexpectedValue(test); } }
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 in the generated code in builder should not be reachable. } Debug.Assert(nextLabel != null); builder.Add(_factory.Label(nextLabel)); builder.Add(_factory.Label(noValueMatches)); return(_factory.Block(builder.ToImmutableAndFree())); }