/// <summary> /// Ensures that the variable is of the given type(s) in the positive branch or not of this type in the negative /// branch. If the current branch is unfeasible, assigns an appropriate boolean to the /// <see cref="BoundExpression.ConstantValue"/> of <paramref name="checkExpr"/>. /// </summary> /// <param name="varRef">The reference to the variable whose types to check.</param> /// <param name="targetTypeCallback">The callback that receives the current type mask of the variable and returns /// the target one.</param> /// <param name="branch">The branch to check - <see cref="ConditionBranch.ToTrue"/> is understood as the positive /// branch if <paramref name="isPositiveCheck"/> is true.</param> /// <param name="flowState">The flow state of the branch.</param> /// <param name="skipPositiveIfAnyType">Whether to skip a mask with <see cref="TypeRefMask.IsAnyType"/> in the /// positive branch (in the negative branch, it is always skipped).</param> /// <param name="checkExpr">The expression to have its <see cref="BoundExpression.ConstantValue"/> potentially /// updated.</param> /// <param name="isPositiveCheck">Whether the expression returns true when the type check succeeds. For example, /// in the case of != it would be false.</param> public static void HandleTypeCheckingExpression( BoundVariableRef varRef, Func <TypeRefMask, TypeRefMask> targetTypeCallback, ConditionBranch branch, FlowState flowState, bool skipPositiveIfAnyType = false, BoundExpression checkExpr = null, bool isPositiveCheck = true) { if (!TryGetVariableHandle(varRef.Variable, flowState, out VariableHandle handle)) { return; } var currentType = flowState.GetLocalType(handle); var targetType = targetTypeCallback(currentType); // Model negative type checks (such as $x != null) by inverting branches for the core checking function var branchHlp = isPositiveCheck ? branch : branch.NegativeBranch(); bool isFeasible = HandleTypeChecking(currentType, targetType, branchHlp, flowState, handle, skipPositiveIfAnyType); // If the constant value was not meant to be updated, skip its computation if (checkExpr == null) { return; } if (!currentType.IsRef) { // If the true branch proves to be unfeasible, the function always returns false and vice versa var resultConstVal = isFeasible ? default(Optional <object>) : new Optional <object>(!branch.TargetValue().Value); // Each branch can clean only the constant value it produced during its analysis (in order not to lose result // of the other branch): true branch can produce false value and vice versa if (!resultConstVal.EqualsOptional(checkExpr.ConstantValue) && (resultConstVal.HasValue || checkExpr.ConstantValue.Value is false && branch == ConditionBranch.ToTrue || checkExpr.ConstantValue.Value is true && branch == ConditionBranch.ToFalse)) { checkExpr.ConstantValue = resultConstVal; } } else { // We cannot reason about the result of the check if the variable can be modified by reference checkExpr.ConstantValue = default(Optional <object>); } }