packages feed

tala-compiler-0.1.0: test/StaticMemberCoreTests.hs

-- SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com>
-- SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-1.1

{- | Core-boundary tests for selected static method identities.

Semantic success alone is insufficient: each overload call must become a
CoreApply to the selected function SymbolId, and CorePrep must preserve that
identity when it atomizes the call.
-}
module StaticMemberCoreTests (staticMemberCoreTests) where

import Data.List (sort)
import Progmasoft.Tala.AST
import Progmasoft.Tala.BuiltinTypes
import Progmasoft.Tala.Compiler
import Progmasoft.Tala.Core
import Progmasoft.Tala.Core.CorePrep
import Progmasoft.Tala.Diagnostic

staticMemberCoreTests :: [(String, Bool)]
staticMemberCoreTests =
    [ ("Core keeps every overload as a distinct function", coreKeepsDistinctFunctions)
    , ("Core call target is the selected overload, not the lexical seed", coreTargetIsSelected)
    , ("Core source metadata has one entry per overload identity", coreOwnersAreUnique)
    , ("Core target and source owner share the same function identity", coreCallOwnerIdentityAgrees)
    , ("CorePrep retains the exact overload target", corePrepTargetIsSelected)
    , ("CorePrep retains the selected function declaration", corePrepFunctionIdentityIsStable)
    , ("two calls to one overload preserve target multiplicity", repeatedCoreCallsKeepMultiplicity)
    , ("calls to two overloads retain their distinct target identities", mixedCoreCallsKeepDistinctTargets)
    , ("unqualified overload dispatch lowers to its declaring method", unqualifiedCoreTargetIsSelected)
    , ("core lowering does not encode a selector as a synthesized name", coreTargetIsSourceFunction)
    , ("overload lowering preserves argument order", coreCallArgumentsPreserveOrder)
    , ("CorePrep preserves the argument order of the selected overload", corePrepCallArgumentsPreserveOrder)
    , ("overload lowering preserves the chosen function type", coreCallFunctionTypeIsExact)
    , ("overloaded methods with different owners do not alias", ownerNamesDoNotAlias)
    , ("all Core function ids remain globally unique in the unit", allCoreFunctionIdsAreUnique)
    , ("all CorePrep function ids remain globally unique in the unit", allCorePrepFunctionIdsAreUnique)
    , ("CorePrep output remains verified after overload lowering", corePrepExistsAndIsWellFormed)
    , ("Core retains one method symbol for each static overload", coreOverloadSymbolCountIsExact)
    , ("CorePrep retains the source file for each overload", corePrepOwnerFilesAreRetained)
    , ("overload resolution is independent of body return literals", bodyLiteralDoesNotChangeBinding)
    , ("the selected overload identity survives a branch in its body", branchBodyDoesNotChangeBinding)
    , ("the selected overload identity survives arithmetic around the call", arithmeticCallRetainsBinding)
    , ("the selected overload identity survives a local initializer", initializerCallRetainsBinding)
    , ("the selected overload identity survives a condition", conditionCallRetainsBinding)
    , ("the selected overload identity survives a loop condition", loopCallRetainsBinding)
    ]
        ++ scalarCoreSelectionTests

coreKeepsDistinctFunctions :: Bool
coreKeepsDistinctFunctions = case compiled fixture of
    Right artifacts -> case selectFunctions (artifactCore artifacts) of
        [first, second] -> resolvedSymbol (coreFunctionName first) /= resolvedSymbol (coreFunctionName second)
        _ -> False
    Left _ -> False

coreTargetIsSelected :: Bool
coreTargetIsSelected = case compiled fixture of
    Right artifacts -> matchingTargets intType (artifactCore artifacts) == namedFunctionSymbols "Select" intType (artifactCore artifacts)
    Left _ -> False

coreOwnersAreUnique :: Bool
coreOwnersAreUnique = case compiled fixture of
    Right artifacts ->
        let selectedIds = map (symbolIdValue . resolvedSymbol . coreFunctionName) (selectFunctions (artifactCore artifacts))
            owners = map fst (coreModuleFunctionSources (artifactCore artifacts))
         in all (\identity -> count identity owners == 1) selectedIds
    Left _ -> False

coreCallOwnerIdentityAgrees :: Bool
coreCallOwnerIdentityAgrees = case compiled fixture of
    Right artifacts -> case matchingTargets intType (artifactCore artifacts) of
        [target] ->
            let moduleValue = artifactCore artifacts
                owners = [owner | (identity, owner) <- coreModuleFunctionSources moduleValue, identity == symbolIdValue target]
             in owners == ["overload-core.tala"]
        _ -> False
    Left _ -> False

corePrepTargetIsSelected :: Bool
corePrepTargetIsSelected = case compiled fixture of
    Right artifacts ->
        matchingCorePrepTargets intType (artifactCorePrep artifacts)
            == namedCorePrepFunctionSymbols "Select" intType (artifactCorePrep artifacts)
    Left _ -> False

corePrepFunctionIdentityIsStable :: Bool
corePrepFunctionIdentityIsStable = case compiled fixture of
    Right artifacts ->
        let coreIds = sort (map (resolvedSymbol . coreFunctionName) (selectFunctions (artifactCore artifacts)))
            prepIds = sort (map (resolvedSymbol . corePrepFunctionName) (selectCorePrepFunctions (artifactCorePrep artifacts)))
         in coreIds == prepIds
    Left _ -> False

repeatedCoreCallsKeepMultiplicity :: Bool
repeatedCoreCallsKeepMultiplicity = case compiled repeatedCalls of
    Right artifacts -> case matchingTargets intType (artifactCore artifacts) of
        [first, second] -> first == second
        _ -> False
    Left _ -> False

mixedCoreCallsKeepDistinctTargets :: Bool
mixedCoreCallsKeepDistinctTargets = case compiled mixedCalls of
    Right artifacts ->
        let targets = matchingTargetsAny "Select" (artifactCore artifacts)
         in length targets == 2 && length (unique targets) == 2
    Left _ -> False

unqualifiedCoreTargetIsSelected :: Bool
unqualifiedCoreTargetIsSelected = case compiled unqualifiedCall of
    Right artifacts ->
        let moduleValue = artifactCore artifacts
            targets = matchingTargets intType moduleValue
            selected = namedFunctionSymbols "Select" intType moduleValue
         in targets == selected && not (null targets)
    Left _ -> False

coreTargetIsSourceFunction :: Bool
coreTargetIsSourceFunction = case compiled fixture of
    Right artifacts ->
        let moduleValue = artifactCore artifacts
            targetNames = map resolvedSpelling (matchingTargetsAny "Select" moduleValue)
            declarations = map (resolvedSpelling . coreFunctionName) (coreModuleFunctions moduleValue)
         in not (null targetNames) && all (`elem` declarations) targetNames
    Left _ -> False

coreCallArgumentsPreserveOrder :: Bool
coreCallArgumentsPreserveOrder = case compiled twoArguments of
    Right artifacts -> case callArgumentsFor "Select" (artifactCore artifacts) of
        [[CoreVariable first _, CoreVariable second _]] ->
            resolvedSpelling first == Identifier "left"
                && resolvedSpelling second == Identifier "right"
                && resolvedSymbol first /= resolvedSymbol second
        _ -> False
    Left _ -> False

corePrepCallArgumentsPreserveOrder :: Bool
corePrepCallArgumentsPreserveOrder = case compiled twoArguments of
    Right artifacts -> case corePrepCallArgumentsFor "Select" (artifactCorePrep artifacts) of
        [[CorePrepVariable first _, CorePrepVariable second _]] ->
            resolvedSpelling first == Identifier "left"
                && resolvedSpelling second == Identifier "right"
                && resolvedSymbol first /= resolvedSymbol second
        _ -> False
    Left _ -> False

coreCallFunctionTypeIsExact :: Bool
coreCallFunctionTypeIsExact = case compiled fixture of
    Right artifacts -> case callTypesFor "Select" (artifactCore artifacts) of
        [FunctionType [parameter] result] -> parameter == intType && result == intType
        _ -> False
    Left _ -> False

ownerNamesDoNotAlias :: Bool
ownerNamesDoNotAlias = case compiled twoOwners of
    Right artifacts ->
        let first = namedFunctions "FirstRead" (artifactCore artifacts)
            second = namedFunctions "SecondRead" (artifactCore artifacts)
         in case (first, second) of
                ([left], [right]) -> resolvedSymbol (coreFunctionName left) /= resolvedSymbol (coreFunctionName right)
                _ -> False
    Left _ -> False

allCoreFunctionIdsAreUnique :: Bool
allCoreFunctionIdsAreUnique = case compiled fixture of
    Right artifacts -> uniqueSymbols (map (resolvedSymbol . coreFunctionName) (coreModuleFunctions (artifactCore artifacts)))
    Left _ -> False

allCorePrepFunctionIdsAreUnique :: Bool
allCorePrepFunctionIdsAreUnique = case compiled fixture of
    Right artifacts -> uniqueSymbols (map (resolvedSymbol . corePrepFunctionName) (corePrepModuleFunctions (artifactCorePrep artifacts)))
    Left _ -> False

corePrepExistsAndIsWellFormed :: Bool
corePrepExistsAndIsWellFormed = case compiled fixture of
    Right artifacts ->
        let prepared = artifactCorePrep artifacts
         in not (null (corePrepModuleFunctions prepared))
                && all ((> 0) . symbolIdValue . resolvedSymbol . corePrepFunctionName) (corePrepModuleFunctions prepared)
                && all (not . null . corePrepFunctionBlocks) (corePrepModuleFunctions prepared)
    Left _ -> False

coreOverloadSymbolCountIsExact :: Bool
coreOverloadSymbolCountIsExact = case compiled fixture of
    Right artifacts -> length (selectFunctions (artifactCore artifacts)) == 2
    Left _ -> False

corePrepOwnerFilesAreRetained :: Bool
corePrepOwnerFilesAreRetained = case compiled fixture of
    Right artifacts ->
        let prepared = corePrepModuleFunctions (artifactCorePrep artifacts)
            selected = selectCorePrepFunctions (artifactCorePrep artifacts)
         in length selected == 2
                && all ((== "overload-core.tala") . corePrepFunctionSourceFile) selected
                && all (`elem` prepared) selected
    Left _ -> False

bodyLiteralDoesNotChangeBinding :: Bool
bodyLiteralDoesNotChangeBinding =
    exactTargetFor
        "class Catalog { public static int Select(long value) { if (value > 0) { return 0; } else { return 2; } } public static int Select(int value) { if (value > 0) { return 1; } else { return 3; } } } class Caller { int Invoke(int value) { return Catalog.Select(value); } }"
        intType

branchBodyDoesNotChangeBinding :: Bool
branchBodyDoesNotChangeBinding = exactTargetFor branchFixture intType

arithmeticCallRetainsBinding :: Bool
arithmeticCallRetainsBinding =
    exactTargetFor
        "class Catalog { public static int Select(long value) { if (value > 0) { return 1; } else { return 2; } } public static int Select(int value) { if (value > 0) { return value; } else { return 3; } } } class Caller { int Invoke(int value) { return Catalog.Select(value) + 1; } }"
        intType

initializerCallRetainsBinding :: Bool
initializerCallRetainsBinding =
    exactTargetFor
        "class Catalog { public static int Select(long value) { if (value > 0) { return 1; } else { return 2; } } public static int Select(int value) { if (value > 0) { return value; } else { return 3; } } } class Caller { int Invoke(int value) { int result = Catalog.Select(value); return result; } }"
        intType

conditionCallRetainsBinding :: Bool
conditionCallRetainsBinding =
    exactTargetFor
        "class Catalog { public static bool Select(long value) { if (value > 0) { return true; } else { return false; } } public static bool Select(int value) { if (value > 0) { return false; } else { return true; } } } class Caller { int Invoke(int value) { if (Catalog.Select(value)) { return 1; } else { return 0; } } }"
        intType

loopCallRetainsBinding :: Bool
loopCallRetainsBinding =
    exactTargetFor
        "class Catalog { public static bool Select(long value) { if (value > 0) { return true; } else { return false; } } public static bool Select(int value) { if (value > 0) { return false; } else { return true; } } } class Caller { int Invoke(int value) { while (Catalog.Select(value)) { return 1; } return 0; } }"
        intType

scalarCoreSelectionTests :: [(String, Bool)]
scalarCoreSelectionTests =
    [ ( "Core and CorePrep bind " ++ scalarTypeName selected ++ " instead of " ++ scalarTypeName distractor
      , exactTargetFor (scalarBranchFixture (scalarTypeName selected) (scalarTypeName distractor)) (scalarTypeToType selected)
      )
    | (selected, distractor) <- scalarPairs
    ]

scalarPairs :: [(ScalarType, ScalarType)]
scalarPairs =
    [ (selected, nextScalar selected)
    | selected <- scalarTypes
    ]
    where
        nextScalar value = case dropWhile (/= value) scalarTypes of
            _ : following : _ -> following
            _ -> case scalarTypes of
                first : _ -> first
                [] -> value

exactTargetFor :: String -> Type -> Bool
exactTargetFor source targetType = case compiled source of
    Right artifacts ->
        let core = artifactCore artifacts
            prepared = artifactCorePrep artifacts
            coreTargets = matchingTargets targetType core
            functionSymbols = namedFunctionSymbols "Select" targetType core
            preparedTargets = matchingCorePrepTargets targetType prepared
            preparedFunctions = namedCorePrepFunctionSymbols "Select" targetType prepared
         in singletonAgreement coreTargets functionSymbols
                && singletonAgreement preparedTargets preparedFunctions
                && coreTargets == preparedTargets
    Left _ -> False

singletonAgreement :: (Eq value) => [value] -> [value] -> Bool
singletonAgreement [first] [second] = first == second
singletonAgreement _ _ = False

compiled :: String -> Either [Diagnostic] FrontendArtifacts
compiled = compileToCorePrep . CompilerInput "overload-core.tala"

selectFunctions :: CoreModule -> [CoreFunction]
selectFunctions = namedFunctions "Select"

namedFunctions :: String -> CoreModule -> [CoreFunction]
namedFunctions spelling moduleValue =
    [ function
    | function <- coreModuleFunctions moduleValue
    , resolvedSpelling (coreFunctionName function) == Identifier spelling
    ]

namedFunctionSymbols :: String -> Type -> CoreModule -> [SymbolId]
namedFunctionSymbols spelling parameterType moduleValue =
    [ resolvedSymbol (coreFunctionName function)
    | function <- namedFunctions spelling moduleValue
    , [(_, actualType)] <- [coreFunctionParameters function]
    , actualType == parameterType
    ]

matchingTargets :: Type -> CoreModule -> [SymbolId]
matchingTargets targetType moduleValue =
    [ resolvedSymbol target
    | target <- matchingTargetsAny "Select" moduleValue
    , maybe False (== targetType) (callParameterType target moduleValue)
    ]

matchingTargetsAny :: String -> CoreModule -> [ResolvedName]
matchingTargetsAny spelling moduleValue = filter ((== Identifier spelling) . resolvedSpelling) (coreCallTargets moduleValue)

matchingCorePrepTargets :: Type -> CorePrepModule -> [SymbolId]
matchingCorePrepTargets targetType moduleValue =
    [ resolvedSymbol target
    | target <- corePrepCallTargets moduleValue
    , maybe False (== targetType) (corePrepParameterType target moduleValue)
    ]

namedCorePrepFunctionSymbols :: String -> Type -> CorePrepModule -> [SymbolId]
namedCorePrepFunctionSymbols spelling parameterType moduleValue =
    [ resolvedSymbol (corePrepFunctionName function)
    | function <- corePrepModuleFunctions moduleValue
    , resolvedSpelling (corePrepFunctionName function) == Identifier spelling
    , [(_, actualType)] <- [corePrepFunctionParameters function]
    , actualType == parameterType
    ]

callParameterType :: ResolvedName -> CoreModule -> Maybe Type
callParameterType target moduleValue = do
    function <- findCoreFunction target (coreModuleFunctions moduleValue)
    (_, parameterType) <- exactlyOneValue (coreFunctionParameters function)
    pure parameterType

corePrepParameterType :: ResolvedName -> CorePrepModule -> Maybe Type
corePrepParameterType target moduleValue = do
    function <- findCorePrepFunction target (corePrepModuleFunctions moduleValue)
    (_, parameterType) <- exactlyOneValue (corePrepFunctionParameters function)
    pure parameterType

findCoreFunction :: ResolvedName -> [CoreFunction] -> Maybe CoreFunction
findCoreFunction _ [] = Nothing
findCoreFunction requested (function : remaining)
    | resolvedSymbol requested == resolvedSymbol (coreFunctionName function) = Just function
    | otherwise = findCoreFunction requested remaining

findCorePrepFunction :: ResolvedName -> [CorePrepFunction] -> Maybe CorePrepFunction
findCorePrepFunction _ [] = Nothing
findCorePrepFunction requested (function : remaining)
    | resolvedSymbol requested == resolvedSymbol (corePrepFunctionName function) = Just function
    | otherwise = findCorePrepFunction requested remaining

callArgumentsFor :: String -> CoreModule -> [[CoreExpression]]
callArgumentsFor spelling moduleValue = concatMap (functionCalls . coreFunctionBody) (coreModuleFunctions moduleValue)
    where
        functionCalls = concatMap statementCalls
        statementCalls statement = case statement of
            CoreBind binding -> expressionCalls (coreBindingValue binding)
            CoreAssign _ value -> expressionCalls value
            CoreReturn value -> expressionCalls value
            CoreIf condition yes no -> expressionCalls condition ++ concatMap statementCalls yes ++ concatMap statementCalls no
            CoreEvaluate value -> expressionCalls value
            CoreWhile condition body -> expressionCalls condition ++ concatMap statementCalls body
            CoreDoWhile body condition -> concatMap statementCalls body ++ expressionCalls condition
            CoreFor condition updates body -> expressionCalls condition ++ concatMap statementCalls updates ++ concatMap statementCalls body
            CoreBreak -> []
            CoreContinue -> []
        expressionCalls expression = case expression of
            CoreVariable _ _ -> []
            CoreLiteral _ _ -> []
            CoreApply (CoreVariable target _) arguments _ ->
                (if resolvedSpelling target == Identifier spelling then [arguments] else [])
                    ++ concatMap expressionCalls arguments
            CoreApply callee arguments _ -> expressionCalls callee ++ concatMap expressionCalls arguments
            CorePrimitive _ arguments _ -> concatMap expressionCalls arguments
            CoreLet _ _ value body _ -> expressionCalls value ++ expressionCalls body
            CoreConditional condition whenTrue whenFalse _ -> concatMap expressionCalls [condition, whenTrue, whenFalse]
            CoreClosure _ _ _ statements _ -> concatMap statementCalls statements

callTypesFor :: String -> CoreModule -> [Type]
callTypesFor spelling moduleValue = concatMap (functionCallTypes . coreFunctionBody) (coreModuleFunctions moduleValue)
    where
        functionCallTypes = concatMap statementTypes
        statementTypes statement = case statement of
            CoreBind binding -> expressionTypes (coreBindingValue binding)
            CoreAssign _ value -> expressionTypes value
            CoreReturn value -> expressionTypes value
            CoreIf condition yes no -> expressionTypes condition ++ concatMap statementTypes yes ++ concatMap statementTypes no
            CoreEvaluate value -> expressionTypes value
            CoreWhile condition body -> expressionTypes condition ++ concatMap statementTypes body
            CoreDoWhile body condition -> concatMap statementTypes body ++ expressionTypes condition
            CoreFor condition updates body -> expressionTypes condition ++ concatMap statementTypes updates ++ concatMap statementTypes body
            CoreBreak -> []
            CoreContinue -> []
        expressionTypes expression = case expression of
            CoreVariable _ _ -> []
            CoreLiteral _ _ -> []
            CoreApply (CoreVariable target functionType) arguments _ ->
                (if resolvedSpelling target == Identifier spelling then [functionType] else [])
                    ++ concatMap expressionTypes arguments
            CoreApply callee arguments _ -> expressionTypes callee ++ concatMap expressionTypes arguments
            CorePrimitive _ arguments _ -> concatMap expressionTypes arguments
            CoreLet _ _ value body _ -> expressionTypes value ++ expressionTypes body
            CoreConditional condition whenTrue whenFalse _ -> concatMap expressionTypes [condition, whenTrue, whenFalse]
            CoreClosure _ _ _ statements _ -> concatMap statementTypes statements

coreCallTargets :: CoreModule -> [ResolvedName]
coreCallTargets moduleValue = concatMap (concatMap statementTargets . coreFunctionBody) (coreModuleFunctions moduleValue)

statementTargets :: CoreStatement -> [ResolvedName]
statementTargets statement = case statement of
    CoreBind binding -> expressionTargets (coreBindingValue binding)
    CoreAssign _ value -> expressionTargets value
    CoreReturn value -> expressionTargets value
    CoreIf condition yes no -> expressionTargets condition ++ concatMap statementTargets yes ++ concatMap statementTargets no
    CoreEvaluate value -> expressionTargets value
    CoreWhile condition body -> expressionTargets condition ++ concatMap statementTargets body
    CoreDoWhile body condition -> concatMap statementTargets body ++ expressionTargets condition
    CoreFor condition updates body -> expressionTargets condition ++ concatMap statementTargets updates ++ concatMap statementTargets body
    CoreBreak -> []
    CoreContinue -> []

expressionTargets :: CoreExpression -> [ResolvedName]
expressionTargets expression = case expression of
    CoreVariable {} -> []
    CoreLiteral {} -> []
    CoreApply callee arguments _ -> calledTarget callee ++ expressionTargets callee ++ concatMap expressionTargets arguments
    CorePrimitive _ arguments _ -> concatMap expressionTargets arguments
    CoreLet _ _ value body _ -> expressionTargets value ++ expressionTargets body
    CoreConditional condition whenTrue whenFalse _ -> concatMap expressionTargets [condition, whenTrue, whenFalse]
    CoreClosure _ _ _ body _ -> concatMap statementTargets body
    where
        calledTarget (CoreVariable name _) = [name]
        calledTarget _ = []

corePrepCallTargets :: CorePrepModule -> [ResolvedName]
corePrepCallTargets moduleValue = concatMap (concatMap blockTargets . corePrepFunctionBlocks) (corePrepModuleFunctions moduleValue)
    where
        blockTargets block = concatMap instructionTargets (corePrepBlockInstructions block)
        instructionTargets instruction = case instruction of
            CorePrepBind _ _ _ operation -> operationTargets operation
            CorePrepAssign _ _ -> []
            CorePrepEvaluate operation -> operationTargets operation
        operationTargets operation = case operation of
            CorePrepCall (CorePrepVariable name _) _ -> [name]
            CorePrepCall _ _ -> []
            CorePrepCopy _ -> []
            CorePrepPrimitive _ _ -> []
            CorePrepMakeClosure _ _ -> []

corePrepCallArgumentsFor :: String -> CorePrepModule -> [[CorePrepAtom]]
corePrepCallArgumentsFor spelling moduleValue = concatMap (concatMap blockArguments . corePrepFunctionBlocks) (corePrepModuleFunctions moduleValue)
    where
        blockArguments block = concatMap instructionArguments (corePrepBlockInstructions block)
        instructionArguments instruction = case instruction of
            CorePrepBind _ _ _ operation -> operationArguments operation
            CorePrepAssign _ _ -> []
            CorePrepEvaluate operation -> operationArguments operation
        operationArguments operation = case operation of
            CorePrepCall (CorePrepVariable target _) arguments
                | resolvedSpelling target == Identifier spelling -> [arguments]
            _ -> []

selectCorePrepFunctions :: CorePrepModule -> [CorePrepFunction]
selectCorePrepFunctions moduleValue =
    [ function
    | function <- corePrepModuleFunctions moduleValue
    , resolvedSpelling (corePrepFunctionName function) == Identifier "Select"
    ]

uniqueSymbols :: [SymbolId] -> Bool
uniqueSymbols symbols = length symbols == length (unique symbols)

unique :: (Eq value) => [value] -> [value]
unique [] = []
unique (value : remaining) = value : unique (filter (/= value) remaining)

count :: (Eq value) => value -> [value] -> Int
count requested = length . filter (== requested)

exactlyOneValue :: [value] -> Maybe value
exactlyOneValue values = case values of
    [value] -> Just value
    _ -> Nothing

fixture :: String
fixture =
    "class Catalog { public static int Select(long value) { if (value > 0) { return 1; } else { return 0; } } public static int Select(int value) { if (value > 0) { return value; } else { return 0; } } } class Caller { public static int Invoke(int value) { return Catalog.Select(value); } }"

branchFixture :: String
branchFixture =
    "class Catalog { public static int Select(long value) { if (value > 0) { return 1; } else { return 0; } } public static int Select(int value) { if (value > 0) { return value; } else { return 0; } } } class Caller { int Invoke(int value) { if (value > 0) { return Catalog.Select(value); } else { return 0; } } }"

repeatedCalls :: String
repeatedCalls =
    "class Catalog { public static int Select(long value) { if (value > 0) { return 1; } else { return 0; } } public static int Select(int value) { if (value > 0) { return value; } else { return 0; } } } class Caller { int Invoke(int value) { return Catalog.Select(value) + Catalog.Select(value); } }"

mixedCalls :: String
mixedCalls =
    "class Catalog { public static int Select(long value) { if (value > 0) { return 1; } else { return 0; } } public static int Select(int value) { if (value > 0) { return value; } else { return 0; } } } class Caller { int Invoke(int integer, long wider) { return Catalog.Select(integer) + Catalog.Select(wider); } }"

unqualifiedCall :: String
unqualifiedCall =
    "class Catalog { public static int Select(long value) { if (value > 0) { return 1; } else { return 0; } } public static int Select(int value) { if (value > 0) { return value; } else { return 0; } } int Invoke(int value) { return Select(value); } }"

twoArguments :: String
twoArguments =
    "class Catalog { public static int Select(int first, int second) { if (first > 0) { return second; } else { return 0; } } public static int Select(long first, long second) { if (first > 0) { return 1; } else { return 0; } } } class Caller { int Invoke(int left, int right) { return Catalog.Select(left, right); } }"

twoOwners :: String
twoOwners =
    "class First { public static int FirstRead(int value) { if (value > 0) { return value; } else { return 0; } } } class Second { public static int SecondRead(int value) { if (value > 0) { return value; } else { return 0; } } } class Caller { int Invoke(int value) { return First.FirstRead(value) + Second.SecondRead(value); } }"

scalarBranchFixture :: String -> String -> String
scalarBranchFixture selected distractor =
    "class Catalog { public static int Select("
        ++ distractor
        ++ " other) { "
        ++ distractor
        ++ " copy = other; if (other == copy) { return 1; } else { return 0; } } public static int Select("
        ++ selected
        ++ " value) { "
        ++ selected
        ++ " copy = value; if (value == copy) { return 2; } else { return 0; } } public static int Invoke("
        ++ selected
        ++ " value) { return Select(value); } }"