tala-compiler-0.1.0: test/MonomorphizationTests.hs
-- SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com>
-- SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-1.1
module MonomorphizationTests (monomorphizationTests) where
import Data.List (isInfixOf)
import Progmasoft.Tala.AST
import Progmasoft.Tala.Compiler
import Progmasoft.Tala.Core
import Progmasoft.Tala.Core.Monomorphization
import Progmasoft.Tala.Core.Specialization
monomorphizationTests :: [(String, Bool)]
monomorphizationTests =
[ ("plain scalar Core produces an empty specialization plan", plainModuleIsEmpty)
, ("parameterized function parameters become root demands", parameterRootIsDiscovered)
, ("parameterized function results become root demands", returnRootIsDiscovered)
, ("binding declarations become root demands", bindingTypeIsDiscovered)
, ("binding initializers retain a separate origin", bindingValueIsDiscovered)
, ("assignment values become root demands", assignmentValueIsDiscovered)
, ("returned expressions become root demands", returnValueIsDiscovered)
, ("if conditions preserve their type location", conditionLocationIsDiscovered)
, ("both if branches are traversed", branchLocationsAreDiscovered)
, ("evaluated expressions become root demands", evaluatedValueIsDiscovered)
, ("call result types become demands", callResultIsDiscovered)
, ("call target types are traversed", callTargetIsDiscovered)
, ("call arguments are traversed", callArgumentsAreDiscovered)
, ("primitive result types become demands", primitiveResultIsDiscovered)
, ("primitive operands are traversed", primitiveOperandsAreDiscovered)
, ("closure callable types are traversed", closureCallableIsDiscovered)
, ("closure capture types are traversed", closureCaptureTypeIsDiscovered)
, ("closure capture values are traversed", closureCaptureValueIsDiscovered)
, ("closure parameter types are traversed", closureParameterIsDiscovered)
, ("closure return types are traversed", closureReturnIsDiscovered)
, ("closure body types are traversed", closureBodyIsDiscovered)
, ("function type containers expose nested specializations", callableChildrenAreDiscovered)
, ("nested template types create dependency demands", nestedDependencyIsPlanned)
, ("deep template trees reach a fixed point", deepFixedPointIsPlanned)
, ("duplicate root types share one demand", duplicateRootsCoalesce)
, ("duplicate roots retain distinct origins", duplicateOriginsAreRetained)
, ("an identical root origin is not duplicated", identicalOriginsCoalesce)
, ("dependency and root origins may coexist", rootAndDependencyOriginsCoexist)
, ("dependency edges are unique", dependencyEdgesAreUnique)
, ("demand identifiers follow insertion order", demandIdsAreOrdered)
, ("demand lookup by id succeeds", demandLookupByIdWorks)
, ("demand lookup by structural type succeeds", demandLookupByTypeWorks)
, ("missing demand id returns Nothing", missingDemandIdIsAbsent)
, ("missing demand type returns Nothing", missingDemandTypeIsAbsent)
, ("all completed plan entries are complete", everyDemandCompletes)
, ("root occurrence statistics count sites", rootOccurrenceStatisticsWork)
, ("unique demand statistics count canonical types", uniqueDemandStatisticsWork)
, ("dependency edge statistics count graph edges", edgeStatisticsWork)
, ("cache hit statistics count repeated requests", cacheHitStatisticsWork)
, ("depth statistics report the deepest dependency", depthStatisticsWork)
, ("zero maximum demand count is invalid", zeroDemandLimitIsRejected)
, ("negative maximum depth is invalid", negativeDepthLimitIsRejected)
, ("zero maximum origin count is invalid", zeroOriginLimitIsRejected)
, ("unique demand limits fail deterministically", demandLimitIsEnforced)
, ("dependency depth limits fail deterministically", depthLimitIsEnforced)
, ("origin limits fail deterministically", originLimitIsEnforced)
, ("open type parameters are rejected", openTypeIsRejected)
, ("open value parameters are rejected", openValueIsRejected)
, ("malformed fixed arrays are rejected", malformedArrayIsRejected)
, ("negative fixed array sizes are rejected", negativeArrayIsRejected)
, ("invalid Unicode template values are rejected", invalidCharacterIsRejected)
, ("empty qualified names are rejected", emptyNameIsRejected)
, ("built-in arrays are specialization candidates", builtinArrayIsCandidate)
, ("dynamic System.Array is a specialization candidate", dynamicArrayIsCandidate)
, ("fixed System.Array is a specialization candidate", fixedArrayIsCandidate)
, ("plain names are not specialization candidates", plainNameIsNotCandidate)
, ("callable types are not standalone specialization candidates", callableIsNotCandidate)
, ("type variables are not standalone specialization candidates", variableIsNotCandidate)
, ("direct dependencies preserve argument indexes", dependencyIndexesAreStable)
, ("value arguments do not manufacture dependencies", valueArgumentsDoNotDepend)
, ("nested callable arguments expose their concrete children", callableDependencyIsVisible)
, ("location rendering includes semantic function identity", locationRendersFunction)
, ("location rendering includes statement and argument indexes", locationRendersPath)
, ("limit diagnostics explain their boundary", limitDiagnosticIsReadable)
, ("open-type diagnostics explain the source location", openDiagnosticIsReadable)
, ("compiler artifacts carry an empty plan for scalar source", scalarPipelineCarriesPlan)
, ("compiler artifacts carry fixed-array specialization demands", fixedArrayPipelineCarriesPlan)
, ("compiler artifacts preserve dynamic and fixed array identities", arrayOverloadsStayDistinct)
, ("discovery order follows function order", functionOrderIsStable)
, ("discovery order follows statement order", statementOrderIsStable)
, ("discovery order follows argument order", argumentOrderIsStable)
, ("Boolean value arguments remain valid demands", booleanValueIsAccepted)
, ("character value arguments remain valid demands", characterValueIsAccepted)
, ("integer and character arguments remain distinct", valueKindsStayDistinct)
, ("fixed array zero length is accepted", zeroLengthArrayIsAccepted)
, ("large integer template values remain exact", largeIntegerStaysExact)
, ("plan derivations support deterministic equality", repeatedPlanningIsEqual)
, ("plan Show output contains concrete identities", planShowContainsIdentity)
, ("completed planner output passes graph validation", completedGraphIsValid)
, ("graph validation rejects duplicate demand ids", duplicateDemandIdsAreRejected)
, ("graph validation rejects non-positive demand ids", nonPositiveDemandIdsAreRejected)
, ("graph validation rejects missing dependencies", missingDependenciesAreRejected)
, ("graph validation rejects duplicate dependency edges", duplicateDependenciesAreRejected)
, ("graph validation rejects self dependencies", selfDependenciesAreRejected)
, ("graph validation rejects queued output entries", queuedDemandsAreRejected)
, ("graph validation checks demand statistics", demandStatisticsAreValidated)
, ("graph validation checks edge statistics", edgeStatisticsAreValidated)
, ("graph validation checks depth statistics", depthStatisticsAreValidated)
, ("graph validation detects cycles", cyclesAreRejected)
, ("graph roots retain directly observed demands", graphRootsAreObserved)
, ("graph roots include child types also observed directly", observedChildrenAreRoots)
, ("graph leaves contain dependency-free specializations", graphLeavesAreTerminal)
, ("reverse graph query returns direct dependents", reverseEdgesAreQueryable)
, ("reachable dependency query walks transitively", transitiveDependenciesAreQueryable)
, ("reachable dependency query excludes its root", transitiveDependenciesExcludeRoot)
, ("missing dependency query is empty", missingDependencyQueryIsEmpty)
, ("emission order places nested children before parents", childrenEmitBeforeParents)
, ("emission order remains stable across runs", emissionOrderIsStable)
, ("demand trace includes identity and depth", traceIncludesIdentityAndDepth)
, ("demand trace includes origins", traceIncludesOrigins)
, ("demand trace includes dependencies", traceIncludesDependencies)
, ("unknown demand trace is explicit", unknownTraceIsExplicit)
, ("completed graph failures have a readable diagnostic", graphDiagnosticIsReadable)
]
plainModuleIsEmpty :: Bool
plainModuleIsEmpty = case planCoreMonomorphization (moduleWith [plainFunction]) of
Right plan -> null (monomorphizationDemands plan) && monomorphizationStatistics plan == emptyMonomorphizationStatistics
Left _ -> False
parameterRootIsDiscovered :: Bool
parameterRootIsDiscovered =
hasRootPath [FunctionParameterPath 0] dynamicArrayPlan
returnRootIsDiscovered :: Bool
returnRootIsDiscovered = case planCoreMonomorphization (moduleWith [functionWith [] dynamicIntArray []]) of
Right plan -> hasRootPath [FunctionReturnPath] plan
Left _ -> False
bindingTypeIsDiscovered :: Bool
bindingTypeIsDiscovered =
hasRootPath [StatementPath 0, BindingTypePath] bindingPlan
bindingValueIsDiscovered :: Bool
bindingValueIsDiscovered =
hasRootPath [StatementPath 0, BindingValuePath, ExpressionResultPath] bindingPlan
assignmentValueIsDiscovered :: Bool
assignmentValueIsDiscovered = case planCoreMonomorphization (moduleWith [functionWith [] unitType [CoreAssign localName dynamicLiteral]]) of
Right plan -> hasRootPath [StatementPath 0, AssignmentValuePath, ExpressionResultPath] plan
Left _ -> False
returnValueIsDiscovered :: Bool
returnValueIsDiscovered = case planCoreMonomorphization (moduleWith [functionWith [] dynamicIntArray [CoreReturn dynamicLiteral]]) of
Right plan -> hasRootPath [StatementPath 0, ReturnValuePath, ExpressionResultPath] plan
Left _ -> False
conditionLocationIsDiscovered :: Bool
conditionLocationIsDiscovered = case planCoreMonomorphization conditionModule of
Right plan -> hasRootPath [StatementPath 0, ConditionPath, ExpressionResultPath] plan
Left _ -> False
branchLocationsAreDiscovered :: Bool
branchLocationsAreDiscovered = case planCoreMonomorphization branchModule of
Right plan ->
hasRootPath [StatementPath 0, TrueBranchPath, StatementPath 0, EvaluatedValuePath, ExpressionResultPath] plan
&& hasRootPath [StatementPath 0, FalseBranchPath, StatementPath 0, EvaluatedValuePath, ExpressionResultPath] plan
Left _ -> False
evaluatedValueIsDiscovered :: Bool
evaluatedValueIsDiscovered = case planCoreMonomorphization (moduleWith [functionWith [] unitType [CoreEvaluate dynamicLiteral]]) of
Right plan -> hasRootPath [StatementPath 0, EvaluatedValuePath, ExpressionResultPath] plan
Left _ -> False
callResultIsDiscovered :: Bool
callResultIsDiscovered = case callPlan of
Right plan -> hasRootPath [StatementPath 0, EvaluatedValuePath, ExpressionResultPath] plan
Left _ -> False
callTargetIsDiscovered :: Bool
callTargetIsDiscovered = case callPlan of
Right plan -> hasRootPath [StatementPath 0, EvaluatedValuePath, CalleePath, ExpressionResultPath, ClosureReturnPath] plan
Left _ -> False
callArgumentsAreDiscovered :: Bool
callArgumentsAreDiscovered = case callPlan of
Right plan -> hasRootPath [StatementPath 0, EvaluatedValuePath, ArgumentPath 0, ExpressionResultPath] plan
Left _ -> False
primitiveResultIsDiscovered :: Bool
primitiveResultIsDiscovered = case primitivePlan of
Right plan -> hasRootPath [StatementPath 0, EvaluatedValuePath, ExpressionResultPath] plan
Left _ -> False
primitiveOperandsAreDiscovered :: Bool
primitiveOperandsAreDiscovered = case primitivePlan of
Right plan ->
hasRootPath [StatementPath 0, EvaluatedValuePath, PrimitiveOperandPath 0, ExpressionResultPath] plan
&& hasRootPath [StatementPath 0, EvaluatedValuePath, PrimitiveOperandPath 1, ExpressionResultPath] plan
Left _ -> False
closureCallableIsDiscovered :: Bool
closureCallableIsDiscovered = case closurePlan of
Right plan ->
hasRootPath [StatementPath 0, EvaluatedValuePath, ExpressionResultPath, ClosureParameterPath 0] plan
&& hasRootPath [StatementPath 0, EvaluatedValuePath, ExpressionResultPath, ClosureReturnPath] plan
Left _ -> False
closureCaptureTypeIsDiscovered :: Bool
closureCaptureTypeIsDiscovered = case closurePlan of
Right plan ->
hasRootPath
[StatementPath 0, EvaluatedValuePath, ClosureCapturePath 0, ClosureCaptureTypePath]
plan
Left _ -> False
closureCaptureValueIsDiscovered :: Bool
closureCaptureValueIsDiscovered = case closurePlan of
Right plan ->
hasRootPath
[StatementPath 0, EvaluatedValuePath, ClosureCapturePath 0, ClosureCaptureValuePath, ExpressionResultPath]
plan
Left _ -> False
closureParameterIsDiscovered :: Bool
closureParameterIsDiscovered = case closurePlan of
Right plan -> hasRootPath [StatementPath 0, EvaluatedValuePath, ClosureParameterPath 0] plan
Left _ -> False
closureReturnIsDiscovered :: Bool
closureReturnIsDiscovered = case closurePlan of
Right plan -> hasRootPath [StatementPath 0, EvaluatedValuePath, ClosureReturnPath] plan
Left _ -> False
closureBodyIsDiscovered :: Bool
closureBodyIsDiscovered = case closurePlan of
Right plan ->
hasRootPath
[StatementPath 0, EvaluatedValuePath, ClosureBodyPath, StatementPath 0, ReturnValuePath, ExpressionResultPath]
plan
Left _ -> False
callableChildrenAreDiscovered :: Bool
callableChildrenAreDiscovered = case planCoreMonomorphization callableModule of
Right plan -> demandTypes plan == [dynamicIntArray, fixedIntArray 4]
Left _ -> False
nestedDependencyIsPlanned :: Bool
nestedDependencyIsPlanned = case nestedPlan of
Right plan -> case monomorphizationDemands plan of
parent : child : _ -> monomorphizationDependencies parent == [monomorphizationDemandId child]
_ -> False
Left _ -> False
deepFixedPointIsPlanned :: Bool
deepFixedPointIsPlanned = case deepPlan of
Right plan ->
length (monomorphizationDemands plan) == 4
&& deepestSpecializationDemand (monomorphizationStatistics plan) == 3
Left _ -> False
duplicateRootsCoalesce :: Bool
duplicateRootsCoalesce = case duplicatePlan of
Right plan -> length (monomorphizationDemands plan) == 1
Left _ -> False
duplicateOriginsAreRetained :: Bool
duplicateOriginsAreRetained = case duplicatePlan of
Right plan -> case monomorphizationDemands plan of
[demand] -> length (monomorphizationOrigins demand) == 2
_ -> False
Left _ -> False
identicalOriginsCoalesce :: Bool
identicalOriginsCoalesce = case planCoreMonomorphization (moduleWith [functionWith [(localName, dynamicIntArray)] unitType []]) of
Right plan -> case monomorphizationDemands plan of
[demand] -> length (monomorphizationOrigins demand) == 1
_ -> False
Left _ -> False
rootAndDependencyOriginsCoexist :: Bool
rootAndDependencyOriginsCoexist = case rootAndDependencyPlan of
Right plan -> case findDemandByType dynamicIntArray plan of
Just demand -> any isRoot (monomorphizationOrigins demand) && any isDependency (monomorphizationOrigins demand)
Nothing -> False
Left _ -> False
dependencyEdgesAreUnique :: Bool
dependencyEdgesAreUnique = case repeatedChildPlan of
Right plan -> case monomorphizationDemands plan of
parent : _ -> length (monomorphizationDependencies parent) == 1
[] -> False
Left _ -> False
demandIdsAreOrdered :: Bool
demandIdsAreOrdered = case multiPlan of
Right plan -> map (demandIdValue . monomorphizationDemandId) (monomorphizationDemands plan) == [1, 2, 3]
Left _ -> False
demandLookupByIdWorks :: Bool
demandLookupByIdWorks = case multiPlan of
Right plan -> case findDemand (DemandId 2) plan of
Just demand -> specializationType (monomorphizationSpecialization demand) == fixedIntArray 2
Nothing -> False
Left _ -> False
demandLookupByTypeWorks :: Bool
demandLookupByTypeWorks = case multiPlan of
Right plan -> case findDemandByType (fixedIntArray 3) plan of
Just demand -> monomorphizationDemandId demand == DemandId 3
Nothing -> False
Left _ -> False
missingDemandIdIsAbsent :: Bool
missingDemandIdIsAbsent = case multiPlan of
Right plan -> findDemand (DemandId 99) plan == Nothing
Left _ -> False
missingDemandTypeIsAbsent :: Bool
missingDemandTypeIsAbsent = case multiPlan of
Right plan -> findDemandByType (fixedIntArray 99) plan == Nothing
Left _ -> False
everyDemandCompletes :: Bool
everyDemandCompletes = case deepPlan of
Right plan -> all ((== DemandComplete) . monomorphizationState) (monomorphizationDemands plan)
Left _ -> False
rootOccurrenceStatisticsWork :: Bool
rootOccurrenceStatisticsWork = case duplicatePlan of
Right plan -> discoveredRootOccurrences (monomorphizationStatistics plan) == 2
Left _ -> False
uniqueDemandStatisticsWork :: Bool
uniqueDemandStatisticsWork = case multiPlan of
Right plan -> uniqueSpecializationDemands (monomorphizationStatistics plan) == 3
Left _ -> False
edgeStatisticsWork :: Bool
edgeStatisticsWork = case deepPlan of
Right plan -> specializationDependencyEdges (monomorphizationStatistics plan) == 3
Left _ -> False
cacheHitStatisticsWork :: Bool
cacheHitStatisticsWork = case duplicatePlan of
Right plan -> specializationCacheHits (monomorphizationStatistics plan) == 1
Left _ -> False
depthStatisticsWork :: Bool
depthStatisticsWork = case deepPlan of
Right plan -> deepestSpecializationDemand (monomorphizationStatistics plan) == 3
Left _ -> False
zeroDemandLimitIsRejected :: Bool
zeroDemandLimitIsRejected = case planCoreMonomorphizationWith (MonomorphizationLimits 0 1 1) (moduleWith []) of
Left (InvalidMonomorphizationLimits _) -> True
_ -> False
negativeDepthLimitIsRejected :: Bool
negativeDepthLimitIsRejected = case planCoreMonomorphizationWith (MonomorphizationLimits 1 (-1) 1) (moduleWith []) of
Left (InvalidMonomorphizationLimits _) -> True
_ -> False
zeroOriginLimitIsRejected :: Bool
zeroOriginLimitIsRejected = case planCoreMonomorphizationWith (MonomorphizationLimits 1 1 0) (moduleWith []) of
Left (InvalidMonomorphizationLimits _) -> True
_ -> False
demandLimitIsEnforced :: Bool
demandLimitIsEnforced = case planCoreMonomorphizationWith (MonomorphizationLimits 2 8 8) multiModule of
Left (SpecializationLimitExceeded 2 _ _) -> True
_ -> False
depthLimitIsEnforced :: Bool
depthLimitIsEnforced = case planCoreMonomorphizationWith (MonomorphizationLimits 8 1 8) deepModule of
Left (DemandDepthExceeded 1 _ _) -> True
_ -> False
originLimitIsEnforced :: Bool
originLimitIsEnforced = case planCoreMonomorphizationWith (MonomorphizationLimits 8 8 1) duplicateModule of
Left (DemandOriginLimitExceeded 1 _ _) -> True
_ -> False
openTypeIsRejected :: Bool
openTypeIsRejected = case planCoreMonomorphization (moduleWith [functionWith [(localName, openBox)] unitType []]) of
Left (InvalidDemandType _ (OpenSpecialization [SymbolId 80])) -> True
_ -> False
openValueIsRejected :: Bool
openValueIsRejected = case planCoreMonomorphization (moduleWith [functionWith [(localName, openArray)] unitType []]) of
Left (InvalidDemandType _ (OpenSpecialization [SymbolId 81])) -> True
_ -> False
malformedArrayIsRejected :: Bool
malformedArrayIsRejected = case planCoreMonomorphization (moduleWith [functionWith [(localName, malformedArray)] unitType []]) of
Left (InvalidDemandType _ (InvalidSpecialization _)) -> True
_ -> False
negativeArrayIsRejected :: Bool
negativeArrayIsRejected = case planCoreMonomorphization (moduleWith [functionWith [(localName, fixedIntArray (-1))] unitType []]) of
Left (InvalidDemandType _ (InvalidSpecialization _)) -> True
_ -> False
invalidCharacterIsRejected :: Bool
invalidCharacterIsRejected = case planCoreMonomorphization (moduleWith [functionWith [(localName, characterBox 0xd800)] unitType []]) of
Left (InvalidDemandType _ (InvalidSpecialization _)) -> True
_ -> False
emptyNameIsRejected :: Bool
emptyNameIsRejected = case planCoreMonomorphization
(moduleWith [functionWith [(localName, NamedType (QualifiedName []) [typeArgument intType])] unitType []]) of
Left (InvalidDemandType _ (InvalidSpecialization _)) -> True
_ -> False
builtinArrayIsCandidate :: Bool
builtinArrayIsCandidate = specializationCandidate builtinIntArray
dynamicArrayIsCandidate :: Bool
dynamicArrayIsCandidate = specializationCandidate dynamicIntArray
fixedArrayIsCandidate :: Bool
fixedArrayIsCandidate = specializationCandidate (fixedIntArray 8)
plainNameIsNotCandidate :: Bool
plainNameIsNotCandidate = not (specializationCandidate intType)
callableIsNotCandidate :: Bool
callableIsNotCandidate = not (specializationCandidate (FunctionType [dynamicIntArray] dynamicIntArray))
variableIsNotCandidate :: Bool
variableIsNotCandidate = not (specializationCandidate (TypeVariable typeParameter))
dependencyIndexesAreStable :: Bool
dependencyIndexesAreStable =
directTypeDependencies pairType
== [(0, dynamicIntArray), (1, fixedIntArray 4)]
valueArgumentsDoNotDepend :: Bool
valueArgumentsDoNotDepend = directTypeDependencies (fixedIntArray 4) == []
callableDependencyIsVisible :: Bool
callableDependencyIsVisible =
directTypeDependencies (FunctionType [dynamicIntArray] (fixedIntArray 4))
== [(0, dynamicIntArray), (1, fixedIntArray 4)]
locationRendersFunction :: Bool
locationRendersFunction = "Run#1" `isInfixOf` renderDemandLocation (DemandLocation functionName [])
locationRendersPath :: Bool
locationRendersPath =
".statement[2].argument[3]"
`isInfixOf` renderDemandLocation (DemandLocation functionName [StatementPath 2, ArgumentPath 3])
limitDiagnosticIsReadable :: Bool
limitDiagnosticIsReadable = case planCoreMonomorphizationWith (MonomorphizationLimits 1 8 8) multiModule of
Left failure -> "specialization demand limit 1 exceeded" `isInfixOf` renderMonomorphizationError failure
Right _ -> False
openDiagnosticIsReadable :: Bool
openDiagnosticIsReadable = case planCoreMonomorphization (moduleWith [functionWith [(localName, openBox)] unitType []]) of
Left failure ->
"Run#1.parameter[0]" `isInfixOf` renderMonomorphizationError failure
&& "unbound template parameters [80]" `isInfixOf` renderMonomorphizationError failure
Right _ -> False
scalarPipelineCarriesPlan :: Bool
scalarPipelineCarriesPlan = case compileToCorePrep (CompilerInput "scalar.tala" scalarSource) of
Right artifacts -> null (monomorphizationDemands (artifactMonomorphizationPlan artifacts))
Left _ -> False
fixedArrayPipelineCarriesPlan :: Bool
fixedArrayPipelineCarriesPlan = case compileToCorePrep (CompilerInput "array.tala" fixedArraySource) of
Right artifacts ->
any
((== fixedIntArray 4) . specializationType . monomorphizationSpecialization)
(monomorphizationDemands (artifactMonomorphizationPlan artifacts))
Left _ -> False
arrayOverloadsStayDistinct :: Bool
arrayOverloadsStayDistinct = case compileToCorePrep (CompilerInput "arrays.tala" arrayOverloadSource) of
Right artifacts ->
let types = demandTypes (artifactMonomorphizationPlan artifacts)
in dynamicIntArray `elem` types && fixedIntArray 4 `elem` types
Left _ -> False
functionOrderIsStable :: Bool
functionOrderIsStable = case planCoreMonomorphization orderedFunctionsModule of
Right plan -> take 2 (demandTypes plan) == [fixedIntArray 1, fixedIntArray 2]
Left _ -> False
statementOrderIsStable :: Bool
statementOrderIsStable = case planCoreMonomorphization orderedStatementsModule of
Right plan -> take 2 (demandTypes plan) == [fixedIntArray 1, fixedIntArray 2]
Left _ -> False
argumentOrderIsStable :: Bool
argumentOrderIsStable = case planCoreMonomorphization orderedArgumentsModule of
Right plan -> take 2 (demandTypes plan) == [fixedIntArray 1, fixedIntArray 2]
Left _ -> False
booleanValueIsAccepted :: Bool
booleanValueIsAccepted = accepts (named "Flagged" [ValueTemplateArgument (BooleanTemplateValue True)])
characterValueIsAccepted :: Bool
characterValueIsAccepted = accepts (characterBox 65)
valueKindsStayDistinct :: Bool
valueKindsStayDistinct = case planCoreMonomorphization valueKindModule of
Right plan -> length (monomorphizationDemands plan) == 2
Left _ -> False
zeroLengthArrayIsAccepted :: Bool
zeroLengthArrayIsAccepted = accepts (fixedIntArray 0)
largeIntegerStaysExact :: Bool
largeIntegerStaysExact =
accepts (named "Wide" [ValueTemplateArgument (IntegerTemplateValue (2 ^ (200 :: Int)))])
repeatedPlanningIsEqual :: Bool
repeatedPlanningIsEqual = planCoreMonomorphization deepModule == planCoreMonomorphization deepModule
planShowContainsIdentity :: Bool
planShowContainsIdentity = case nestedPlan of
Right plan -> "System" `isInfixOf` show plan && "Array" `isInfixOf` show plan
Left _ -> False
completedGraphIsValid :: Bool
completedGraphIsValid = maybePlan deepPlan (null . validateDemandGraph)
duplicateDemandIdsAreRejected :: Bool
duplicateDemandIdsAreRejected = case planDemands deepPlan of
first : second : remaining ->
let duplicate = second {monomorphizationDemandId = monomorphizationDemandId first}
plan = replacePlanDemands deepPlan (first : duplicate : remaining)
in DuplicateDemandIdentifier (monomorphizationDemandId first) `elem` validateDemandGraph plan
_ -> False
nonPositiveDemandIdsAreRejected :: Bool
nonPositiveDemandIdsAreRejected = case planDemands nestedPlan of
first : remaining ->
let invalid = first {monomorphizationDemandId = DemandId 0}
plan = replacePlanDemands nestedPlan (invalid : remaining)
in NonPositiveDemandIdentifier (DemandId 0) `elem` validateDemandGraph plan
_ -> False
missingDependenciesAreRejected :: Bool
missingDependenciesAreRejected = case planDemands nestedPlan of
first : remaining ->
let invalid = first {monomorphizationDependencies = [DemandId 99]}
plan = replacePlanDemands nestedPlan (invalid : remaining)
in MissingDependency (monomorphizationDemandId first) (DemandId 99) `elem` validateDemandGraph plan
_ -> False
duplicateDependenciesAreRejected :: Bool
duplicateDependenciesAreRejected = case planDemands nestedPlan of
first : remaining -> case monomorphizationDependencies first of
dependency : _ ->
let invalid = first {monomorphizationDependencies = [dependency, dependency]}
plan = replacePlanDemands nestedPlan (invalid : remaining)
in DuplicateDependency (monomorphizationDemandId first) dependency `elem` validateDemandGraph plan
[] -> False
_ -> False
selfDependenciesAreRejected :: Bool
selfDependenciesAreRejected = case planDemands nestedPlan of
first : remaining ->
let owner = monomorphizationDemandId first
invalid = first {monomorphizationDependencies = [owner]}
plan = replacePlanDemands nestedPlan (invalid : remaining)
in SelfDependency owner `elem` validateDemandGraph plan
_ -> False
queuedDemandsAreRejected :: Bool
queuedDemandsAreRejected = case planDemands nestedPlan of
first : remaining ->
let invalid = first {monomorphizationState = DemandQueued}
plan = replacePlanDemands nestedPlan (invalid : remaining)
in IncompleteDemand (monomorphizationDemandId first) `elem` validateDemandGraph plan
_ -> False
demandStatisticsAreValidated :: Bool
demandStatisticsAreValidated = case nestedPlan of
Right plan ->
let statistics = (monomorphizationStatistics plan) {uniqueSpecializationDemands = 77}
invalid = plan {monomorphizationStatistics = statistics}
in StatisticsDemandCountMismatch 77 2 `elem` validateDemandGraph invalid
Left _ -> False
edgeStatisticsAreValidated :: Bool
edgeStatisticsAreValidated = case nestedPlan of
Right plan ->
let statistics = (monomorphizationStatistics plan) {specializationDependencyEdges = 77}
invalid = plan {monomorphizationStatistics = statistics}
in StatisticsEdgeCountMismatch 77 1 `elem` validateDemandGraph invalid
Left _ -> False
depthStatisticsAreValidated :: Bool
depthStatisticsAreValidated = case nestedPlan of
Right plan ->
let statistics = (monomorphizationStatistics plan) {deepestSpecializationDemand = 77}
invalid = plan {monomorphizationStatistics = statistics}
in StatisticsDepthMismatch 77 1 `elem` validateDemandGraph invalid
Left _ -> False
cyclesAreRejected :: Bool
cyclesAreRejected = case planDemands nestedPlan of
first : second : remaining ->
let firstId = monomorphizationDemandId first
secondId = monomorphizationDemandId second
left = first {monomorphizationDependencies = [secondId]}
right = second {monomorphizationDependencies = [firstId]}
plan = replacePlanDemands nestedPlan (left : right : remaining)
in any isCycle (validateDemandGraph plan)
_ -> False
where
isCycle (CyclicDemandPath _) = True
isCycle _ = False
graphRootsAreObserved :: Bool
graphRootsAreObserved = case nestedPlan of
Right plan -> map demandType (demandRoots plan) == [nestedArray]
Left _ -> False
observedChildrenAreRoots :: Bool
observedChildrenAreRoots = case rootAndDependencyPlan of
Right plan -> map demandType (demandRoots plan) == [nestedArray, dynamicIntArray]
Left _ -> False
graphLeavesAreTerminal :: Bool
graphLeavesAreTerminal = case deepPlan of
Right plan -> map demandType (demandLeaves plan) == [dynamicIntArray]
Left _ -> False
reverseEdgesAreQueryable :: Bool
reverseEdgesAreQueryable = case nestedPlan of
Right plan -> case findDemandByType dynamicIntArray plan of
Just child -> map demandType (dependentDemands (monomorphizationDemandId child) plan) == [nestedArray]
Nothing -> False
Left _ -> False
transitiveDependenciesAreQueryable :: Bool
transitiveDependenciesAreQueryable = case deepPlan of
Right plan -> case findDemandByType deepArray plan of
Just root -> length (reachableDependencies (monomorphizationDemandId root) plan) == 3
Nothing -> False
Left _ -> False
transitiveDependenciesExcludeRoot :: Bool
transitiveDependenciesExcludeRoot = case deepPlan of
Right plan -> case findDemandByType deepArray plan of
Just root -> root `notElem` reachableDependencies (monomorphizationDemandId root) plan
Nothing -> False
Left _ -> False
missingDependencyQueryIsEmpty :: Bool
missingDependencyQueryIsEmpty = maybePlan nestedPlan (null . reachableDependencies (DemandId 99))
childrenEmitBeforeParents :: Bool
childrenEmitBeforeParents = case deepPlan >>= mapGraphResult . specializationEmissionOrder of
Right demands -> map demandType demands == reverse (demandTypes (rightPlan deepPlan))
Left _ -> False
emissionOrderIsStable :: Bool
emissionOrderIsStable = case deepPlan of
Right plan -> specializationEmissionOrder plan == specializationEmissionOrder plan
Left _ -> False
traceIncludesIdentityAndDepth :: Bool
traceIncludesIdentityAndDepth = case nestedPlan of
Right plan ->
let trace = renderDemandTrace plan (DemandId 1)
in "System" `isInfixOf` trace && "depth=0" `isInfixOf` trace
Left _ -> False
traceIncludesOrigins :: Bool
traceIncludesOrigins = case nestedPlan of
Right plan -> "Run#1.parameter[0]" `isInfixOf` renderDemandTrace plan (DemandId 1)
Left _ -> False
traceIncludesDependencies :: Bool
traceIncludesDependencies = case nestedPlan of
Right plan -> "dependencies=[demand#2]" `isInfixOf` renderDemandTrace plan (DemandId 1)
Left _ -> False
unknownTraceIsExplicit :: Bool
unknownTraceIsExplicit = case nestedPlan of
Right plan -> renderDemandTrace plan (DemandId 99) == "unknown demand demand#99"
Left _ -> False
graphDiagnosticIsReadable :: Bool
graphDiagnosticIsReadable =
"completed specialization demand graph failed validation"
`isInfixOf` renderMonomorphizationError (InvalidCompletedDemandGraph ["SelfDependency (DemandId 1)"])
demandType :: MonomorphizationDemand -> Type
demandType = specializationType . monomorphizationSpecialization
planDemands :: Either MonomorphizationError MonomorphizationPlan -> [MonomorphizationDemand]
planDemands = maybe [] monomorphizationDemands . either (const Nothing) Just
replacePlanDemands ::
Either MonomorphizationError MonomorphizationPlan -> [MonomorphizationDemand] -> MonomorphizationPlan
replacePlanDemands result demands = case result of
Right plan -> plan {monomorphizationDemands = demands}
Left _ -> MonomorphizationPlan demands emptyMonomorphizationStatistics
maybePlan :: Either MonomorphizationError MonomorphizationPlan -> (MonomorphizationPlan -> Bool) -> Bool
maybePlan result predicate = case result of
Right plan -> predicate plan
Left _ -> False
mapGraphResult :: Either [DemandId] value -> Either MonomorphizationError value
mapGraphResult result = case result of
Right value -> Right value
Left _ -> Left (InternalMissingDemand "cycle")
hasRootPath :: [DemandPathStep] -> MonomorphizationPlan -> Bool
hasRootPath path plan =
any
(any matches . monomorphizationOrigins)
(monomorphizationDemands plan)
where
matches (RootDemand location) = demandLocationPath location == path
matches (DependencyDemand _ _) = False
demandTypes :: MonomorphizationPlan -> [Type]
demandTypes = map (specializationType . monomorphizationSpecialization) . monomorphizationDemands
accepts :: Type -> Bool
accepts valueType = case planCoreMonomorphization (moduleWith [functionWith [(localName, valueType)] unitType []]) of
Right _ -> True
Left _ -> False
isRoot :: DemandOrigin -> Bool
isRoot (RootDemand _) = True
isRoot _ = False
isDependency :: DemandOrigin -> Bool
isDependency (DependencyDemand _ _) = True
isDependency _ = False
moduleWith :: [CoreFunction] -> CoreModule
moduleWith functions = CoreModuleWithSources (QualifiedName [Identifier "Tests"]) functions [] []
functionWith :: [(ResolvedName, Type)] -> Type -> [CoreStatement] -> CoreFunction
functionWith = CoreFunction functionName
plainFunction :: CoreFunction
plainFunction = functionWith [(localName, intType)] unitType [CoreReturn (CoreLiteral CoreUnit unitType)]
dynamicArrayPlan :: MonomorphizationPlan
dynamicArrayPlan = rightPlan (planCoreMonomorphization (moduleWith [functionWith [(localName, dynamicIntArray)] unitType []]))
bindingPlan :: MonomorphizationPlan
bindingPlan =
rightPlan
( planCoreMonomorphization
(moduleWith [functionWith [] unitType [CoreBind (CoreBinding localName dynamicIntArray False dynamicLiteral)]])
)
conditionModule :: CoreModule
conditionModule =
moduleWith
[ functionWith
[]
unitType
[CoreIf (CoreLiteral (CoreString "condition") dynamicIntArray) [] []]
]
branchModule :: CoreModule
branchModule =
moduleWith
[ functionWith
[]
unitType
[ CoreIf
(CoreLiteral (CoreBoolean True) boolType)
[CoreEvaluate dynamicLiteral]
[CoreEvaluate fixedLiteral]
]
]
callPlan :: Either MonomorphizationError MonomorphizationPlan
callPlan = planCoreMonomorphization callModule
callModule :: CoreModule
callModule =
moduleWith
[ functionWith
[]
unitType
[ CoreEvaluate
( CoreApply
(CoreVariable calleeName (FunctionType [dynamicIntArray] (fixedIntArray 4)))
[dynamicLiteral]
(fixedIntArray 4)
)
]
]
primitivePlan :: Either MonomorphizationError MonomorphizationPlan
primitivePlan =
planCoreMonomorphization
( moduleWith
[ functionWith
[]
unitType
[CoreEvaluate (CorePrimitive CoreAdd [dynamicLiteral, fixedLiteral] dynamicIntArray)]
]
)
closurePlan :: Either MonomorphizationError MonomorphizationPlan
closurePlan = planCoreMonomorphization closureModule
closureModule :: CoreModule
closureModule =
moduleWith
[ functionWith
[]
unitType
[ CoreEvaluate
( CoreClosure
[CoreCapture StrongCapture capturedValueName dynamicIntArray dynamicLiteral]
[(closureParameterName, fixedIntArray 4)]
dynamicIntArray
[CoreReturn dynamicLiteral]
(FunctionType [fixedIntArray 4] dynamicIntArray)
)
]
]
callableModule :: CoreModule
callableModule =
moduleWith
[functionWith [(localName, FunctionType [dynamicIntArray] (fixedIntArray 4))] unitType []]
nestedPlan :: Either MonomorphizationError MonomorphizationPlan
nestedPlan = planCoreMonomorphization nestedModule
nestedModule :: CoreModule
nestedModule = moduleWith [functionWith [(localName, nestedArray)] unitType []]
deepPlan :: Either MonomorphizationError MonomorphizationPlan
deepPlan = planCoreMonomorphization deepModule
deepModule :: CoreModule
deepModule = moduleWith [functionWith [(localName, deepArray)] unitType []]
duplicatePlan :: Either MonomorphizationError MonomorphizationPlan
duplicatePlan = planCoreMonomorphization duplicateModule
duplicateModule :: CoreModule
duplicateModule = moduleWith [functionWith [(localName, dynamicIntArray), (secondName, dynamicIntArray)] unitType []]
rootAndDependencyPlan :: Either MonomorphizationError MonomorphizationPlan
rootAndDependencyPlan =
planCoreMonomorphization
(moduleWith [functionWith [(localName, nestedArray), (secondName, dynamicIntArray)] unitType []])
repeatedChildPlan :: Either MonomorphizationError MonomorphizationPlan
repeatedChildPlan =
planCoreMonomorphization
( moduleWith
[functionWith [(localName, named "Pair" [typeArgument dynamicIntArray, typeArgument dynamicIntArray])] unitType []]
)
multiPlan :: Either MonomorphizationError MonomorphizationPlan
multiPlan = planCoreMonomorphization multiModule
multiModule :: CoreModule
multiModule =
moduleWith
[functionWith [(localName, fixedIntArray 1), (secondName, fixedIntArray 2), (thirdName, fixedIntArray 3)] unitType []]
orderedFunctionsModule :: CoreModule
orderedFunctionsModule =
moduleWith
[ CoreFunction functionName [(localName, fixedIntArray 1)] unitType []
, CoreFunction secondFunctionName [(localName, fixedIntArray 2)] unitType []
]
orderedStatementsModule :: CoreModule
orderedStatementsModule =
moduleWith
[ functionWith
[]
unitType
[CoreEvaluate (CoreLiteral CoreUnit (fixedIntArray 1)), CoreEvaluate (CoreLiteral CoreUnit (fixedIntArray 2))]
]
orderedArgumentsModule :: CoreModule
orderedArgumentsModule =
moduleWith
[ functionWith
[]
unitType
[ CoreEvaluate
( CoreApply
(CoreVariable calleeName (FunctionType [fixedIntArray 1, fixedIntArray 2] unitType))
[CoreLiteral CoreUnit (fixedIntArray 1), CoreLiteral CoreUnit (fixedIntArray 2)]
unitType
)
]
]
valueKindModule :: CoreModule
valueKindModule =
moduleWith
[ functionWith
[(localName, named "Code" [ValueTemplateArgument (IntegerTemplateValue 65)])]
(named "Code" [ValueTemplateArgument (CharacterTemplateValue 65)])
[]
]
dynamicLiteral :: CoreExpression
dynamicLiteral = CoreLiteral (CoreString "dynamic") dynamicIntArray
fixedLiteral :: CoreExpression
fixedLiteral = CoreLiteral (CoreString "fixed") (fixedIntArray 4)
dynamicIntArray :: Type
dynamicIntArray = namedQualified ["System", "Array"] [typeArgument intType]
fixedIntArray :: Integer -> Type
fixedIntArray size =
namedQualified
["System", "Array"]
[typeArgument intType, ValueTemplateArgument (IntegerTemplateValue size)]
builtinIntArray :: Type
builtinIntArray = named "[]" [typeArgument intType]
nestedArray :: Type
nestedArray = namedQualified ["System", "Array"] [typeArgument dynamicIntArray]
deepArray :: Type
deepArray = foldr (\_ nested -> named "Box" [typeArgument nested]) dynamicIntArray [1 :: Int .. 3]
pairType :: Type
pairType = named "Pair" [typeArgument dynamicIntArray, typeArgument (fixedIntArray 4)]
openBox :: Type
openBox = named "Box" [typeArgument (TypeVariable typeParameter)]
openArray :: Type
openArray =
namedQualified
["System", "Array"]
[typeArgument intType, ValueTemplateArgument (TemplateValueParameter valueParameter)]
malformedArray :: Type
malformedArray = namedQualified ["System", "Array"] [ValueTemplateArgument (IntegerTemplateValue 4)]
characterBox :: Integer -> Type
characterBox scalar = named "Character" [ValueTemplateArgument (CharacterTemplateValue scalar)]
typeArgument :: Type -> TemplateArgument
typeArgument = TypeTemplateArgument
named :: String -> [TemplateArgument] -> Type
named value = namedQualified [value]
namedQualified :: [String] -> [TemplateArgument] -> Type
namedQualified parts = NamedType (QualifiedName (map Identifier parts))
functionName
, secondFunctionName
, localName
, secondName
, thirdName
, calleeName
, capturedValueName
, closureParameterName ::
ResolvedName
functionName = resolved 1 "Run"
secondFunctionName = resolved 2 "Next"
localName = resolved 10 "value"
secondName = resolved 11 "second"
thirdName = resolved 12 "third"
calleeName = resolved 20 "callee"
capturedValueName = resolved 21 "captured"
closureParameterName = resolved 22 "argument"
typeParameter, valueParameter :: ResolvedName
typeParameter = resolved 80 "T"
valueParameter = resolved 81 "N"
resolved :: Int -> String -> ResolvedName
resolved symbol spelling = ResolvedName (SymbolId symbol) (Identifier spelling)
rightPlan :: Either MonomorphizationError MonomorphizationPlan -> MonomorphizationPlan
rightPlan result = case result of
Right plan -> plan
Left _ -> MonomorphizationPlan [] emptyMonomorphizationStatistics
scalarSource :: String
scalarSource =
unlines
[ "namespace Tests;"
, "class Program {"
, " public static void Main() {"
, " return;"
, " }"
, "}"
]
fixedArraySource :: String
fixedArraySource =
unlines
[ "namespace Tests;"
, "class Program {"
, " public static void Consume(_ [int; 4] values) {"
, " return;"
, " }"
, "}"
]
arrayOverloadSource :: String
arrayOverloadSource =
unlines
[ "namespace Tests;"
, "class Program {"
, " public static void Consume(_ [int] dynamicValues, _ [int; 4] fixedValues) {"
, " return;"
, " }"
, "}"
]