public static IEnumerable <BitSet> Choose(this BitSet b, int choose) { if (choose == 0) { // Choosing zero elements from any set gives the empty set. yield return(BitSet.Empty); } else if (b.Any()) { // We are choosing at least one element from a set that has // a first element. Get the first element, and the set // lacking the first element. int first = b.First(); BitSet rest = b.Remove(first); // These are the permutations that contain the first element: foreach (BitSet r in rest.Choose(choose - 1)) { yield return(r.Add(first)); } // These are the permutations that do not contain the first element: foreach (BitSet r in rest.Choose(choose)) { yield return(r); } } }
/// <summary> /// Recursive function that lifts the specified instruction. /// The input instruction is expected to a subexpression of the trueInst /// (so that all nullableVars are guaranteed non-null within this expression). /// /// Creates a new lifted instruction without modifying the input instruction. /// On success, returns (new lifted instruction, bitset). /// If lifting fails, returns (null, null). /// /// The returned bitset specifies which nullableVars were considered "relevant" for this instruction. /// bitSet[i] == true means nullableVars[i] was relevant. /// /// The new lifted instruction will have equivalent semantics to the input instruction /// if all relevant variables are non-null [except that the result will be wrapped in a Nullable{T} struct]. /// If any relevant variable is null, the new instruction is guaranteed to evaluate to <c>null</c> /// without having any other effect. /// </summary> (ILInstruction, BitSet) DoLift(ILInstruction inst) { if (MatchGetValueOrDefault(inst, out ILVariable inputVar)) { // n.GetValueOrDefault() lifted => n. BitSet foundIndices = new BitSet(nullableVars.Count); for (int i = 0; i < nullableVars.Count; i++) { if (nullableVars[i] == inputVar) { foundIndices[i] = true; } } if (foundIndices.Any()) { return(new LdLoc(inputVar) { ILRange = inst.ILRange }, foundIndices); } else { return(null, null); } } else if (inst is Conv conv) { var(arg, bits) = DoLift(conv.Argument); if (arg != null) { if (conv.HasFlag(InstructionFlags.MayThrow) && !bits.All(0, nullableVars.Count)) { // Cannot execute potentially-throwing instruction unless all // the nullableVars are arguments to the instruction // (thus causing it not to throw when any of them is null). return(null, null); } var newInst = new Conv(arg, conv.InputType, conv.InputSign, conv.TargetType, conv.CheckForOverflow, isLifted: true) { ILRange = conv.ILRange }; return(newInst, bits); } } else if (inst is BitNot bitnot) { var(arg, bits) = DoLift(bitnot.Argument); if (arg != null) { var newInst = new BitNot(arg, isLifted: true, stackType: bitnot.ResultType) { ILRange = bitnot.ILRange }; return(newInst, bits); } } else if (inst is BinaryNumericInstruction binary) { var(left, right, bits) = DoLiftBinary(binary.Left, binary.Right); if (left != null && right != null) { if (binary.HasFlag(InstructionFlags.MayThrow) && !bits.All(0, nullableVars.Count)) { // Cannot execute potentially-throwing instruction unless all // the nullableVars are arguments to the instruction // (thus causing it not to throw when any of them is null). return(null, null); } var newInst = new BinaryNumericInstruction( binary.Operator, left, right, binary.LeftInputType, binary.RightInputType, binary.CheckForOverflow, binary.Sign, isLifted: true ) { ILRange = binary.ILRange }; return(newInst, bits); } } else if (inst is Comp comp && !comp.IsLifted && comp.Kind == ComparisonKind.Equality && MatchGetValueOrDefault(comp.Left, out ILVariable v) && NullableType.GetUnderlyingType(v.Type).IsKnownType(KnownTypeCode.Boolean) && comp.Right.MatchLdcI4(0) ) { // C# doesn't support ComparisonLiftingKind.ThreeValuedLogic, // except for operator! on bool?. var(arg, bits) = DoLift(comp.Left); Debug.Assert(arg != null); var newInst = new Comp(comp.Kind, ComparisonLiftingKind.ThreeValuedLogic, comp.InputType, comp.Sign, arg, comp.Right.Clone()) { ILRange = comp.ILRange }; return(newInst, bits); }
/// <summary> /// Recursive function that lifts the specified instruction. /// The input instruction is expected to a subexpression of the trueInst /// (so that all nullableVars are guaranteed non-null within this expression). /// /// Creates a new lifted instruction without modifying the input instruction. /// On success, returns (new lifted instruction, bitset). /// If lifting fails, returns (null, null). /// /// The returned bitset specifies which nullableVars were considered "relevant" for this instruction. /// bitSet[i] == true means nullableVars[i] was relevant. /// /// The new lifted instruction will have equivalent semantics to the input instruction /// if all relevant variables are non-null [except that the result will be wrapped in a Nullable{T} struct]. /// If any relevant variable is null, the new instruction is guaranteed to evaluate to <c>null</c> /// without having any other effect. /// </summary> (ILInstruction, BitSet) DoLift(ILInstruction inst) { if (MatchGetValueOrDefault(inst, out ILVariable inputVar)) { // n.GetValueOrDefault() lifted => n. BitSet foundIndices = new BitSet(nullableVars.Count); for (int i = 0; i < nullableVars.Count; i++) { if (nullableVars[i] == inputVar) { foundIndices[i] = true; } } if (foundIndices.Any()) { return(new LdLoc(inputVar) { ILRange = inst.ILRange }, foundIndices); } else { return(null, null); } } else if (inst is Conv conv) { var(arg, bits) = DoLift(conv.Argument); if (arg != null) { if (conv.HasFlag(InstructionFlags.MayThrow) && !bits.All(0, nullableVars.Count)) { // Cannot execute potentially-throwing instruction unless all // the nullableVars are arguments to the instruction // (thus causing it not to throw when any of them is null). return(null, null); } var newInst = new Conv(arg, conv.InputType, conv.InputSign, conv.TargetType, conv.CheckForOverflow, isLifted: true) { ILRange = conv.ILRange }; return(newInst, bits); } } else if (inst is BinaryNumericInstruction binary) { var(left, leftBits) = DoLift(binary.Left); var(right, rightBits) = DoLift(binary.Right); if (left != null && right == null && SemanticHelper.IsPure(binary.Right.Flags)) { // Embed non-nullable pure expression in lifted expression. right = binary.Right.Clone(); } if (left == null && right != null && SemanticHelper.IsPure(binary.Left.Flags)) { // Embed non-nullable pure expression in lifted expression. left = binary.Left.Clone(); } if (left != null && right != null) { var bits = leftBits ?? rightBits; if (rightBits != null) { bits.UnionWith(rightBits); } if (binary.HasFlag(InstructionFlags.MayThrow) && !bits.All(0, nullableVars.Count)) { // Cannot execute potentially-throwing instruction unless all // the nullableVars are arguments to the instruction // (thus causing it not to throw when any of them is null). return(null, null); } var newInst = new BinaryNumericInstruction( binary.Operator, left, right, binary.LeftInputType, binary.RightInputType, binary.CheckForOverflow, binary.Sign, isLifted: true ) { ILRange = binary.ILRange }; return(newInst, bits); } } return(null, null); }