/// <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); } }
/// <summary> /// Lower a test followed by an evaluation into a side-effect followed by a test. This permits us to optimize /// a type test followed by a cast into an `as` expression followed by a null check. Returns true if the optimization /// applies and the results are placed into <paramref name="sideEffect"/> and <paramref name="test"/>. The caller /// should place the side-effect before the test in the generated code. /// </summary> /// <param name="evaluation"></param> /// <param name="test"></param> /// <param name="sideEffect"></param> /// <param name="testExpression"></param> /// <returns>true if the optimization is applied</returns> protected bool TryLowerTypeTestAndCast( BoundDagTest test, BoundDagEvaluation evaluation, out BoundExpression sideEffect, out BoundExpression testExpression) { HashSet <DiagnosticInfo> useSiteDiagnostics = null; // case 1: type test followed by cast to that type if (test is BoundDagTypeTest typeDecision && evaluation is BoundDagTypeEvaluation typeEvaluation1 && typeDecision.Type.IsReferenceType && typeEvaluation1.Type.Equals(typeDecision.Type, TypeCompareKind.AllIgnoreOptions) && typeEvaluation1.Input == typeDecision.Input) { BoundExpression input = _tempAllocator.GetTemp(test.Input); BoundExpression output = _tempAllocator.GetTemp(new BoundDagTemp(evaluation.Syntax, typeEvaluation1.Type, evaluation)); sideEffect = _factory.AssignmentExpression(output, _factory.As(input, typeEvaluation1.Type)); testExpression = _factory.ObjectNotEqual(output, _factory.Null(output.Type)); return(true); } // case 2: null check followed by cast to a base type if (test is BoundDagNonNullTest nonNullTest && evaluation is BoundDagTypeEvaluation typeEvaluation2 && _factory.Compilation.Conversions.ClassifyBuiltInConversion(test.Input.Type, typeEvaluation2.Type, ref useSiteDiagnostics) is Conversion conv && (conv.IsIdentity || conv.Kind == ConversionKind.ImplicitReference || conv.IsBoxing) && typeEvaluation2.Input == nonNullTest.Input) { BoundExpression input = _tempAllocator.GetTemp(test.Input); var baseType = typeEvaluation2.Type; BoundExpression output = _tempAllocator.GetTemp(new BoundDagTemp(evaluation.Syntax, baseType, evaluation)); sideEffect = _factory.AssignmentExpression(output, _factory.Convert(baseType, input)); testExpression = _factory.ObjectNotEqual(output, _factory.Null(baseType)); _localRewriter._diagnostics.Add(test.Syntax, useSiteDiagnostics); return(true); } sideEffect = testExpression = null; return(false); }
/// <summary> /// Lower a test followed by an evaluation into a side-effect followed by a test. This permits us to optimize /// a type test followed by a cast into an `as` expression followed by a null check. Returns true if the optimization /// applies and the results are placed into <paramref name="sideEffect"/> and <paramref name="test"/>. The caller /// should place the side-effect before the test in the generated code. /// </summary> /// <param name="evaluation"></param> /// <param name="test"></param> /// <param name="sideEffect"></param> /// <param name="testExpression"></param> /// <returns>true if the optimization is applied</returns> protected bool TryLowerTypeTestAndCast( BoundDagTest test, BoundDagEvaluation evaluation, out BoundExpression sideEffect, out BoundExpression testExpression) { if (test is BoundDagTypeTest typeDecision && evaluation is BoundDagTypeEvaluation typeEvaluation && typeDecision.Type.IsReferenceType && typeEvaluation.Type.Equals(typeDecision.Type, TypeCompareKind.AllIgnoreOptions) && typeEvaluation.Input == typeDecision.Input ) { BoundExpression input = _tempAllocator.GetTemp(test.Input); BoundExpression output = _tempAllocator.GetTemp(new BoundDagTemp(evaluation.Syntax, typeEvaluation.Type, evaluation, index: 0)); sideEffect = _factory.AssignmentExpression(output, _factory.As(input, typeEvaluation.Type)); testExpression = _factory.ObjectNotEqual(output, _factory.Null(output.Type)); return(true); } sideEffect = testExpression = null; return(false); }
/// <summary> /// Translate the single test into _sideEffectBuilder and _conjunctBuilder. /// </summary> private void LowerOneTest(BoundDagTest test) { _factory.Syntax = test.Syntax; switch (test) { case BoundDagEvaluation eval: { var sideEffect = LowerEvaluation(eval); _sideEffectBuilder.Add(sideEffect); return; } case var _: { var testExpression = LowerTest(test); if (testExpression != null) { AddConjunct(testExpression); } return; } } }