From 7bc0a7fc7ff66f6d24bf0c643d024b8e82223da2 Mon Sep 17 00:00:00 2001 From: Yura Lazarev Date: Mon, 27 Jul 2026 20:31:47 +0200 Subject: [PATCH 1/2] test(optimizer): specs for derived inline directives (#241) Pipeline-level specs for the directive derivation on specialization bindings: a binding shaped f a1..ak where f carries @inline arity=N inherits always-inline when k >= N and arity=(N-k) when under-applied, transitively through chains, never overriding an explicit directive. The Golden.DirectiveDerived.Test module and its hand-computed eval oracle are committed in the pre-derivation state: golden.ir/golden.lua show the specialization surviving as a shared binding with un-inlined call sites, so the implementing commit carries the reviewable diff of what derivation changes. The unit specs are red at this commit. --- .../PureScript/Backend/IR/Optimizer/Spec.hs | 175 +++++++ .../Golden.DirectiveDerived.Test/corefn.json | 1 + .../eval/.gitignore | 1 + .../eval/golden.txt | 4 + .../Golden.DirectiveDerived.Test/golden.ir | 437 ++++++++++++++++++ .../Golden.DirectiveDerived.Test/golden.lua | 24 + test/ps/src/Golden/DirectiveDerived/Test.purs | 38 ++ 7 files changed, 680 insertions(+) create mode 100644 test/ps/output/Golden.DirectiveDerived.Test/corefn.json create mode 100644 test/ps/output/Golden.DirectiveDerived.Test/eval/.gitignore create mode 100644 test/ps/output/Golden.DirectiveDerived.Test/eval/golden.txt create mode 100644 test/ps/output/Golden.DirectiveDerived.Test/golden.ir create mode 100644 test/ps/output/Golden.DirectiveDerived.Test/golden.lua create mode 100644 test/ps/src/Golden/DirectiveDerived/Test.purs diff --git a/test/Language/PureScript/Backend/IR/Optimizer/Spec.hs b/test/Language/PureScript/Backend/IR/Optimizer/Spec.hs index c128ced4..1c8d0705 100644 --- a/test/Language/PureScript/Backend/IR/Optimizer/Spec.hs +++ b/test/Language/PureScript/Backend/IR/Optimizer/Spec.hs @@ -3149,6 +3149,181 @@ spec = describe "IR Optimizer" do ) ] + describe "derives directives for specializations (issue #241)" do + let mainModule = moduleNameFromString "Main" + extern = moduleNameFromString "Extern" + g = refImported extern (Name "g") + checked = either (fail . show) pure . optimizedUberModuleChecked mempty + namesOf uber = + [ name + | Standalone (QName _ (Name name), _) ← + Linker.uberModuleBindings uber + ] + -- g a (g b (g c (g a …))) — an opaque chain nothing folds, far + -- over 'inlineSizeBudget', so only a directive can paste the + -- bindings built from it. + combineBody a b c = + foldr + (\v acc → application (application g v) acc) + c + (concat (replicate 12 [a, b, c])) + combineName = QName mainModule (Name "combine") + combineRef = refImported mainModule (Name "combine") + combineDef = + abstraction (paramNamed (Name "a")) $ + abstraction (paramNamed (Name "b")) $ + abstraction (paramNamed (Name "c")) $ + combineBody + (refLocal (Name "a")) + (refLocal (Name "b")) + (refLocal (Name "c")) + + it "marks a specialization saturated at the directed arity always" do + -- spec = combine 1 2 — applied to exactly the directed arity, so + -- every use of spec stands for a qualifying call of combine: the + -- derived always-inline directive pastes it at both sites with no + -- annotation of its own, and the folds collapse them. + let specName = QName mainModule (Name "spec") + specRef = refImported mainModule (Name "spec") + specDef = + application + (application combineRef (literalInt 1)) + (literalInt 2) + optimized ← + checked + Linker.UberModule + { uberModuleForeigns = [] + , uberModuleBindings = + [ Standalone (combineName, setAnn (Just (Arity 2)) combineDef) + , Standalone (specName, specDef) + ] + , uberModuleExports = + [ (Name "main1", application specRef (literalInt 7)) + , (Name "main2", application specRef (literalInt 8)) + ] + } + namesOf optimized `shouldBe` [] + Linker.uberModuleExports optimized + `shouldBe` [ + ( Name "main1" + , combineBody (literalInt 1) (literalInt 2) (literalInt 7) + ) + , + ( Name "main2" + , combineBody (literalInt 1) (literalInt 2) (literalInt 8) + ) + ] + + it "gives an under-applied specialization the decremented arity" do + -- partial = combine 1 — one argument short of the directed + -- arity: a site applying one more argument reconstructs a + -- qualifying combine call and pastes, while a bare use keeps the + -- shared binding pinned. + let partialName = QName mainModule (Name "partial") + partialRef = refImported mainModule (Name "partial") + partialDef = application combineRef (literalInt 1) + optimized ← + checked + Linker.UberModule + { uberModuleForeigns = [] + , uberModuleBindings = + [ Standalone (combineName, setAnn (Just (Arity 2)) combineDef) + , Standalone (partialName, partialDef) + ] + , uberModuleExports = + [ + ( Name "main1" + , application + (application partialRef (literalInt 5)) + (literalInt 6) + ) + , (Name "main2", partialRef) + ] + } + namesOf optimized `shouldBe` ["combine", "partial"] + Linker.uberModuleExports optimized + `shouldBe` [ + ( Name "main1" + , combineBody (literalInt 1) (literalInt 5) (literalInt 6) + ) + , (Name "main2", partialRef) + ] + + it "derives transitively through a chain of specializations" do + -- outer = combine 1 inherits arity=2; inner = outer 2 inherits + -- arity=1 from the derived entry, not an explicit one — the + -- chain resolves in one pass because a binding may only + -- reference earlier bindings. + let outerName = QName mainModule (Name "outer") + outerRef = refImported mainModule (Name "outer") + innerName = QName mainModule (Name "inner") + innerRef = refImported mainModule (Name "inner") + optimized ← + checked + Linker.UberModule + { uberModuleForeigns = [] + , uberModuleBindings = + [ Standalone (combineName, setAnn (Just (Arity 3)) combineDef) + , Standalone + (outerName, application combineRef (literalInt 1)) + , Standalone (innerName, application outerRef (literalInt 2)) + ] + , uberModuleExports = + [ + ( Name "main1" + , application + (application outerRef (literalInt 7)) + (literalInt 8) + ) + , (Name "main2", application innerRef (literalInt 9)) + , -- The second inner site keeps the binding multi-use, + -- so the settle phase cannot dissolve it before the + -- derivation reads its shape. + (Name "main3", application innerRef (literalInt 10)) + ] + } + namesOf optimized `shouldBe` [] + Linker.uberModuleExports optimized + `shouldBe` [ + ( Name "main1" + , combineBody (literalInt 1) (literalInt 7) (literalInt 8) + ) + , + ( Name "main2" + , combineBody (literalInt 1) (literalInt 2) (literalInt 9) + ) + , + ( Name "main3" + , combineBody (literalInt 1) (literalInt 2) (literalInt 10) + ) + ] + + it "never overrides an explicit directive on a specialization" do + -- The explicit never on the specialization wins over the arity + -- the shape would otherwise derive: no site pastes. + let partialName = QName mainModule (Name "partial") + partialRef = refImported mainModule (Name "partial") + partialDef = + setAnn (Just Never) (application combineRef (literalInt 1)) + site x y = + application + (application partialRef (literalInt x)) + (literalInt y) + optimized ← + checked + Linker.UberModule + { uberModuleForeigns = [] + , uberModuleBindings = + [ Standalone (combineName, setAnn (Just (Arity 2)) combineDef) + , Standalone (partialName, partialDef) + ] + , uberModuleExports = + [(Name "main1", site 5 6), (Name "main2", site 7 8)] + } + namesOf optimized `shouldBe` ["combine", "partial"] + Linker.uberModuleExports optimized + `shouldBe` [(Name "main1", site 5 6), (Name "main2", site 7 8)] + describe "keeps foreign module tables hoisted (issue #175)" do -- A foreign module's value table must stay a single shared binding: -- its export values (some of which are Lua table constructors with diff --git a/test/ps/output/Golden.DirectiveDerived.Test/corefn.json b/test/ps/output/Golden.DirectiveDerived.Test/corefn.json new file mode 100644 index 00000000..75ab02a8 --- /dev/null +++ b/test/ps/output/Golden.DirectiveDerived.Test/corefn.json @@ -0,0 +1 @@ +{"builtWith":"0.15.16","comments":[{"LineComment":" @inline combine arity=2"},{"LineComment":" @inline apply34 never"}],"decls":[{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"bindType":"NonRec","expression":{"abstraction":{"annotation":{"meta":{"metaType":"IsForeign"},"sourceSpan":{"end":[17,37],"start":[17,36]}},"type":"Var","value":{"identifier":"sub","moduleName":["Data","Ring"]}},"annotation":{"meta":{"metaType":"IsSyntheticApp"},"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"ringInt","moduleName":["Data","Ring"]}},"type":"App"},"identifier":"sub"},{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"bindType":"NonRec","expression":{"abstraction":{"abstraction":{"annotation":{"meta":{"metaType":"IsForeign"},"sourceSpan":{"end":[35,30],"start":[35,3]}},"type":"Var","value":{"identifier":"discard","moduleName":["Control","Bind"]}},"annotation":{"meta":{"metaType":"IsSyntheticApp"},"sourceSpan":{"end":[35,30],"start":[35,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"discardUnit","moduleName":["Control","Bind"]}},"type":"App"},"annotation":{"meta":{"metaType":"IsSyntheticApp"},"sourceSpan":{"end":[35,30],"start":[35,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"bindEffect","moduleName":["Effect"]}},"type":"App"},"identifier":"discard"},{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"bindType":"NonRec","expression":{"abstraction":{"annotation":{"meta":{"metaType":"IsForeign"},"sourceSpan":{"end":[35,12],"start":[35,8]}},"type":"Var","value":{"identifier":"show","moduleName":["Data","Show"]}},"annotation":{"meta":{"metaType":"IsSyntheticApp"},"sourceSpan":{"end":[35,29],"start":[35,8]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"showInt","moduleName":["Data","Show"]}},"type":"App"},"identifier":"show"},{"annotation":{"meta":null,"sourceSpan":{"end":[13,36],"start":[13,1]}},"bindType":"NonRec","expression":{"annotation":{"meta":null,"sourceSpan":{"end":[13,36],"start":[13,1]}},"argument":"a","body":{"annotation":{"meta":null,"sourceSpan":{"end":[13,36],"start":[13,1]}},"argument":"b","body":{"annotation":{"meta":null,"sourceSpan":{"end":[13,36],"start":[13,1]}},"argument":"c","body":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,4],"start":[15,3]}},"type":"Var","value":{"identifier":"c","sourcePos":[14,1]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,8],"start":[15,7]}},"type":"Var","value":{"identifier":"a","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,12],"start":[15,11]}},"type":"Literal","value":{"literalType":"IntLiteral","value":1}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,16],"start":[15,15]}},"type":"Var","value":{"identifier":"b","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,20],"start":[15,19]}},"type":"Literal","value":{"literalType":"IntLiteral","value":2}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,24],"start":[15,23]}},"type":"Var","value":{"identifier":"a","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,28],"start":[15,27]}},"type":"Literal","value":{"literalType":"IntLiteral","value":3}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,32],"start":[15,31]}},"type":"Var","value":{"identifier":"b","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,36],"start":[15,35]}},"type":"Literal","value":{"literalType":"IntLiteral","value":4}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,40],"start":[15,39]}},"type":"Var","value":{"identifier":"a","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,44],"start":[15,43]}},"type":"Literal","value":{"literalType":"IntLiteral","value":5}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,48],"start":[15,47]}},"type":"Var","value":{"identifier":"b","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,52],"start":[15,51]}},"type":"Literal","value":{"literalType":"IntLiteral","value":6}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,56],"start":[15,55]}},"type":"Var","value":{"identifier":"a","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,60],"start":[15,59]}},"type":"Literal","value":{"literalType":"IntLiteral","value":7}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,64],"start":[15,63]}},"type":"Var","value":{"identifier":"b","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,68],"start":[15,67]}},"type":"Literal","value":{"literalType":"IntLiteral","value":8}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[16,8],"start":[16,7]}},"type":"Var","value":{"identifier":"a","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[16,12],"start":[16,11]}},"type":"Literal","value":{"literalType":"IntLiteral","value":9}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[16,16],"start":[16,15]}},"type":"Var","value":{"identifier":"b","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[16,21],"start":[16,19]}},"type":"Literal","value":{"literalType":"IntLiteral","value":10}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[16,25],"start":[16,24]}},"type":"Var","value":{"identifier":"a","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[16,30],"start":[16,28]}},"type":"Literal","value":{"literalType":"IntLiteral","value":11}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[16,34],"start":[16,33]}},"type":"Var","value":{"identifier":"b","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[16,39],"start":[16,37]}},"type":"Literal","value":{"literalType":"IntLiteral","value":12}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[16,43],"start":[16,42]}},"type":"Var","value":{"identifier":"a","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[16,48],"start":[16,46]}},"type":"Literal","value":{"literalType":"IntLiteral","value":13}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[16,52],"start":[16,51]}},"type":"Var","value":{"identifier":"b","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[16,57],"start":[16,55]}},"type":"Literal","value":{"literalType":"IntLiteral","value":14}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[16,61],"start":[16,60]}},"type":"Var","value":{"identifier":"a","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[16,66],"start":[16,64]}},"type":"Literal","value":{"literalType":"IntLiteral","value":15}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[16,70],"start":[16,69]}},"type":"Var","value":{"identifier":"b","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[16,75],"start":[16,73]}},"type":"Literal","value":{"literalType":"IntLiteral","value":16}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[17,8],"start":[17,7]}},"type":"Var","value":{"identifier":"a","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[17,13],"start":[17,11]}},"type":"Literal","value":{"literalType":"IntLiteral","value":17}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[17,17],"start":[17,16]}},"type":"Var","value":{"identifier":"b","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[17,22],"start":[17,20]}},"type":"Literal","value":{"literalType":"IntLiteral","value":18}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[17,26],"start":[17,25]}},"type":"Var","value":{"identifier":"a","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[17,31],"start":[17,29]}},"type":"Literal","value":{"literalType":"IntLiteral","value":19}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[17,35],"start":[17,34]}},"type":"Var","value":{"identifier":"b","sourcePos":[14,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[15,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[17,40],"start":[17,38]}},"type":"Literal","value":{"literalType":"IntLiteral","value":20}},"type":"App"},"type":"Abs"},"type":"Abs"},"type":"Abs"},"identifier":"combine"},{"annotation":{"meta":null,"sourceSpan":{"end":[27,29],"start":[27,1]}},"bindType":"NonRec","expression":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[28,18],"start":[28,11]}},"type":"Var","value":{"identifier":"combine","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[28,20],"start":[28,11]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[28,20],"start":[28,19]}},"type":"Literal","value":{"literalType":"IntLiteral","value":1}},"type":"App"},"identifier":"oneOnly"},{"annotation":{"meta":null,"sourceSpan":{"end":[22,25],"start":[22,1]}},"bindType":"NonRec","expression":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[23,21],"start":[23,14]}},"type":"Var","value":{"identifier":"combine","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[23,23],"start":[23,14]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[23,23],"start":[23,22]}},"type":"Literal","value":{"literalType":"IntLiteral","value":1}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[23,25],"start":[23,14]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[23,25],"start":[23,24]}},"type":"Literal","value":{"literalType":"IntLiteral","value":2}},"type":"App"},"identifier":"onePlusTwo"},{"annotation":{"meta":null,"sourceSpan":{"end":[30,38],"start":[30,1]}},"bindType":"NonRec","expression":{"annotation":{"meta":null,"sourceSpan":{"end":[30,38],"start":[30,1]}},"argument":"f","body":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[31,14],"start":[31,13]}},"type":"Var","value":{"identifier":"f","sourcePos":[31,1]}},"annotation":{"meta":null,"sourceSpan":{"end":[31,16],"start":[31,13]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[31,16],"start":[31,15]}},"type":"Literal","value":{"literalType":"IntLiteral","value":3}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[31,18],"start":[31,13]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[31,18],"start":[31,17]}},"type":"Literal","value":{"literalType":"IntLiteral","value":4}},"type":"App"},"type":"Abs"},"identifier":"apply34"},{"annotation":{"meta":null,"sourceSpan":{"end":[33,20],"start":[33,1]}},"bindType":"NonRec","expression":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"discard","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[35,30],"start":[35,3]}},"argument":{"abstraction":{"annotation":{"meta":{"metaType":"IsForeign"},"sourceSpan":{"end":[35,6],"start":[35,3]}},"type":"Var","value":{"identifier":"log","moduleName":["Effect","Console"]}},"annotation":{"meta":null,"sourceSpan":{"end":[35,30],"start":[35,3]}},"argument":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"show","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[35,29],"start":[35,8]}},"argument":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[35,24],"start":[35,14]}},"type":"Var","value":{"identifier":"onePlusTwo","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[35,28],"start":[35,14]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[35,28],"start":[35,25]}},"type":"Literal","value":{"literalType":"IntLiteral","value":100}},"type":"App"},"type":"App"},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[35,30],"start":[35,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[35,30],"start":[35,3]}},"argument":"$__unused","body":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"discard","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[36,30],"start":[36,3]}},"argument":{"abstraction":{"annotation":{"meta":{"metaType":"IsForeign"},"sourceSpan":{"end":[36,6],"start":[36,3]}},"type":"Var","value":{"identifier":"log","moduleName":["Effect","Console"]}},"annotation":{"meta":null,"sourceSpan":{"end":[36,30],"start":[36,3]}},"argument":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"show","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[36,29],"start":[36,8]}},"argument":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[36,24],"start":[36,14]}},"type":"Var","value":{"identifier":"onePlusTwo","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[36,28],"start":[36,14]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[36,28],"start":[36,25]}},"type":"Literal","value":{"literalType":"IntLiteral","value":200}},"type":"App"},"type":"App"},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[36,30],"start":[36,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[36,30],"start":[36,3]}},"argument":"$__unused","body":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"discard","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[37,29],"start":[37,3]}},"argument":{"abstraction":{"annotation":{"meta":{"metaType":"IsForeign"},"sourceSpan":{"end":[37,6],"start":[37,3]}},"type":"Var","value":{"identifier":"log","moduleName":["Effect","Console"]}},"annotation":{"meta":null,"sourceSpan":{"end":[37,29],"start":[37,3]}},"argument":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"show","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[37,28],"start":[37,8]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[37,21],"start":[37,14]}},"type":"Var","value":{"identifier":"oneOnly","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[37,24],"start":[37,14]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[37,24],"start":[37,22]}},"type":"Literal","value":{"literalType":"IntLiteral","value":50}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[37,27],"start":[37,14]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[37,27],"start":[37,25]}},"type":"Literal","value":{"literalType":"IntLiteral","value":60}},"type":"App"},"type":"App"},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[37,29],"start":[37,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[37,29],"start":[37,3]}},"argument":"$__unused","body":{"abstraction":{"annotation":{"meta":{"metaType":"IsForeign"},"sourceSpan":{"end":[38,6],"start":[38,3]}},"type":"Var","value":{"identifier":"log","moduleName":["Effect","Console"]}},"annotation":{"meta":null,"sourceSpan":{"end":[38,31],"start":[38,3]}},"argument":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"show","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[38,30],"start":[38,8]}},"argument":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[38,21],"start":[38,14]}},"type":"Var","value":{"identifier":"apply34","moduleName":["Golden","DirectiveDerived","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[38,29],"start":[38,14]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[38,29],"start":[38,22]}},"type":"Var","value":{"identifier":"oneOnly","moduleName":["Golden","DirectiveDerived","Test"]}},"type":"App"},"type":"App"},"type":"App"},"type":"Abs"},"type":"App"},"type":"Abs"},"type":"App"},"type":"Abs"},"type":"App"},"identifier":"main"}],"exports":["combine","onePlusTwo","oneOnly","apply34","main"],"foreign":[],"imports":[{"annotation":{"meta":null,"sourceSpan":{"end":[38,31],"start":[3,1]}},"moduleName":["Control","Bind"]},{"annotation":{"meta":null,"sourceSpan":{"end":[38,31],"start":[3,1]}},"moduleName":["Data","Ring"]},{"annotation":{"meta":null,"sourceSpan":{"end":[38,31],"start":[3,1]}},"moduleName":["Data","Show"]},{"annotation":{"meta":null,"sourceSpan":{"end":[38,31],"start":[3,1]}},"moduleName":["Effect"]},{"annotation":{"meta":null,"sourceSpan":{"end":[38,31],"start":[3,1]}},"moduleName":["Effect","Console"]},{"annotation":{"meta":null,"sourceSpan":{"end":[38,31],"start":[3,1]}},"moduleName":["Golden","DirectiveDerived","Test"]},{"annotation":{"meta":null,"sourceSpan":{"end":[5,15],"start":[5,1]}},"moduleName":["Prelude"]},{"annotation":{"meta":null,"sourceSpan":{"end":[38,31],"start":[3,1]}},"moduleName":["Prim"]}],"moduleName":["Golden","DirectiveDerived","Test"],"modulePath":"src/Golden/DirectiveDerived/Test.purs","reExports":{},"sourceSpan":{"end":[38,31],"start":[3,1]}} \ No newline at end of file diff --git a/test/ps/output/Golden.DirectiveDerived.Test/eval/.gitignore b/test/ps/output/Golden.DirectiveDerived.Test/eval/.gitignore new file mode 100644 index 00000000..d2dc29bb --- /dev/null +++ b/test/ps/output/Golden.DirectiveDerived.Test/eval/.gitignore @@ -0,0 +1 @@ +actual.txt diff --git a/test/ps/output/Golden.DirectiveDerived.Test/eval/golden.txt b/test/ps/output/Golden.DirectiveDerived.Test/eval/golden.txt new file mode 100644 index 00000000..4b66945f --- /dev/null +++ b/test/ps/output/Golden.DirectiveDerived.Test/eval/golden.txt @@ -0,0 +1,4 @@ +-140 +-40 +-660 +-246 diff --git a/test/ps/output/Golden.DirectiveDerived.Test/golden.ir b/test/ps/output/Golden.DirectiveDerived.Test/golden.ir new file mode 100644 index 00000000..4afc069c --- /dev/null +++ b/test/ps/output/Golden.DirectiveDerived.Test/golden.ir @@ -0,0 +1,437 @@ +UberModule + { uberModuleBindings = + [ Standalone + ( QName + { qnameModuleName = ModuleName "Data.Show", qnameName = Name "foreign" + }, ForeignImport Nothing + ( ModuleName "Data.Show" ) ".spago/p/prelude/5718c84fdde6247749cb053e816df696c30fe691/src/Data/Show.purs" + [ ( Nothing, Name "showIntImpl" ) ] + ), Standalone + ( QName + { qnameModuleName = ModuleName "Data.Show", qnameName = Name "showIntImpl" + }, ObjectProp Nothing + ( Ref Nothing ( Imported ( ModuleName "Data.Show" ) ( Name "foreign" ) ) ) + ( PropName "showIntImpl" ) + ), Standalone + ( QName + { qnameModuleName = ModuleName "Effect.Console", qnameName = Name "foreign" + }, ForeignImport Nothing + ( ModuleName "Effect.Console" ) ".spago/p/console/f82835a0b873aafe6bd7b14dd30cc150553d4ab9/src/Effect/Console.purs" + [ ( Nothing, Name "log" ) ] + ), Standalone + ( QName + { qnameModuleName = ModuleName "Effect.Console", qnameName = Name "log" + }, ObjectProp Nothing + ( Ref Nothing ( Imported ( ModuleName "Effect.Console" ) ( Name "foreign" ) ) ) + ( PropName "log" ) + ), Standalone + ( QName + { qnameModuleName = ModuleName "Golden.DirectiveDerived.Test", qnameName = Name "combine" + }, AbsN + ( Just ( Arity 2 ) ) + ( ParamNamed Nothing ( Name "a" ) :| [] ) + ( AbsN Nothing + ( ParamNamed Nothing ( Name "b" ) :| [] ) + ( AbsN Nothing + ( ParamNamed Nothing ( Name "c" ) :| [] ) + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( Ref Nothing + ( Local + ( Name "c" ) + ) + ) + ( Ref Nothing + ( Local + ( Name "a" ) + ) + ) + ) + ( LiteralInt Nothing 1 ) + ) + ( Ref Nothing + ( Local + ( Name "b" ) + ) + ) + ) + ( LiteralInt Nothing 2 ) + ) + ( Ref Nothing + ( Local + ( Name "a" ) + ) + ) + ) + ( LiteralInt Nothing 3 ) + ) + ( Ref Nothing + ( Local + ( Name "b" ) + ) + ) + ) + ( LiteralInt Nothing 4 ) + ) + ( Ref Nothing + ( Local ( Name "a" ) ) + ) + ) + ( LiteralInt Nothing 5 ) + ) + ( Ref Nothing + ( Local ( Name "b" ) ) + ) + ) + ( LiteralInt Nothing 6 ) + ) + ( Ref Nothing + ( Local ( Name "a" ) ) + ) + ) + ( LiteralInt Nothing 7 ) + ) + ( Ref Nothing + ( Local ( Name "b" ) ) + ) + ) + ( LiteralInt Nothing 8 ) + ) + ( Ref Nothing ( Local ( Name "a" ) ) ) + ) + ( LiteralInt Nothing 9 ) + ) + ( Ref Nothing ( Local ( Name "b" ) ) ) + ) + ( LiteralInt Nothing 10 ) + ) + ( Ref Nothing ( Local ( Name "a" ) ) ) + ) + ( LiteralInt Nothing 11 ) + ) + ( Ref Nothing ( Local ( Name "b" ) ) ) + ) + ( LiteralInt Nothing 12 ) + ) + ( Ref Nothing ( Local ( Name "a" ) ) ) + ) + ( LiteralInt Nothing 13 ) + ) + ( Ref Nothing ( Local ( Name "b" ) ) ) + ) + ( LiteralInt Nothing 14 ) + ) + ( Ref Nothing ( Local ( Name "a" ) ) ) + ) + ( LiteralInt Nothing 15 ) + ) + ( Ref Nothing ( Local ( Name "b" ) ) ) + ) + ( LiteralInt Nothing 16 ) + ) + ( Ref Nothing ( Local ( Name "a" ) ) ) + ) + ( LiteralInt Nothing 17 ) + ) + ( Ref Nothing ( Local ( Name "b" ) ) ) + ) + ( LiteralInt Nothing 18 ) + ) + ( Ref Nothing ( Local ( Name "a" ) ) ) + ) + ( LiteralInt Nothing 19 ) + ) + ( Ref Nothing ( Local ( Name "b" ) ) ) + ) + ( LiteralInt Nothing 20 ) + ) + ) + ) + ), Standalone + ( QName + { qnameModuleName = ModuleName "Golden.DirectiveDerived.Test", qnameName = Name "oneOnly" + }, AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.DirectiveDerived.Test" ) ( Name "combine" ) ) + ) + ( LiteralInt Nothing 1 :| [] ) + ), Standalone + ( QName + { qnameModuleName = ModuleName "Golden.DirectiveDerived.Test", qnameName = Name "onePlusTwo" + }, AbsN Nothing + ( ParamNamed Nothing ( Name "c$298" ) :| [] ) + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( PrimBinOp Nothing PrimSub + ( Ref Nothing + ( Local + ( Name "c$298" ) + ) + ) + ( LiteralInt Nothing 1 ) + ) + ( LiteralInt Nothing 1 ) + ) + ( LiteralInt Nothing 2 ) + ) + ( LiteralInt Nothing 2 ) + ) + ( LiteralInt Nothing 1 ) + ) + ( LiteralInt Nothing 3 ) + ) + ( LiteralInt Nothing 2 ) + ) + ( LiteralInt Nothing 4 ) + ) + ( LiteralInt Nothing 1 ) + ) + ( LiteralInt Nothing 5 ) + ) + ( LiteralInt Nothing 2 ) + ) + ( LiteralInt Nothing 6 ) + ) + ( LiteralInt Nothing 1 ) + ) + ( LiteralInt Nothing 7 ) + ) + ( LiteralInt Nothing 2 ) + ) + ( LiteralInt Nothing 8 ) + ) + ( LiteralInt Nothing 1 ) + ) + ( LiteralInt Nothing 9 ) + ) + ( LiteralInt Nothing 2 ) + ) + ( LiteralInt Nothing 10 ) + ) + ( LiteralInt Nothing 1 ) + ) + ( LiteralInt Nothing 11 ) + ) + ( LiteralInt Nothing 2 ) + ) + ( LiteralInt Nothing 12 ) + ) + ( LiteralInt Nothing 1 ) + ) + ( LiteralInt Nothing 13 ) + ) + ( LiteralInt Nothing 2 ) + ) + ( LiteralInt Nothing 14 ) + ) + ( LiteralInt Nothing 1 ) + ) + ( LiteralInt Nothing 15 ) + ) + ( LiteralInt Nothing 2 ) + ) + ( LiteralInt Nothing 16 ) + ) + ( LiteralInt Nothing 1 ) + ) + ( LiteralInt Nothing 17 ) + ) + ( LiteralInt Nothing 2 ) + ) + ( LiteralInt Nothing 18 ) + ) + ( LiteralInt Nothing 1 ) + ) + ( LiteralInt Nothing 19 ) + ) + ( LiteralInt Nothing 2 ) + ) + ( LiteralInt Nothing 20 ) + ) + ), Standalone + ( QName + { qnameModuleName = ModuleName "Golden.DirectiveDerived.Test", qnameName = Name "apply34" + }, AbsN ( Just Never ) + ( ParamNamed Nothing ( Name "f" ) :| [] ) + ( AppN Nothing + ( AppN Nothing ( Ref Nothing ( Local ( Name "f" ) ) ) ( LiteralInt Nothing 3 :| [] ) ) + ( LiteralInt Nothing 4 :| [] ) + ) + ) + ], uberModuleForeigns = [], uberModuleExports = + [ + ( Name "combine", Ref Nothing + ( Imported ( ModuleName "Golden.DirectiveDerived.Test" ) ( Name "combine" ) ) + ), + ( Name "onePlusTwo", Ref Nothing + ( Imported ( ModuleName "Golden.DirectiveDerived.Test" ) ( Name "onePlusTwo" ) ) + ), + ( Name "oneOnly", Ref Nothing + ( Imported ( ModuleName "Golden.DirectiveDerived.Test" ) ( Name "oneOnly" ) ) + ), + ( Name "apply34", Ref Nothing + ( Imported ( ModuleName "Golden.DirectiveDerived.Test" ) ( Name "apply34" ) ) + ), + ( Name "main", AbsN Nothing + ( ParamUnused Nothing :| [] ) + ( Let Nothing + ( Standalone + ( Nothing, Name "_", AppN Nothing + ( AppN Nothing + ( Ref Nothing ( Imported ( ModuleName "Effect.Console" ) ( Name "log" ) ) ) + ( AppN Nothing + ( Ref Nothing ( Imported ( ModuleName "Data.Show" ) ( Name "showIntImpl" ) ) ) + ( AppN Nothing + ( Ref Nothing + ( Imported + ( ModuleName "Golden.DirectiveDerived.Test" ) + ( Name "onePlusTwo" ) + ) + ) + ( LiteralInt Nothing 100 :| [] ) :| [] + ) :| [] + ) + ) + ( Ref Nothing ( Imported ( ModuleName "Prim" ) ( Name "$magicDoRun" ) ) :| [] ) + ) :| + [ Standalone + ( Nothing, Name "_", AppN Nothing + ( AppN Nothing + ( Ref Nothing ( Imported ( ModuleName "Effect.Console" ) ( Name "log" ) ) ) + ( AppN Nothing + ( Ref Nothing ( Imported ( ModuleName "Data.Show" ) ( Name "showIntImpl" ) ) ) + ( AppN Nothing + ( Ref Nothing + ( Imported + ( ModuleName "Golden.DirectiveDerived.Test" ) + ( Name "onePlusTwo" ) + ) + ) + ( LiteralInt Nothing 200 :| [] ) :| [] + ) :| [] + ) + ) + ( Ref Nothing ( Imported ( ModuleName "Prim" ) ( Name "$magicDoRun" ) ) :| [] ) + ), Standalone + ( Nothing, Name "_", AppN Nothing + ( AppN Nothing + ( Ref Nothing ( Imported ( ModuleName "Effect.Console" ) ( Name "log" ) ) ) + ( AppN Nothing + ( Ref Nothing ( Imported ( ModuleName "Data.Show" ) ( Name "showIntImpl" ) ) ) + ( AppN Nothing + ( AppN Nothing + ( Ref Nothing + ( Imported + ( ModuleName "Golden.DirectiveDerived.Test" ) + ( Name "oneOnly" ) + ) + ) + ( LiteralInt Nothing 50 :| [] ) + ) + ( LiteralInt Nothing 60 :| [] ) :| [] + ) :| [] + ) + ) + ( Ref Nothing ( Imported ( ModuleName "Prim" ) ( Name "$magicDoRun" ) ) :| [] ) + ) + ] + ) + ( AppN Nothing + ( AppN Nothing + ( Ref Nothing ( Imported ( ModuleName "Effect.Console" ) ( Name "log" ) ) ) + ( AppN Nothing + ( Ref Nothing ( Imported ( ModuleName "Data.Show" ) ( Name "showIntImpl" ) ) ) + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.DirectiveDerived.Test" ) ( Name "apply34" ) ) + ) + ( Ref Nothing + ( Imported + ( ModuleName "Golden.DirectiveDerived.Test" ) + ( Name "oneOnly" ) + ) :| [] + ) :| [] + ) :| [] + ) + ) + ( Ref Nothing ( Imported ( ModuleName "Prim" ) ( Name "$magicDoRun" ) ) :| [] ) + ) + ) + ) + ] + } \ No newline at end of file diff --git a/test/ps/output/Golden.DirectiveDerived.Test/golden.lua b/test/ps/output/Golden.DirectiveDerived.Test/golden.lua new file mode 100644 index 00000000..5ba3bb68 --- /dev/null +++ b/test/ps/output/Golden.DirectiveDerived.Test/golden.lua @@ -0,0 +1,24 @@ +local Data_Show_foreign = { showIntImpl = function(n) return tostring(n) end } +local Data_Show_showIntImpl = Data_Show_foreign.showIntImpl +local Effect_Console_foreign = { + log = function(s) return function() print(s) end end +} +local Effect_Console_log = Effect_Console_foreign.log +local Golden_DirectiveDerived_Test_combine = function(a) + return function(b) + return function(c) + return c - a - 1 - b - 2 - a - 3 - b - 4 - a - 5 - b - 6 - a - 7 - b - 8 - a - 9 - b - 10 - a - 11 - b - 12 - a - 13 - b - 14 - a - 15 - b - 16 - a - 17 - b - 18 - a - 19 - b - 20 + end + end +end +local Golden_DirectiveDerived_Test_oneOnly = Golden_DirectiveDerived_Test_combine(1) +local Golden_DirectiveDerived_Test_onePlusTwo = function(c_S_0) + return c_S_0 - 1 - 1 - 2 - 2 - 1 - 3 - 2 - 4 - 1 - 5 - 2 - 6 - 1 - 7 - 2 - 8 - 1 - 9 - 2 - 10 - 1 - 11 - 2 - 12 - 1 - 13 - 2 - 14 - 1 - 15 - 2 - 16 - 1 - 17 - 2 - 18 - 1 - 19 - 2 - 20 +end +local Golden_DirectiveDerived_Test_apply34 = function(f) return f(3)(4) end +return (function() + local _ = Effect_Console_log(Data_Show_showIntImpl(Golden_DirectiveDerived_Test_onePlusTwo(100)))() + local _ = Effect_Console_log(Data_Show_showIntImpl(Golden_DirectiveDerived_Test_onePlusTwo(200)))() + local _ = Effect_Console_log(Data_Show_showIntImpl(Golden_DirectiveDerived_Test_oneOnly(50)(60)))() + return Effect_Console_log(Data_Show_showIntImpl(Golden_DirectiveDerived_Test_apply34(Golden_DirectiveDerived_Test_oneOnly)))() +end)() diff --git a/test/ps/src/Golden/DirectiveDerived/Test.purs b/test/ps/src/Golden/DirectiveDerived/Test.purs new file mode 100644 index 00000000..31207021 --- /dev/null +++ b/test/ps/src/Golden/DirectiveDerived/Test.purs @@ -0,0 +1,38 @@ +-- @inline combine arity=2 +-- @inline apply34 never +module Golden.DirectiveDerived.Test where + +import Prelude + +import Effect (Effect) +import Effect.Console (log) + +-- A body over the inliner's size budget: a subtraction chain headed by +-- the last parameter, so nothing folds until a site supplies it. Only +-- a directive can paste it. +combine :: Int -> Int -> Int -> Int +combine a b c = + c - a - 1 - b - 2 - a - 3 - b - 4 - a - 5 - b - 6 - a - 7 - b - 8 + - a - 9 - b - 10 - a - 11 - b - 12 - a - 13 - b - 14 - a - 15 - b - 16 + - a - 17 - b - 18 - a - 19 - b - 20 + +-- Saturated at the directed arity: derives always-inline, so each +-- applied site pastes the specialization and folds to a constant, with +-- no pragma on this binding. +onePlusTwo :: Int -> Int +onePlusTwo = combine 1 2 + +-- One argument short of the directed arity: derives arity=1 — applied +-- sites paste, while the bare use below keeps the shared binding. +oneOnly :: Int -> Int -> Int +oneOnly = combine 1 + +apply34 :: (Int -> Int -> Int) -> Int +apply34 f = f 3 4 + +main :: Effect Unit +main = do + log (show (onePlusTwo 100)) + log (show (onePlusTwo 200)) + log (show (oneOnly 50 60)) + log (show (apply34 oneOnly)) From ed2f72ec3d1c6b81c0958e137c3a60336b4faf1b Mon Sep 17 00:00:00 2001 From: Yura Lazarev Date: Mon, 27 Jul 2026 20:44:35 +0200 Subject: [PATCH 2/2] feat(optimizer): derive inline directives on specializations (#241) A top-level binding whose settled shape applies an @inline arity=N target to k arguments inherits arity=(N-k) while under-applied and always-inline once saturated: a site applying N-k more arguments to the binding reconstructs, after the paste, a qualifying site of the explicit directive, so the derivation is the compositional reading of the arity policy (Note [Derived inline directives]). The pipeline is split into two phases around the derivation point (OptimizerPhases): derivedInlinePolicy reads the module settled by the post-merge optimize+dce fixpoint (use-once specializations are already dissolved), and the lower phase runs under the extended policy, so derived arities join the uncurry veto and the arity call-site gate, and derived always entries are pasted by the post-uncurry fixpoint. One left-to-right fold over the bindings derives transitively and bounds the derivation; explicit root directives are never overridden. Golden.DirectiveDerived.Test goldens move from the shared-binding state committed with the specs to the derived state: the saturated specialization is pasted and folds to constants at both sites, the under-applied one pastes at its applied site and stays a pinned shared binding for the bare use. The hand-written eval oracle is unchanged. --- README.md | 8 + ...200000_unisay_derived_inline_directives.md | 13 + .../PureScript/Backend/IR/Optimizer.hs | 375 +++++++++++------- .../PureScript/Backend/IR/Optimizer/Spec.hs | 2 +- .../Golden.DirectiveDerived.Test/golden.ir | 70 +--- .../Golden.DirectiveDerived.Test/golden.lua | 9 +- 6 files changed, 283 insertions(+), 194 deletions(-) create mode 100644 changelog.d/20260727_200000_unisay_derived_inline_directives.md diff --git a/README.md b/README.md index 44746c34..56cb8831 100644 --- a/README.md +++ b/README.md @@ -208,3 +208,11 @@ target. Foreign bindings accept only whole-binding `always`/`never`/`default` (their implementation is opaque to the optimizer). Note that `spago run` re-invokes the backend without build-phase flags, so `--directives` (like all build flags) applies to `spago build` output, not to the `--run` re-link. + +Specializations of an `arity=N` target need no directives of their own: a +top-level binding that applies the target to `k` arguments — a hand-written +partial application, or the binding the PureScript compiler's CSE floats for +a repeated dictionary application — inherits `arity=(N-k)` while +under-applied and `always` once saturated, transitively through chains of +such bindings. An explicit directive on the specialization overrides the +derived one. diff --git a/changelog.d/20260727_200000_unisay_derived_inline_directives.md b/changelog.d/20260727_200000_unisay_derived_inline_directives.md new file mode 100644 index 00000000..d13812a3 --- /dev/null +++ b/changelog.d/20260727_200000_unisay_derived_inline_directives.md @@ -0,0 +1,13 @@ +### Added + +- Inline directives are now derived for specialization bindings (#241). A + top-level binding whose settled shape applies a directive-carrying + combinator to some of its arguments — the shape purs's common-subexpression + pass floats for repeated dictionary applications (`bind = Control.Bind.bind + bindStateT`), and the shape of a hand-written partial application — needs + no pragma of its own: with the combinator under `@inline f arity=N` and the + binding applying `k` arguments, the binding inherits `arity=(N-k)` when + under-applied and always-inline when saturated (`k >= N`), transitively + through chains of such bindings. A specialized combinator then inlines at + its qualifying call sites with no hand-written annotation, while explicit + directives on the specialization keep full precedence. diff --git a/lib/Language/PureScript/Backend/IR/Optimizer.hs b/lib/Language/PureScript/Backend/IR/Optimizer.hs index 527d4f0f..6ce88565 100644 --- a/lib/Language/PureScript/Backend/IR/Optimizer.hs +++ b/lib/Language/PureScript/Backend/IR/Optimizer.hs @@ -97,9 +97,12 @@ import Language.PureScript.Backend.IR.Uncurry (uncurryWorkerWrapper) import Language.PureScript.Backend.IR.Uniquify (uniquifyNames) optimizedUberModule ∷ DataTypes → UberModule → UberModule -optimizedUberModule dataTypes uber = - runSupply - (runSteps (optimizerPipeline dataTypes (collectInlinePolicy uber)) uber) +optimizedUberModule dataTypes uber = runSupply do + let policy = collectInlinePolicy uber + settled ← runSteps (settlePhase (optimizerPipeline dataTypes policy)) uber + -- See Note [Derived inline directives] + let extended = policy <> derivedInlinePolicy policy settled + runSteps (lowerPhase (optimizerPipeline dataTypes extended)) settled {- | 'optimizedUberModule' with every pass's contract checked by the linter, failing with the name of the offending pass. Used by the test @@ -107,12 +110,26 @@ suite always, and by the CLI behind the @--lint-ir@ flag. -} optimizedUberModuleChecked ∷ DataTypes → UberModule → Either PassCheckFailure UberModule -optimizedUberModuleChecked dataTypes uber = - runSupply - ( runStepsChecked - (optimizerPipeline dataTypes (collectInlinePolicy uber)) - uber - ) +optimizedUberModuleChecked dataTypes uber = runSupply $ runExceptT do + let policy = collectInlinePolicy uber + settled ← + ExceptT + (runStepsChecked (settlePhase (optimizerPipeline dataTypes policy)) uber) + -- See Note [Derived inline directives] + let extended = policy <> derivedInlinePolicy policy settled + ExceptT + (runStepsChecked (lowerPhase (optimizerPipeline dataTypes extended)) settled) + +{- | The optimizer pipeline, split at the directive-derivation point: +the settle phase brings every binding to the shape +'derivedInlinePolicy' reads, and the lower phase consumes the extended +policy. See Note [Derived inline directives] for why the split falls +exactly there. +-} +data OptimizerPhases = OptimizerPhases + { settlePhase ∷ [Step] + , lowerPhase ∷ [Step] + } {- | The IR optimization pipeline. The first argument is the data-type table collected from CoreFn ('collectDataDeclarations'), consulted by @@ -120,134 +137,139 @@ the exhaustiveness-driven rewrite ('removeUnreachableMatchDefault'); the second is the inlining policy collected once from the pristine module before any pass runs: later rewrites may strip annotations, so every directive keys off a name (see Note [Inline annotations and -inlining heuristics]). --} -optimizerPipeline ∷ DataTypes → InlinePolicy → [Step] -optimizerPipeline dataTypes policy = - [ -- The entry pass (issue #139): establishes the global-uniqueness - -- condition (GUC = 'UniqueBinders') that every - -- following pass requires and preserves. - RunPass uniquifyPass - , RunFixpoint "optimize+dce" (optimizePass :| [dcePass]) - , -- by merging foreign bindings into the main bindings, we can - -- unblock even more optimizations, e.g. inline foreign bindings. - RunPass mergeForeignsPass - , RunFixpoint "optimize+dce-post-merge" (optimizePass :| [dcePass]) - , -- Split curried bindings into n-ary workers and curried wrappers - -- and rewrite the saturated call sites to direct worker calls - -- (issue #24) — the early of the pass's two runs (the late one - -- closes the pipeline). Runs after the post-merge fixpoint, so - -- manifest arities are measured once inlining has settled. See - -- Language.PureScript.Backend.IR.Uncurry. - RunPass uncurryPass - , -- The post-uncurry fixpoint dead-code-eliminates wrappers with no - -- remaining references and reduces the n-ary redexes that pasting - -- a single-use worker into its one call site produces. - RunFixpoint "optimize+dce-post-uncurry" (optimizePass :| [dcePass]) - , -- Float a Let-bound value down into the single IfThenElse branch that - -- uses it (issue #136). Runs after DCE (a dead binding is simply gone, - -- never worth sinking) and outside any fixpoint: it preserves every - -- free-reference count, so the count-driven inline/DCE decisions stay - -- settled. A structural rule could still fire on the moved Let — e.g. - -- a sink can leave both branches of an IfThenElse alpha-equivalent - -- for removeIfWithEqualBranches — but chasing such second-order - -- opportunities is deliberately traded away against re-running the - -- whole fixpoint after the pass. Runs before magicDo/flattenDeepBinds - -- (which see the final placement of every Let). See - -- Language.PureScript.Backend.IR.FloatIn. - RunPass floatInPass - , -- Magic-do (issue #46) recognises Effect/ST chains by their canonical - -- heads (Note [Canonical Effect/ST heads]) and relies on the unique - -- naming established by 'uniquifyNames', which it preserves. It runs - -- after the optimize fixpoints so dissolution has exposed the - -- canonical heads at the use sites; the `local _ =` statements it - -- introduces for `discard` are effect runs, which the passes that - -- follow leave alone (see 'isEffectRun'). See - -- Language.PureScript.Backend.IR.MagicDo. - RunPass magicDoPass - , -- Budgeted call-site inlining of dictionary methods (issue #180), the - -- cure for non-Effect/ST monadic chains that compile to nested closure - -- chains. Runs only here, after magicDo has lowered the Effect/ST chains: - -- inlining a bind before that would leave magicDo a chain it can no - -- longer recognise. The inlined methods' constructor matches then meet - -- the case-of-known-constructor folds (#177/#213/#214), collapsing - -- Maybe/Either/Writer/State chains into straight-line code. Code growth - -- is bounded per paste by 'inlineSizeBudget' and per expression by the - -- growth veto (Note [Bounded call-site inlining growth]), which keeps - -- a chain whose pastes never collapse from unrolling. - -- - -- Call-pattern specialization (issue #208) rides in the same fixpoint: - -- a recursive binding whose recursion passes a known constructor at a - -- scrutinized parameter position gets an unboxed specialized copy, and - -- the next optimize round's constructor folds collapse the reboxes its - -- body carries. Each round mints one specialization layer, so the - -- fixpoint provides the bounded iteration a nested accumulator needs; - -- the per-binding cap in Language.PureScript.Backend.IR.SpecConstr - -- keeps the minting finite. - RunFixpoint "specialize+dce" (specializePass :| [dcePass, specConstrPass]) - , -- CPR worker/wrapper on results (issue #206): split every binding - -- whose every return path builds one fixed saturated constructor - -- into a worker returning the fields as Lua multiple values plus a - -- rebox wrapper, rewriting the let-bound deconstructing sites to - -- direct worker calls behind an in-place rebox. Runs after the - -- specialize fixpoint — the monadic chains are collapsed and the - -- call-pattern specializations are minted, so the constructor-tailed - -- candidates and their deconstructing sites are maximal — and before - -- shareAccessors/cse/flattenDeepBinds, which must tolerate, but never - -- create, the Values/LetValues nodes. See - -- Language.PureScript.Backend.IR.Cpr. - RunPass cprPass - , -- Cancel the reboxes the split planted: floatLetValuesFromLetRhs - -- surfaces each one as a let-bound constructor, where the - -- known-constructor folds meet the site's eliminating reads and - -- the product dissolves; dce then drops the wrappers whose every - -- site went to the worker directly. Same members as the fixpoint - -- above, so call-pattern specialization keeps firing on shapes the - -- cancellation exposes. - RunFixpoint - "specialize+dce-post-cpr" - (specializePass :| [dcePass, specConstrPass]) - , -- Rebuild sharing for the foreign-accessor reads that dissolution - -- and the call-site pastes above duplicated: a read surviving at - -- two or more sites is re-bound to its linker name, which stage-2 - -- promotion then turns into a chunk local (issue #248). Runs after - -- the last pass that can multiply reads and before the final - -- flattening. See 'shareForeignAccessors'. - RunPass shareAccessorsPass - , -- Rebuild sharing within one body for the pure repeats the pastes - -- above left behind (issue #183): alpha-equivalent effect-free - -- subexpressions of a block are hoisted into a shared Let. Runs - -- after the last duplicating pass and after share-accessors (so - -- accessor reads are already references to top-level names, not - -- grabbed per body here), and outside any fixpoint: the Deref tier - -- of inlineLocalBindings would paste hoisted projections straight - -- back. See Language.PureScript.Backend.IR.CSE. - RunPass csePass - , -- Flatten the remaining deeply-nested expression trees (issues #104, - -- #108): continuation/bind chains of any monad (lambda-lifted - -- into $kont helpers) and applicative/flipped-bind application - -- spines (A-normalised into $tmp locals). Runs after magicDo (which - -- consumes Effect/ST chains, leaving only non-Effect/ST ones) and - -- likewise consumes and preserves the unique naming. - -- See Language.PureScript.Backend.IR.FlattenDeepBinds. - RunPass flattenDeepBindsPass - , -- The late uncurry run (issue #200): the same pass again, now that - -- the two saturated-site families invisible to the early run exist — - -- the effect-run spines magicDo completed (f(a)(b)(run), saturating - -- the effect function's manifest chain, thunk parameter included) - -- and the saturated-by-construction $kont helpers minted by the - -- flattening above. Bindings the early run split are recognised and - -- left split (see the Rerun section in - -- Language.PureScript.Backend.IR.Uncurry). - RunPass uncurryLatePass - , -- Drop the wrappers the late run left unreferenced. A single dce - -- pass, deliberately not an optimize+dce fixpoint like the early - -- run's: optimize's use-once inlining would paste the $kont workers - -- (each is called exactly once) back into their call sites round by - -- round, re-nesting exactly what flattenDeepBinds just flattened. - RunPass dcePass - ] +inlining heuristics]). The result is phase-split so the caller can +extend the policy with derived directives between the phases +('OptimizerPhases'). +-} +optimizerPipeline ∷ DataTypes → InlinePolicy → OptimizerPhases +optimizerPipeline dataTypes policy = OptimizerPhases {settlePhase, lowerPhase} where + settlePhase = + [ -- The entry pass (issue #139): establishes the global-uniqueness + -- condition (GUC = 'UniqueBinders') that every + -- following pass requires and preserves. + RunPass uniquifyPass + , RunFixpoint "optimize+dce" (optimizePass :| [dcePass]) + , -- by merging foreign bindings into the main bindings, we can + -- unblock even more optimizations, e.g. inline foreign bindings. + RunPass mergeForeignsPass + , RunFixpoint "optimize+dce-post-merge" (optimizePass :| [dcePass]) + ] + lowerPhase = + [ -- Split curried bindings into n-ary workers and curried wrappers + -- and rewrite the saturated call sites to direct worker calls + -- (issue #24) — the early of the pass's two runs (the late one + -- closes the pipeline). Runs after the post-merge fixpoint, so + -- manifest arities are measured once inlining has settled. See + -- Language.PureScript.Backend.IR.Uncurry. + RunPass uncurryPass + , -- The post-uncurry fixpoint dead-code-eliminates wrappers with no + -- remaining references and reduces the n-ary redexes that pasting + -- a single-use worker into its one call site produces. + RunFixpoint "optimize+dce-post-uncurry" (optimizePass :| [dcePass]) + , -- Float a Let-bound value down into the single IfThenElse branch that + -- uses it (issue #136). Runs after DCE (a dead binding is simply gone, + -- never worth sinking) and outside any fixpoint: it preserves every + -- free-reference count, so the count-driven inline/DCE decisions stay + -- settled. A structural rule could still fire on the moved Let — e.g. + -- a sink can leave both branches of an IfThenElse alpha-equivalent + -- for removeIfWithEqualBranches — but chasing such second-order + -- opportunities is deliberately traded away against re-running the + -- whole fixpoint after the pass. Runs before magicDo/flattenDeepBinds + -- (which see the final placement of every Let). See + -- Language.PureScript.Backend.IR.FloatIn. + RunPass floatInPass + , -- Magic-do (issue #46) recognises Effect/ST chains by their canonical + -- heads (Note [Canonical Effect/ST heads]) and relies on the unique + -- naming established by 'uniquifyNames', which it preserves. It runs + -- after the optimize fixpoints so dissolution has exposed the + -- canonical heads at the use sites; the `local _ =` statements it + -- introduces for `discard` are effect runs, which the passes that + -- follow leave alone (see 'isEffectRun'). See + -- Language.PureScript.Backend.IR.MagicDo. + RunPass magicDoPass + , -- Budgeted call-site inlining of dictionary methods (issue #180), the + -- cure for non-Effect/ST monadic chains that compile to nested closure + -- chains. Runs only here, after magicDo has lowered the Effect/ST chains: + -- inlining a bind before that would leave magicDo a chain it can no + -- longer recognise. The inlined methods' constructor matches then meet + -- the case-of-known-constructor folds (#177/#213/#214), collapsing + -- Maybe/Either/Writer/State chains into straight-line code. Code growth + -- is bounded per paste by 'inlineSizeBudget' and per expression by the + -- growth veto (Note [Bounded call-site inlining growth]), which keeps + -- a chain whose pastes never collapse from unrolling. + -- + -- Call-pattern specialization (issue #208) rides in the same fixpoint: + -- a recursive binding whose recursion passes a known constructor at a + -- scrutinized parameter position gets an unboxed specialized copy, and + -- the next optimize round's constructor folds collapse the reboxes its + -- body carries. Each round mints one specialization layer, so the + -- fixpoint provides the bounded iteration a nested accumulator needs; + -- the per-binding cap in Language.PureScript.Backend.IR.SpecConstr + -- keeps the minting finite. + RunFixpoint "specialize+dce" (specializePass :| [dcePass, specConstrPass]) + , -- CPR worker/wrapper on results (issue #206): split every binding + -- whose every return path builds one fixed saturated constructor + -- into a worker returning the fields as Lua multiple values plus a + -- rebox wrapper, rewriting the let-bound deconstructing sites to + -- direct worker calls behind an in-place rebox. Runs after the + -- specialize fixpoint — the monadic chains are collapsed and the + -- call-pattern specializations are minted, so the constructor-tailed + -- candidates and their deconstructing sites are maximal — and before + -- shareAccessors/cse/flattenDeepBinds, which must tolerate, but never + -- create, the Values/LetValues nodes. See + -- Language.PureScript.Backend.IR.Cpr. + RunPass cprPass + , -- Cancel the reboxes the split planted: floatLetValuesFromLetRhs + -- surfaces each one as a let-bound constructor, where the + -- known-constructor folds meet the site's eliminating reads and + -- the product dissolves; dce then drops the wrappers whose every + -- site went to the worker directly. Same members as the fixpoint + -- above, so call-pattern specialization keeps firing on shapes the + -- cancellation exposes. + RunFixpoint + "specialize+dce-post-cpr" + (specializePass :| [dcePass, specConstrPass]) + , -- Rebuild sharing for the foreign-accessor reads that dissolution + -- and the call-site pastes above duplicated: a read surviving at + -- two or more sites is re-bound to its linker name, which stage-2 + -- promotion then turns into a chunk local (issue #248). Runs after + -- the last pass that can multiply reads and before the final + -- flattening. See 'shareForeignAccessors'. + RunPass shareAccessorsPass + , -- Rebuild sharing within one body for the pure repeats the pastes + -- above left behind (issue #183): alpha-equivalent effect-free + -- subexpressions of a block are hoisted into a shared Let. Runs + -- after the last duplicating pass and after share-accessors (so + -- accessor reads are already references to top-level names, not + -- grabbed per body here), and outside any fixpoint: the Deref tier + -- of inlineLocalBindings would paste hoisted projections straight + -- back. See Language.PureScript.Backend.IR.CSE. + RunPass csePass + , -- Flatten the remaining deeply-nested expression trees (issues #104, + -- #108): continuation/bind chains of any monad (lambda-lifted + -- into $kont helpers) and applicative/flipped-bind application + -- spines (A-normalised into $tmp locals). Runs after magicDo (which + -- consumes Effect/ST chains, leaving only non-Effect/ST ones) and + -- likewise consumes and preserves the unique naming. + -- See Language.PureScript.Backend.IR.FlattenDeepBinds. + RunPass flattenDeepBindsPass + , -- The late uncurry run (issue #200): the same pass again, now that + -- the two saturated-site families invisible to the early run exist — + -- the effect-run spines magicDo completed (f(a)(b)(run), saturating + -- the effect function's manifest chain, thunk parameter included) + -- and the saturated-by-construction $kont helpers minted by the + -- flattening above. Bindings the early run split are recognised and + -- left split (see the Rerun section in + -- Language.PureScript.Backend.IR.Uncurry). + RunPass uncurryLatePass + , -- Drop the wrappers the late run left unreferenced. A single dce + -- pass, deliberately not an optimize+dce fixpoint like the early + -- run's: optimize's use-once inlining would paste the $kont workers + -- (each is called exactly once) back into their call sites round by + -- round, re-nesting exactly what flattenDeepBinds just flattened. + RunPass dcePass + ] ctorTags ∷ CtorTagSets ctorTags = ctorTagSets dataTypes @@ -538,6 +560,87 @@ collectInlinePolicy UberModule {uberModuleBindings, uberModuleForeigns} = LiteralObject _ props → Just (depth, props) _ → Nothing +{- Note [Derived inline directives] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +A specialization binding names a directive-carrying combinator without +carrying a directive of its own: purs's common-subexpression pass +floats a repeated dictionary application to a top-level binding like +@bind = Control.Bind.bind bindStateT@, and a user writes the same shape +by hand as a top-level partial application. Annotating each such +binding is busywork the compiler can do itself, because the Arity +policy composes over application. Let @f@ carry @arity=N@ and let +@a = f x₁ … xₖ@: + + * k < N: a site applying @N − k@ more arguments to @a@ becomes, once + @a@'s right-hand side is pasted there, a site applying at least N + arguments to @f@ — exactly a qualifying site of the directive. So + @a@ inherits @arity=(N − k)@. + * k ≥ N: the right-hand side is itself already a qualifying call of + @f@, so every use of @a@ stands for the specialized value and the + binding inherits always-inline — the @arity=0@ reading of the same + arithmetic. + +Pasting a partial application re-evaluates its argument expressions per +site, exactly as pasting the target at a qualifying non-lambda site +does ('inlineSaturatedCall'): the explicit directive on the target is +what licenses the duplication, so no cheapness guard applies to the +arguments. + +The derivation reads the module settled by the post-merge optimize+dce +fixpoint ('settlePhase'), not the pristine input: a specialization +referenced once has already dissolved into its use site — deriving for +it would only pin what the use-once path inlines wholesale — and the +survivors carry their final shapes. It runs before the uncurry split, +so a derived arity joins 'uncurryVeto' like an explicit one, and +before the post-uncurry optimize+dce fixpoint, whose whole-binding +inlining ('withBinding') performs the always-inline pastes the +derivation emits; derived arities paste at qualifying call sites in +the specialize fixpoint exactly like explicit ones. + +One left-to-right fold derives transitively and bounds the work: a +standalone binding references only earlier bindings (the order +'withBinding' relies on), so a chain of specializations meets each +target's — possibly itself derived — arity before the alias naming it +is visited, and every binding is inspected exactly once. Only the +Arity policy seeds derivation: an @always@ target's bare-Ref alias is +deliberately kept as its materialization point (issue #171), and a +@never@ target needs no propagation — its own veto already covers +every site. An explicit root directive on a specialization is never +overridden. +-} + +{- | The directives derived from the settled shapes of specialization +bindings — the extension only, disjoint from the explicit policy it is +derived against and combined with by the caller. +See Note [Derived inline directives]. +-} +derivedInlinePolicy ∷ InlinePolicy → UberModule → InlinePolicy +derivedInlinePolicy explicit UberModule {uberModuleBindings} = + foldl' derive mempty uberModuleBindings + where + derive ∷ InlinePolicy → Grouping (QName, Exp) → InlinePolicy + derive derived = \case + Standalone (qname, expr) + | not (hasRootDirective qname) + , (Ref _ headName, args@(_ : _)) ← unwindApp expr + , Just target ← refQName headName + , Just arity ← + Map.lookup target (policyArity explicit) + <|> Map.lookup target (policyArity derived) → + derived <> case fromIntegral (length args) of + applied + | applied >= arity → + mempty {policyAlways = Set.singleton qname} + | otherwise → + mempty {policyArity = Map.singleton qname (arity - applied)} + _ → derived + + hasRootDirective ∷ QName → Bool + hasRootDirective qname = + qname `Set.member` policyAlways explicit + || qname `Set.member` policyNever explicit + || qname `Map.member` policyArity explicit + {- | Names the uncurry pass may not split: rewriting their call sites to @$w@ worker calls would hide those sites from the name-keyed policies. -} diff --git a/test/Language/PureScript/Backend/IR/Optimizer/Spec.hs b/test/Language/PureScript/Backend/IR/Optimizer/Spec.hs index 1c8d0705..e05a0652 100644 --- a/test/Language/PureScript/Backend/IR/Optimizer/Spec.hs +++ b/test/Language/PureScript/Backend/IR/Optimizer/Spec.hs @@ -3164,7 +3164,7 @@ spec = describe "IR Optimizer" do -- bindings built from it. combineBody a b c = foldr - (\v acc → application (application g v) acc) + (application . application g) c (concat (replicate 12 [a, b, c])) combineName = QName mainModule (Name "combine") diff --git a/test/ps/output/Golden.DirectiveDerived.Test/golden.ir b/test/ps/output/Golden.DirectiveDerived.Test/golden.ir index 4afc069c..6f89ca1d 100644 --- a/test/ps/output/Golden.DirectiveDerived.Test/golden.ir +++ b/test/ps/output/Golden.DirectiveDerived.Test/golden.ir @@ -195,9 +195,21 @@ UberModule ( LiteralInt Nothing 1 :| [] ) ), Standalone ( QName - { qnameModuleName = ModuleName "Golden.DirectiveDerived.Test", qnameName = Name "onePlusTwo" - }, AbsN Nothing - ( ParamNamed Nothing ( Name "c$298" ) :| [] ) + { qnameModuleName = ModuleName "Golden.DirectiveDerived.Test", qnameName = Name "apply34" + }, AbsN ( Just Never ) + ( ParamNamed Nothing ( Name "f" ) :| [] ) + ( AppN Nothing + ( AppN Nothing ( Ref Nothing ( Local ( Name "f" ) ) ) ( LiteralInt Nothing 3 :| [] ) ) + ( LiteralInt Nothing 4 :| [] ) + ) + ) + ], uberModuleForeigns = [], uberModuleExports = + [ + ( Name "combine", Ref Nothing + ( Imported ( ModuleName "Golden.DirectiveDerived.Test" ) ( Name "combine" ) ) + ), + ( Name "onePlusTwo", AbsN Nothing + ( ParamNamed Nothing ( Name "c$378" ) :| [] ) ( PrimBinOp Nothing PrimSub ( PrimBinOp Nothing PrimSub ( PrimBinOp Nothing PrimSub @@ -240,7 +252,7 @@ UberModule ( PrimBinOp Nothing PrimSub ( Ref Nothing ( Local - ( Name "c$298" ) + ( Name "c$378" ) ) ) ( LiteralInt Nothing 1 ) @@ -323,23 +335,6 @@ UberModule ) ( LiteralInt Nothing 20 ) ) - ), Standalone - ( QName - { qnameModuleName = ModuleName "Golden.DirectiveDerived.Test", qnameName = Name "apply34" - }, AbsN ( Just Never ) - ( ParamNamed Nothing ( Name "f" ) :| [] ) - ( AppN Nothing - ( AppN Nothing ( Ref Nothing ( Local ( Name "f" ) ) ) ( LiteralInt Nothing 3 :| [] ) ) - ( LiteralInt Nothing 4 :| [] ) - ) - ) - ], uberModuleForeigns = [], uberModuleExports = - [ - ( Name "combine", Ref Nothing - ( Imported ( ModuleName "Golden.DirectiveDerived.Test" ) ( Name "combine" ) ) - ), - ( Name "onePlusTwo", Ref Nothing - ( Imported ( ModuleName "Golden.DirectiveDerived.Test" ) ( Name "onePlusTwo" ) ) ), ( Name "oneOnly", Ref Nothing ( Imported ( ModuleName "Golden.DirectiveDerived.Test" ) ( Name "oneOnly" ) ) @@ -356,15 +351,7 @@ UberModule ( Ref Nothing ( Imported ( ModuleName "Effect.Console" ) ( Name "log" ) ) ) ( AppN Nothing ( Ref Nothing ( Imported ( ModuleName "Data.Show" ) ( Name "showIntImpl" ) ) ) - ( AppN Nothing - ( Ref Nothing - ( Imported - ( ModuleName "Golden.DirectiveDerived.Test" ) - ( Name "onePlusTwo" ) - ) - ) - ( LiteralInt Nothing 100 :| [] ) :| [] - ) :| [] + ( LiteralInt Nothing ( -140 ) :| [] ) :| [] ) ) ( Ref Nothing ( Imported ( ModuleName "Prim" ) ( Name "$magicDoRun" ) ) :| [] ) @@ -375,15 +362,7 @@ UberModule ( Ref Nothing ( Imported ( ModuleName "Effect.Console" ) ( Name "log" ) ) ) ( AppN Nothing ( Ref Nothing ( Imported ( ModuleName "Data.Show" ) ( Name "showIntImpl" ) ) ) - ( AppN Nothing - ( Ref Nothing - ( Imported - ( ModuleName "Golden.DirectiveDerived.Test" ) - ( Name "onePlusTwo" ) - ) - ) - ( LiteralInt Nothing 200 :| [] ) :| [] - ) :| [] + ( LiteralInt Nothing ( -40 ) :| [] ) :| [] ) ) ( Ref Nothing ( Imported ( ModuleName "Prim" ) ( Name "$magicDoRun" ) ) :| [] ) @@ -393,18 +372,7 @@ UberModule ( Ref Nothing ( Imported ( ModuleName "Effect.Console" ) ( Name "log" ) ) ) ( AppN Nothing ( Ref Nothing ( Imported ( ModuleName "Data.Show" ) ( Name "showIntImpl" ) ) ) - ( AppN Nothing - ( AppN Nothing - ( Ref Nothing - ( Imported - ( ModuleName "Golden.DirectiveDerived.Test" ) - ( Name "oneOnly" ) - ) - ) - ( LiteralInt Nothing 50 :| [] ) - ) - ( LiteralInt Nothing 60 :| [] ) :| [] - ) :| [] + ( LiteralInt Nothing ( -660 ) :| [] ) :| [] ) ) ( Ref Nothing ( Imported ( ModuleName "Prim" ) ( Name "$magicDoRun" ) ) :| [] ) diff --git a/test/ps/output/Golden.DirectiveDerived.Test/golden.lua b/test/ps/output/Golden.DirectiveDerived.Test/golden.lua index 5ba3bb68..56851ee5 100644 --- a/test/ps/output/Golden.DirectiveDerived.Test/golden.lua +++ b/test/ps/output/Golden.DirectiveDerived.Test/golden.lua @@ -12,13 +12,10 @@ local Golden_DirectiveDerived_Test_combine = function(a) end end local Golden_DirectiveDerived_Test_oneOnly = Golden_DirectiveDerived_Test_combine(1) -local Golden_DirectiveDerived_Test_onePlusTwo = function(c_S_0) - return c_S_0 - 1 - 1 - 2 - 2 - 1 - 3 - 2 - 4 - 1 - 5 - 2 - 6 - 1 - 7 - 2 - 8 - 1 - 9 - 2 - 10 - 1 - 11 - 2 - 12 - 1 - 13 - 2 - 14 - 1 - 15 - 2 - 16 - 1 - 17 - 2 - 18 - 1 - 19 - 2 - 20 -end local Golden_DirectiveDerived_Test_apply34 = function(f) return f(3)(4) end return (function() - local _ = Effect_Console_log(Data_Show_showIntImpl(Golden_DirectiveDerived_Test_onePlusTwo(100)))() - local _ = Effect_Console_log(Data_Show_showIntImpl(Golden_DirectiveDerived_Test_onePlusTwo(200)))() - local _ = Effect_Console_log(Data_Show_showIntImpl(Golden_DirectiveDerived_Test_oneOnly(50)(60)))() + local _ = Effect_Console_log(Data_Show_showIntImpl(-140))() + local _ = Effect_Console_log(Data_Show_showIntImpl(-40))() + local _ = Effect_Console_log(Data_Show_showIntImpl(-660))() return Effect_Console_log(Data_Show_showIntImpl(Golden_DirectiveDerived_Test_apply34(Golden_DirectiveDerived_Test_oneOnly)))() end)()