Exemplo n.º 1
0
        private ExpressionSyntax GenIncDec(FuzzType type, bool isIncrement)
        {
            SyntaxKind[] acceptedTypes =
            {
                SyntaxKind.ULongKeyword,
                SyntaxKind.LongKeyword,
                SyntaxKind.UIntKeyword,
                SyntaxKind.IntKeyword,
                SyntaxKind.UShortKeyword,
                SyntaxKind.ShortKeyword,
                SyntaxKind.ByteKeyword,
                SyntaxKind.SByteKeyword,
            };

            if (!(type is PrimitiveType pt) || !acceptedTypes.Contains(pt.Keyword))
            {
                return(null);
            }

            LValueInfo subject = GenExistingLValue(type, int.MinValue);

            if (subject == null)
            {
                return(null);
            }

            ExpressionSyntax gen = PostfixUnaryExpression(
                isIncrement ? SyntaxKind.PostIncrementExpression : SyntaxKind.PostDecrementExpression,
                subject.Expression);

            return(gen);
        }
Exemplo n.º 2
0
 public ScopeValue(FuzzType type, ExpressionSyntax expr, int refEscapeScope, bool readOnly)
 {
     Type           = type;
     Expression     = expr;
     RefEscapeScope = refEscapeScope;
     ReadOnly       = readOnly;
 }
Exemplo n.º 3
0
        public void Generate(FuzzType returnType, bool randomizeParams)
        {
            ReturnType = returnType;

            if (randomizeParams)
            {
                int numArgs = Options.MethodParameterCountDist.Sample(Random.Rng);
                Parameters = new VariableIdentifier[numArgs];
                for (int i = 0; i < Parameters.Length; i++)
                {
                    FuzzType type = Types.PickType(Options.ParameterIsByRefProb);
                    string   name = $"arg{i}";
                    // A ref to a by-ref parameter can escape to at least its parent method
                    int refEscapeScope = type is RefType ? 1 : 0;
                    Parameters[i] = new VariableIdentifier(type, name, refEscapeScope);
                }
            }
            else
            {
                Parameters = Array.Empty <VariableIdentifier>();
            }

            _level = -1;
            Body   = GenBlock(true);
        }
Exemplo n.º 4
0
        private ExpressionSyntax GenBoolProducingBinary()
        {
            FuzzType         boolType = Types.GetPrimitiveType(SyntaxKind.BoolKeyword);
            ExpressionSyntax left, right;
            SyntaxKind       op = (SyntaxKind)Options.BinaryBoolDist.Sample(Random.Rng);

            if (op == SyntaxKind.LogicalAndExpression || op == SyntaxKind.LogicalOrExpression ||
                op == SyntaxKind.ExclusiveOrExpression || op == SyntaxKind.BitwiseAndExpression ||
                op == SyntaxKind.BitwiseOrExpression)
            {
                // &&, ||, ^, &, | must be between bools to produce bool
                left  = GenExpression(boolType);
                right = GenExpression(boolType);
            }
            else if (op == SyntaxKind.EqualsExpression || op == SyntaxKind.NotEqualsExpression)
            {
                PrimitiveType leftType = Types.PickPrimitiveType(f => true);
                left = GenExpression(leftType);
                PrimitiveType rightType = Types.PickPrimitiveType(f => BinOpTable.Equality.GetResultType(leftType.Keyword, f.Keyword).HasValue);
                right = GenExpression(rightType);
            }
            else
            {
                Debug.Assert(op == SyntaxKind.LessThanOrEqualExpression || op == SyntaxKind.LessThanExpression ||
                             op == SyntaxKind.GreaterThanOrEqualExpression || op == SyntaxKind.GreaterThanExpression);

                PrimitiveType leftType  = Types.PickPrimitiveType(f => f.Keyword != SyntaxKind.BoolKeyword);
                PrimitiveType rightType = Types.PickPrimitiveType(f => BinOpTable.Relop.GetResultType(leftType.Keyword, f.Keyword).HasValue);
                left  = GenExpression(leftType);
                right = GenExpression(rightType);
            }

            return(BinaryExpression(op, ParenthesizeIfNecessary(left), ParenthesizeIfNecessary(right)));
        }
Exemplo n.º 5
0
    public ArrayTypeSyntax GenReferenceToArrayType()
    {
        if (_type != null)
        {
            return(_type);
        }

        List <int> ranks = new() { Rank };
        // int[][] => ArrayType(ArrayType(Int, 1), 1)
        // int[][,] => ArrayType(ArrayType(Int, 2), 1)
        FuzzType innerElem = ElementType;

        while (innerElem is ArrayType at)
        {
            ranks.Add(at.Rank);
            innerElem = at.ElementType;
        }

        _type = ArrayType(
            innerElem.GenReferenceTo(),
            ranks.Select(
                r =>
                ArrayRankSpecifier(
                    SeparatedList(
                        Enumerable.Repeat((ExpressionSyntax)OmittedArraySizeExpression(), r)))).ToSyntaxList());

        return(_type);
    }
Exemplo n.º 6
0
    public static ExpressionSyntax GenLiteral(TypeManager types, Randomizer random, FuzzType type)
    {
        switch (type)
        {
        case PrimitiveType prim:
            return(GenPrimitiveLiteral(random, prim));

        case AggregateType agg:
            return
                (ObjectCreationExpression(type.GenReferenceTo())
                 .WithArgumentList(
                     ArgumentList(
                         SeparatedList(agg.Fields.Select(af => Argument(GenLiteral(types, random, af.Type)))))));

        case ArrayType arr:
            List <int> dims = GenArrayDimensions(random, arr);
            return(GenArrayCreation(types, random, arr, dims));

        case InterfaceType it:
            return(GenLiteral(types, random, random.NextElement(types.GetImplementingTypes(it))));

        default:
            throw new Exception("Unreachable");
        }
    }
Exemplo n.º 7
0
 public LValueInfo(ExpressionSyntax expression, FuzzType type, int refEscapeScope, bool readOnly)
 {
     Expression     = expression;
     Type           = type;
     RefEscapeScope = refEscapeScope;
     ReadOnly       = readOnly;
 }
Exemplo n.º 8
0
        /// <summary>
        /// Generates an access to a random member:
        /// * A static variable
        /// * A local variable
        /// * An argument
        /// * A field in one of these, if aggregate type
        /// * An element in one of these, if array type
        /// </summary>
        private LValueInfo GenMemberAccess(FuzzType ft)
        {
            List <LValueInfo> paths =
                Random.FlipCoin(Options.MemberAccessSelectLocalProb)
                ? CollectVariablePaths(ft, int.MinValue, true, false)
                : CollectVariablePaths(ft, int.MinValue, false, true);

            return(paths.Count > 0 ? Random.NextElement(paths) : null);
        }
Exemplo n.º 9
0
        public StaticField GenerateNewField(FuzzType type)
        {
            type = type ?? Types.PickType();

            string      name  = "s_" + (++_counter);
            StaticField field = new StaticField(new VariableIdentifier(type, name), LiteralGenerator.GenLiteral(Random, type));

            _fields.Add(field);
            return(field);
        }
Exemplo n.º 10
0
    public ArrayType(FuzzType elementType, int rank = 1)
    {
        if (rank < 1)
        {
            throw new ArgumentException("Invalid rank " + rank, nameof(rank));
        }

        ElementType = elementType;
        Rank        = rank;
    }
Exemplo n.º 11
0
        private ExpressionSyntax GenExpression(FuzzType type)
        {
            Debug.Assert(!(type is RefType));
            ExpressionSyntax gen;

            do
            {
                ExpressionKind kind = (ExpressionKind)Options.ExpressionTypeDist.Sample(Random.Rng);
                switch (kind)
                {
                case ExpressionKind.MemberAccess:
                    gen = GenMemberAccess(type)?.Expression;
                    break;

                case ExpressionKind.Literal:
                    gen = GenLiteral(type);
                    break;

                case ExpressionKind.Binary:
                    gen = GenBinary(type);
                    break;

                case ExpressionKind.Call:
                    gen = GenCall(type, true);
                    break;

                case ExpressionKind.Increment:
                    gen = GenIncDec(type, true);
                    break;

                case ExpressionKind.Decrement:
                    gen = GenIncDec(type, false);
                    break;

                case ExpressionKind.NewObject:
                    gen = GenNewObject(type);
                    break;

                case ExpressionKind.Cast:
                    gen = null;
                    continue;
                    gen = GenCast(type);
                    break;

                default:
                    throw new Exception("Unreachable");
                }
            }while (gen == null);

            return(gen);
        }
Exemplo n.º 12
0
        /// <summary>Returns an lvalue.</summary>
        private LValueInfo GenLValue(FuzzType type, int minRefEscapeScope)
        {
            Debug.Assert(type != null);

            LValueInfo lv = GenExistingLValue(type, minRefEscapeScope);

            if (lv == null)
            {
                StaticField newStatic = Statics.GenerateNewField(type);
                lv = new LValueInfo(IdentifierName(newStatic.Var.Name), type, int.MaxValue);
            }

            return(lv);
        }
Exemplo n.º 13
0
        private ExpressionSyntax GenBinary(FuzzType type)
        {
            if (!(type is PrimitiveType pt))
            {
                return(null);
            }

            if (pt.Keyword == SyntaxKind.BoolKeyword)
            {
                return(GenBoolProducingBinary());
            }

            return(GenIntegralProducingBinary(pt));
        }
Exemplo n.º 14
0
        private (ExpressionSyntax, ExpressionSyntax) GenLeftRightForBinary(FuzzType leftType, FuzzType rightType)
        {
            ExpressionSyntax left = GenExpression(leftType);
            ExpressionSyntax right;

            // There are two reasons we don't allow both left and right to be literals:
            // 1. If the computation overflows, this gives a C# compiler error
            // 2. The compiler is required to constant fold these expressions which is not interesting.
            do
            {
                right = GenExpression(rightType);
            } while (left is LiteralExpressionSyntax && right is LiteralExpressionSyntax);

            return(left, right);
        }
Exemplo n.º 15
0
    private static List <int> GenArrayDimensions(Randomizer random, ArrayType at)
    {
        int      dimsRequired = at.Rank;
        FuzzType elemType     = at.ElementType;

        while (elemType is ArrayType innerArr)
        {
            dimsRequired += innerArr.Rank;
            elemType      = innerArr.ElementType;
        }

        List <int> dimensions = new(dimsRequired);

        for (int tries = 0; tries < 100; tries++)
        {
            // If we are constructing an aggregate type start out by the number of fields, to limit
            // the number of constants required here.
            int totalSize = elemType is AggregateType elemAgg?elemAgg.GetTotalNumPrimitiveFields() : 1;

            int maxArrayTotalSize = Math.Max(totalSize, random.Options.MaxArrayTotalSize);

            for (int i = 0; i < dimensions.Capacity; i++)
            {
                int dim = random.Next(1, random.Options.MaxArrayLengthPerDimension + 1);
                if (totalSize * dim > maxArrayTotalSize)
                {
                    break;
                }

                dimensions.Add(dim);
                totalSize *= dim;
            }

            if (dimensions.Count == dimensions.Capacity)
            {
                return(dimensions);
            }

            dimensions.Clear();
        }

        for (int i = 0; i < dimsRequired; i++)
        {
            dimensions.Add(1);
        }

        return(dimensions);
    }
Exemplo n.º 16
0
        private ExpressionSyntax GenNewObject(FuzzType type)
        {
            if (!(type is AggregateType at))
            {
                return(null);
            }

            ObjectCreationExpressionSyntax creation =
                ObjectCreationExpression(type.GenReferenceTo())
                .WithArgumentList(
                    ArgumentList(
                        SeparatedList(
                            at.Fields.Select(f => Argument(GenExpression(f.Type))))));

            return(creation);
        }
Exemplo n.º 17
0
        public StaticField PickStatic(FuzzType type = null)
        {
            List <StaticField> fields = _fields;

            if (type != null)
            {
                fields = _fields.Where(f => f.Var.Type == type).ToList();
            }

            if (!fields.Any())
            {
                return(GenerateNewField(type));
            }

            return(Random.NextElement(fields));
        }
Exemplo n.º 18
0
        public void Generate(FuzzType returnType, bool randomizeParams)
        {
            ReturnType = returnType;

            if (randomizeParams)
            {
                int numArgs = Options.MethodParameterCountDist.Sample(Random.Rng);
                ParameterTypes = Enumerable.Range(0, numArgs).Select(i => Types.PickType()).ToArray();
            }
            else
            {
                ParameterTypes = Array.Empty <FuzzType>();
            }

            _level = -1;
            Body   = GenBlock(ReturnType != null);
        }
Exemplo n.º 19
0
        private ExpressionSyntax GenCall(FuzzType type, bool allowNew)
        {
            Debug.Assert(!(type is RefType), "Cannot GenCall to ref type -- use GenExistingLValue for that");

            FuncGenerator func;

            if (allowNew && Random.FlipCoin(Options.GenNewMethodProb))
            {
                type = type ?? Types.PickType(Options.ReturnTypeIsByRefProb);

                func = new FuncGenerator(_funcs, Random, Types, Statics, _genChecksumSiteId);
                func.Generate(type, true);
            }
            else
            {
                IEnumerable <FuncGenerator> funcs = _funcs.Skip(_funcIndex + 1);
                if (type != null)
                {
                    funcs = funcs.Where(f => f.ReturnType.IsCastableTo(type) || (f.ReturnType is RefType rt && rt.InnerType.IsCastableTo(type)));
                }

                List <FuncGenerator> list = funcs.ToList();
                if (list.Count == 0)
                {
                    return(null);
                }

                func = Random.NextElement(list);
                type = type ?? func.ReturnType;
            }

            ArgumentSyntax[]           args  = GenArgs(func, 0, out _);
            InvocationExpressionSyntax invoc =
                InvocationExpression(
                    IdentifierName(func.Name),
                    ArgumentList(
                        SeparatedList(args)));

            if (func.ReturnType == type || func.ReturnType is RefType retRt && retRt.InnerType == type)
            {
                return(invoc);
            }

            return(CastExpression(type.GenReferenceTo(), invoc));
        }
Exemplo n.º 20
0
        private List <LValueInfo> CollectVariablePaths(FuzzType type, int minRefEscapeScope, bool collectLocals, bool collectStatics)
        {
            List <LValueInfo> paths = new List <LValueInfo>();

            if (collectLocals)
            {
                foreach (ScopeFrame sf in _scope)
                {
                    foreach (VariableIdentifier variable in sf.Variables)
                    {
                        AppendVariablePaths(paths, variable);
                    }
                }
            }

            if (collectStatics)
            {
                foreach (StaticField stat in Statics.Fields)
                {
                    AppendVariablePaths(paths, stat.Var);
                }
            }

            Debug.Assert(type == null || !(type is RefType), "Cannot collect variables of ref type");

            // Verify that a type is allowed. Often we want to implicitly promote
            // a by-ref to its inner type, because we are taking a ref anyway or using
            // its value, and it is automatically promoted.
            // This checks for both of these cases:
            // ref int lhs = ...;
            // int rhs1 = ...;
            // ref int rhs2 = ...;
            // lhs = ref rhs1; // must be supported
            // lhs = ref rhs2; // must be supported
            bool IsAllowedType(FuzzType other)
            => type == null || (other == type || (other is RefType rt && rt.InnerType == type));

            paths.RemoveAll(lv => lv.RefEscapeScope < minRefEscapeScope || !IsAllowedType(lv.Type));
            return(paths);
        }
Exemplo n.º 21
0
        private ArgumentSyntax[] GenArgs(FuncGenerator funcToCall, int minRefEscapeScope, out int argsMinRefEscapeScope)
        {
            ArgumentSyntax[] args = new ArgumentSyntax[funcToCall.Parameters.Length];
            argsMinRefEscapeScope = int.MaxValue;

            for (int i = 0; i < args.Length; i++)
            {
                FuzzType paramType = funcToCall.Parameters[i].Type;
                if (paramType is RefType rt)
                {
                    LValueInfo lv = GenLValue(rt.InnerType, minRefEscapeScope);
                    argsMinRefEscapeScope = Math.Min(argsMinRefEscapeScope, lv.RefEscapeScope);
                    args[i] = Argument(lv.Expression).WithRefKindKeyword(Token(SyntaxKind.RefKeyword));
                }
                else
                {
                    args[i] = Argument(GenExpression(paramType));
                }
            }

            return(args);
        }
Exemplo n.º 22
0
 public VariableIdentifier(FuzzType type, string name)
 {
     Type = type;
     Name = name;
 }
Exemplo n.º 23
0
 public RefType(FuzzType innerType)
 {
     Debug.Assert(!(innerType is RefType), "Cannot create ref to ref type");
     InnerType = innerType;
 }
Exemplo n.º 24
0
 private ExpressionSyntax GenCast(FuzzType type)
 {
     return(CastExpression(type.GenReferenceTo(), GenExpression(type)));
 }
Exemplo n.º 25
0
 public AggregateField(FuzzType type, string name)
 {
     Type = type;
     Name = name;
 }
Exemplo n.º 26
0
        private ExpressionSyntax GenCall(FuzzType type, bool allowNew)
        {
            Debug.Assert(!(type is RefType), "Cannot GenCall to ref type -- use GenExistingLValue for that");

            FuncGenerator func;

            if (allowNew && Random.FlipCoin(Options.GenNewFunctionProb) && !Options.FuncGenRejection.Reject(_funcs.Count, Random.Rng))
            {
                type = type ?? Types.PickType(Options.ReturnTypeIsByRefProb);

                func = new FuncGenerator(_funcs, Random, Types, Statics, _genChecksumSiteId);
                func.Generate(type, true);
            }
            else
            {
                IEnumerable <FuncGenerator> funcs =
                    _funcs
                    .Skip(_funcIndex + 1)
                    .Where(candidate =>
                {
                    // Make sure we do not get too many leaf calls. Here we compute what the new transitive
                    // number of calls would be to each function, and if it's too much, reject this candidate.
                    // Note that we will never reject calling a leaf function directly, even if the +1 puts
                    // us over the cap. That is intentional. We only want to limit the exponential growth
                    // which happens when functions call functions multiple times, and those functions also
                    // call functions multiple times.
                    foreach (var(transFunc, transNumCall) in candidate._callCountMap)
                    {
                        _callCountMap.TryGetValue(transFunc, out long curNumCalls);
                        if (curNumCalls + transNumCall > Options.SingleFunctionMaxTotalCalls)
                        {
                            return(false);
                        }
                    }

                    return(true);
                });

                if (type != null)
                {
                    funcs = funcs.Where(f => f.ReturnType.IsCastableTo(type) || (f.ReturnType is RefType rt && rt.InnerType.IsCastableTo(type)));
                }

                List <FuncGenerator> list = funcs.ToList();
                if (list.Count == 0)
                {
                    return(null);
                }

                func = Random.NextElement(list);
                type = type ?? func.ReturnType;
            }

            // Update transitive call counts before generating args, so we decrease chance of
            // calling the same methods in the arg expressions.
            _callCountMap.TryGetValue(func._funcIndex, out long numCalls);
            _callCountMap[func._funcIndex] = numCalls + 1;

            foreach (var(transFunc, transNumCalls) in func._callCountMap)
            {
                _callCountMap.TryGetValue(transFunc, out long curNumCalls);
                _callCountMap[transFunc] = curNumCalls + transNumCalls;
            }

            ArgumentSyntax[]           args  = GenArgs(func, 0, out _);
            InvocationExpressionSyntax invoc =
                InvocationExpression(
                    IdentifierName(func.Name),
                    ArgumentList(
                        SeparatedList(args)));

            if (func.ReturnType == type || func.ReturnType is RefType retRt && retRt.InnerType == type)
            {
                return(invoc);
            }

            return(CastExpression(type.GenReferenceTo(), invoc));
        }
Exemplo n.º 27
0
 public StaticField(FuzzType type, string name, ExpressionSyntax initializer)
 {
     Type        = type;
     Name        = name;
     Initializer = initializer;
 }
Exemplo n.º 28
0
        private StatementSyntax GenAssignmentStatement()
        {
            LValueInfo lvalue = null;

            if (!Random.FlipCoin(Options.AssignToNewVarProb))
            {
                lvalue = GenExistingLValue(null, int.MinValue);
            }

            if (lvalue == null)
            {
                FuzzType newType = Types.PickType(Options.LocalIsByRefProb);
                // Determine if we should create a new local. We do this with a certain probabilty,
                // or always if the new type is a by-ref type (we cannot have static by-refs).
                if (newType is RefType || Random.FlipCoin(Options.NewVarIsLocalProb))
                {
                    VariableIdentifier variable;
                    string             varName = $"var{_varCounter++}";
                    ExpressionSyntax   rhs;
                    if (newType is RefType newRt)
                    {
                        LValueInfo rhsLV = GenLValue(newRt.InnerType, int.MinValue);
                        variable = new VariableIdentifier(newType, varName, rhsLV.RefEscapeScope);
                        rhs      = RefExpression(rhsLV.Expression);
                    }
                    else
                    {
                        rhs      = GenExpression(newType);
                        variable = new VariableIdentifier(newType, varName, -(_scope.Count - 1));
                    }

                    LocalDeclarationStatementSyntax decl =
                        LocalDeclarationStatement(
                            VariableDeclaration(
                                variable.Type.GenReferenceTo(),
                                SingletonSeparatedList(
                                    VariableDeclarator(variable.Name)
                                    .WithInitializer(
                                        EqualsValueClause(rhs)))));

                    _scope.Last().Variables.Add(variable);

                    return(decl);
                }

                StaticField newStatic = Statics.GenerateNewField(newType);
                lvalue = new LValueInfo(IdentifierName(newStatic.Var.Name), newType, int.MaxValue);
            }

            // Determine if we should generate a ref-reassignment. In that case we cannot do anything
            // clever, like generate compound assignments.
            FuzzType rhsType = lvalue.Type;

            if (lvalue.Type is RefType rt)
            {
                if (Random.FlipCoin(Options.AssignGenRefReassignProb))
                {
                    RefExpressionSyntax refRhs = RefExpression(GenLValue(rt.InnerType, lvalue.RefEscapeScope).Expression);
                    return
                        (ExpressionStatement(
                             AssignmentExpression(
                                 SyntaxKind.SimpleAssignmentExpression,
                                 lvalue.Expression,
                                 refRhs)));
                }

                // We have a ref-type, but are not generating a ref-reassign, so lift the type and make a normal assignment.
                rhsType = rt.InnerType;
            }

            SyntaxKind assignmentKind = SyntaxKind.SimpleAssignmentExpression;

            // Determine if we should generate compound assignment.
            if (rhsType.AllowedAdditionalAssignmentKinds.Length > 0 && Random.FlipCoin(Options.CompoundAssignmentProb))
            {
                assignmentKind = Random.NextElement(rhsType.AllowedAdditionalAssignmentKinds);
            }

            // Early our for simple cases.
            if (assignmentKind == SyntaxKind.PreIncrementExpression ||
                assignmentKind == SyntaxKind.PreDecrementExpression)
            {
                return(ExpressionStatement(PrefixUnaryExpression(assignmentKind, lvalue.Expression)));
            }

            if (assignmentKind == SyntaxKind.PostIncrementExpression ||
                assignmentKind == SyntaxKind.PostDecrementExpression)
            {
                return(ExpressionStatement(PostfixUnaryExpression(assignmentKind, lvalue.Expression)));
            }

            // Right operand of shifts are always ints.
            if (assignmentKind == SyntaxKind.LeftShiftAssignmentExpression ||
                assignmentKind == SyntaxKind.RightShiftAssignmentExpression)
            {
                rhsType = Types.GetPrimitiveType(SyntaxKind.IntKeyword);
            }

            ExpressionSyntax right = GenExpression(rhsType);

            // For modulo and division we don't want to throw divide-by-zero exceptions,
            // so always or right-hand-side with 1.
            if (assignmentKind == SyntaxKind.ModuloAssignmentExpression ||
                assignmentKind == SyntaxKind.DivideAssignmentExpression)
            {
                right =
                    CastExpression(
                        rhsType.GenReferenceTo(),
                        ParenthesizedExpression(
                            BinaryExpression(
                                SyntaxKind.BitwiseOrExpression,
                                ParenthesizeIfNecessary(right),
                                LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(1)))));
            }

            return(ExpressionStatement(AssignmentExpression(assignmentKind, lvalue.Expression, right)));
        }
Exemplo n.º 29
0
 private ExpressionSyntax GenLiteral(FuzzType type)
 {
     return(LiteralGenerator.GenLiteral(Random, type));
 }
Exemplo n.º 30
0
        private LValueInfo GenExistingLValue(FuzzType type, int minRefEscapeScope)
        {
            Debug.Assert(type == null || !(type is RefType));

            LValueKind kind = (LValueKind)Options.ExistingLValueDist.Sample(Random.Rng);

            if (kind == LValueKind.RefReturningCall)
            {
                List <FuncGenerator> refReturningFuncs = new List <FuncGenerator>();
                foreach (FuncGenerator func in _funcs.Skip(_funcIndex + 1))
                {
                    if (!(func.ReturnType is RefType rt))
                    {
                        continue;
                    }

                    if (type == null || type == rt.InnerType)
                    {
                        refReturningFuncs.Add(func);
                    }
                }

                // If we don't have a ref-returning function, fall back to local/static by retrying.
                // The reason we don't always fall back to trying other options is that otherwise
                // we may stack overflow in cases like ref int M(ref int a), if we have no locals
                // or statics of type int. We would keep generating method calls in this case.
                // This is kind of a hack, although it is pretty natural to pick statics/locals for
                // lvalues, so it is not that big of a deal.
                if (refReturningFuncs.Count == 0)
                {
                    return(GenExistingLValue(type, minRefEscapeScope));
                }

                FuncGenerator funcToCall = Random.NextElement(refReturningFuncs);

                // When calling a func that returns a by-ref, we need to take into account that the C# compiler
                // performs 'escape-analysis' on by-refs. If we want to produce a non-local by-ref we are only
                // allowed to pass non-local by-refs. The following example illustrates the analysis performed
                // by the compiler:
                // ref int M(ref int b, ref int c) {
                //   int a = 2;
                //   return ref Max(ref a, ref b); // compiler error, returned by-ref could point to 'a' and escape
                //   return ref Max(ref b, ref c); // ok, returned ref cannot point to local
                // }
                // ref int Max(ref int a, ref int b) { ... }
                // This is the reason for the second arg passed to GenArgs. For example, if we want a call to return
                // a ref that may escape, we need a minimum ref escape scope of 1, since 0 could pass refs to locals,
                // and the result of that call would not be valid to return.
                ArgumentSyntax[]           args  = GenArgs(funcToCall, minRefEscapeScope, out int argsMinRefEscapeScope);
                InvocationExpressionSyntax invoc =
                    InvocationExpression(
                        IdentifierName(funcToCall.Name),
                        ArgumentList(
                            SeparatedList(
                                args)));

                return(new LValueInfo(invoc, ((RefType)funcToCall.ReturnType).InnerType, argsMinRefEscapeScope));
            }

            List <LValueInfo> lvalues = CollectVariablePaths(type, minRefEscapeScope, kind == LValueKind.Local, kind == LValueKind.Static);

            if (lvalues.Count == 0)
            {
                // We typically fall back to generating a static, so just try to find a static if we found no local.
                if (kind != LValueKind.Local)
                {
                    return(null);
                }

                lvalues = CollectVariablePaths(type, minRefEscapeScope, false, true);
                if (lvalues.Count == 0)
                {
                    return(null);
                }
            }

            return(Random.NextElement(lvalues));
        }