@harmoniclabs/pebble 0.1.10 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/IR/CompilationCtx.d.ts +40 -0
- package/dist/IR/CompilationCtx.js +54 -0
- package/dist/IR/IRHash.d.ts +23 -2
- package/dist/IR/IRHash.js +10 -60
- package/dist/IR/IRNodes/IRConst.js +35 -4
- package/dist/IR/IRNodes/IRHoisted.d.ts +0 -1
- package/dist/IR/IRNodes/IRHoisted.js +4 -6
- package/dist/IR/IRNodes/IRLetted.d.ts +0 -1
- package/dist/IR/IRNodes/IRLetted.js +4 -6
- package/dist/IR/IRNodes/IRNative/IRNativeTag.d.ts +22 -2
- package/dist/IR/IRNodes/IRNative/IRNativeTag.js +26 -2
- package/dist/IR/IRNodes/IRNative/index.d.ts +16 -1
- package/dist/IR/IRNodes/IRNative/index.js +31 -2
- package/dist/IR/IRNodes/utils/hashVarSym.d.ts +0 -1
- package/dist/IR/IRNodes/utils/hashVarSym.js +27 -33
- package/dist/IR/toUPLC/CompilerOptions.d.ts +34 -7
- package/dist/IR/toUPLC/CompilerOptions.js +19 -10
- package/dist/IR/toUPLC/compileIRToUPLC.js +39 -3
- package/dist/IR/toUPLC/subRoutines/inlineSingleUseLetBindingsAndReturnRoot.d.ts +23 -0
- package/dist/IR/toUPLC/subRoutines/inlineSingleUseLetBindingsAndReturnRoot.js +263 -0
- package/dist/IR/toUPLC/subRoutines/introduceCaseForDualHeadTailAndReturnRoot.d.ts +35 -0
- package/dist/IR/toUPLC/subRoutines/introduceCaseForDualHeadTailAndReturnRoot.js +169 -0
- package/dist/IR/toUPLC/subRoutines/replaceHoistedWithLetted.d.ts +0 -1
- package/dist/IR/toUPLC/subRoutines/replaceHoistedWithLetted.js +6 -6
- package/dist/IR/toUPLC/subRoutines/replaceNatives/nativeToIR.d.ts +2 -3
- package/dist/IR/toUPLC/subRoutines/replaceNatives/nativeToIR.js +106 -65
- package/dist/IR/toUPLC/subRoutines/rewriteHeadTailInCaseConsAndReturnRoot.d.ts +30 -0
- package/dist/IR/toUPLC/subRoutines/rewriteHeadTailInCaseConsAndReturnRoot.js +95 -0
- package/dist/IR/toUPLC/subRoutines/rewriteNativesAppliedToConstantsAndReturnRoot.js +36 -5
- package/dist/IR/toUPLC/subRoutines/rewriteToCaseOverConstAndReturnRoot.d.ts +35 -0
- package/dist/IR/toUPLC/subRoutines/rewriteToCaseOverConstAndReturnRoot.js +169 -0
- package/dist/IR/tree_utils/_ir_caseList.d.ts +15 -0
- package/dist/IR/tree_utils/_ir_caseList.js +19 -0
- package/dist/IR/tree_utils/bytesToHex.d.ts +8 -0
- package/dist/IR/tree_utils/bytesToHex.js +69 -0
- package/dist/ast/nodes/expr/functions/FuncExpr.d.ts +16 -2
- package/dist/ast/nodes/expr/functions/FuncExpr.js +17 -0
- package/dist/ast/nodes/expr/litteral/LitteralExpr.d.ts +2 -1
- package/dist/ast/nodes/expr/litteral/LitteralExpr.js +2 -0
- package/dist/ast/nodes/expr/litteral/TemplateStrExpr.d.ts +30 -0
- package/dist/ast/nodes/expr/litteral/TemplateStrExpr.js +35 -0
- package/dist/ast/nodes/statements/ExportStmt.d.ts +3 -3
- package/dist/ast/nodes/statements/PebbleStmt.d.ts +4 -3
- package/dist/ast/nodes/statements/PebbleStmt.js +6 -2
- package/dist/ast/nodes/statements/TestParam.d.ts +18 -0
- package/dist/ast/nodes/statements/TestParam.js +18 -0
- package/dist/ast/nodes/statements/TestStmt.d.ts +5 -3
- package/dist/ast/nodes/statements/TestStmt.js +3 -1
- package/dist/ast/nodes/statements/UsingStmt.d.ts +32 -2
- package/dist/ast/nodes/statements/UsingStmt.js +39 -3
- package/dist/ast/nodes/statements/declarations/NamespaceDecl.d.ts +21 -0
- package/dist/ast/nodes/statements/declarations/NamespaceDecl.js +31 -0
- package/dist/ast/nodes/statements/declarations/StructDecl.d.ts +16 -2
- package/dist/ast/nodes/statements/declarations/StructDecl.js +15 -1
- package/dist/compiler/AstCompiler/AstCompiler.d.ts +27 -0
- package/dist/compiler/AstCompiler/AstCompiler.js +244 -7
- package/dist/compiler/AstCompiler/internal/_deriveContractBody/_deriveContractBody.js +16 -5
- package/dist/compiler/AstCompiler/internal/exprs/_compileCallExpr.js +97 -6
- package/dist/compiler/AstCompiler/internal/exprs/_compileCaseExpr.js +31 -0
- package/dist/compiler/AstCompiler/internal/exprs/_compileFuncExpr.js +12 -5
- package/dist/compiler/AstCompiler/internal/exprs/_compileIsExpr.js +12 -0
- package/dist/compiler/AstCompiler/internal/exprs/_compileLitteralExpr.js +59 -0
- package/dist/compiler/AstCompiler/internal/exprs/_compilePropAccessExpr.d.ts +2 -3
- package/dist/compiler/AstCompiler/internal/exprs/_compilePropAccessExpr.js +64 -0
- package/dist/compiler/AstCompiler/internal/exprs/_compileUnaryPrefixExpr.js +13 -1
- package/dist/compiler/AstCompiler/internal/exprs/_compileVarAccessExpr.js +2 -0
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileAddExpr.js +18 -5
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileEqualExpr.js +3 -1
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileGreaterThanEqualExpr.js +2 -1
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileGreaterThanExpr.js +2 -1
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileLessThanEqualExpr.js +2 -1
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileLessThanExpr.js +2 -1
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileMultExpr.js +24 -6
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileNotEqualExpr.js +2 -1
- package/dist/compiler/AstCompiler/internal/exprs/binary/_compileSubExpr.js +16 -5
- package/dist/compiler/AstCompiler/internal/statements/_compileMatchStmt.js +33 -20
- package/dist/compiler/AstCompiler/internal/statements/_compileStatement.js +4 -1
- package/dist/compiler/AstCompiler/internal/statements/_compileTestStmt.d.ts +15 -1
- package/dist/compiler/AstCompiler/internal/statements/_compileTestStmt.js +70 -30
- package/dist/compiler/AstCompiler/internal/statements/_compileUsingAliasStmt.d.ts +11 -0
- package/dist/compiler/AstCompiler/internal/statements/_compileUsingAliasStmt.js +26 -0
- package/dist/compiler/AstCompiler/internal/statements/_compileUsingStmt.d.ts +9 -4
- package/dist/compiler/AstCompiler/internal/statements/_compileUsingStmt.js +51 -10
- package/dist/compiler/AstCompiler/internal/types/_compileDataEncodedConcreteType.js +21 -2
- package/dist/compiler/AstCompiler/internal/types/_compileSopEncodedConcreteType.js +17 -2
- package/dist/compiler/AstCompiler/scope/AstScope.d.ts +70 -1
- package/dist/compiler/AstCompiler/scope/AstScope.js +91 -0
- package/dist/compiler/AstCompiler/utils/getPropAccessReturnType.js +36 -1
- package/dist/compiler/AstCompiler/utils/monomorphizeGeneric.d.ts +36 -0
- package/dist/compiler/AstCompiler/utils/monomorphizeGeneric.js +123 -0
- package/dist/compiler/AstCompiler/utils/resolveNamespaceChain.d.ts +28 -0
- package/dist/compiler/AstCompiler/utils/resolveNamespaceChain.js +95 -0
- package/dist/compiler/AstCompiler/utils/resolveNamespacePath.d.ts +37 -0
- package/dist/compiler/AstCompiler/utils/resolveNamespacePath.js +93 -0
- package/dist/compiler/Compiler.d.ts +9 -1
- package/dist/compiler/Compiler.js +218 -30
- package/dist/compiler/TirCompiler/expressify/ExpressifyCtx.js +1 -1
- package/dist/compiler/TirCompiler/expressify/expressify.js +30 -2
- package/dist/compiler/TirCompiler/expressify/expressifyForStmt.d.ts +2 -1
- package/dist/compiler/TirCompiler/expressify/expressifyForStmt.js +45 -7
- package/dist/compiler/TirCompiler/expressify/expressifyVars.d.ts +0 -1
- package/dist/compiler/TirCompiler/expressify/expressifyVars.js +49 -15
- package/dist/compiler/test/TestResult.d.ts +38 -0
- package/dist/compiler/test/TestResult.js +6 -0
- package/dist/compiler/test/fuzz/PRNG.d.ts +26 -0
- package/dist/compiler/test/fuzz/PRNG.js +59 -0
- package/dist/compiler/tir/expressions/TirCaseExpr.d.ts +9 -0
- package/dist/compiler/tir/expressions/TirCaseExpr.js +144 -122
- package/dist/compiler/tir/expressions/TirElemAccessExpr.js +2 -2
- package/dist/compiler/tir/expressions/TirExpr.d.ts +2 -1
- package/dist/compiler/tir/expressions/TirExpr.js +2 -0
- package/dist/compiler/tir/expressions/TirFromDataExpr.js +102 -67
- package/dist/compiler/tir/expressions/TirIsExpr.js +14 -1
- package/dist/compiler/tir/expressions/TirNativeFunc.d.ts +18 -2
- package/dist/compiler/tir/expressions/TirNativeFunc.js +55 -118
- package/dist/compiler/tir/expressions/TirShowExpr.d.ts +52 -0
- package/dist/compiler/tir/expressions/TirShowExpr.js +199 -0
- package/dist/compiler/tir/expressions/TirToDataExpr.js +3 -0
- package/dist/compiler/tir/expressions/TirTraceExpr.js +11 -7
- package/dist/compiler/tir/expressions/TirTypeConversionExpr.js +10 -0
- package/dist/compiler/tir/expressions/TirVariableAccessExpr.d.ts +2 -3
- package/dist/compiler/tir/expressions/TirVariableAccessExpr.js +1 -4
- package/dist/compiler/tir/expressions/ToIRTermCtx.d.ts +20 -3
- package/dist/compiler/tir/expressions/ToIRTermCtx.js +48 -3
- package/dist/compiler/tir/expressions/binary/TirBinaryExpr.d.ts +2 -2
- package/dist/compiler/tir/expressions/binary/TirBinaryExpr.js +45 -8
- package/dist/compiler/tir/expressions/litteral/TirLitEnumMemberExpr.d.ts +19 -0
- package/dist/compiler/tir/expressions/litteral/TirLitEnumMemberExpr.js +24 -0
- package/dist/compiler/tir/expressions/litteral/TirLitteralExpr.d.ts +2 -1
- package/dist/compiler/tir/expressions/litteral/TirLitteralExpr.js +2 -0
- package/dist/compiler/tir/expressions/unary/TirUnaryMinus.js +4 -1
- package/dist/compiler/tir/program/TypedProgram.d.ts +101 -0
- package/dist/compiler/tir/program/TypedProgram.js +43 -0
- package/dist/compiler/tir/program/stdScope/populateBuiltinInterfaces.d.ts +17 -0
- package/dist/compiler/tir/program/stdScope/populateBuiltinInterfaces.js +70 -0
- package/dist/compiler/tir/program/stdScope/populateStdNamespace.d.ts +22 -0
- package/dist/compiler/tir/program/stdScope/populateStdNamespace.js +619 -0
- package/dist/compiler/tir/program/stdScope/prelude/preludeTypesSrc.js +35 -2
- package/dist/compiler/tir/program/stdScope/stdScope.d.ts +8 -0
- package/dist/compiler/tir/program/stdScope/stdScope.js +84 -41
- package/dist/compiler/tir/statements/TirStmt.js +0 -1
- package/dist/compiler/tir/statements/TirTestStmt.d.ts +46 -0
- package/dist/compiler/tir/statements/TirTestStmt.js +35 -0
- package/dist/compiler/tir/types/TirEnumType.d.ts +21 -0
- package/dist/compiler/tir/types/TirEnumType.js +36 -0
- package/dist/compiler/tir/types/TirNativeType/TirNativeType.d.ts +53 -2
- package/dist/compiler/tir/types/TirNativeType/TirNativeType.js +58 -1
- package/dist/compiler/tir/types/TirNativeType/native/array.d.ts +16 -0
- package/dist/compiler/tir/types/TirNativeType/native/array.js +38 -0
- package/dist/compiler/tir/types/TirNativeType/native/index.d.ts +2 -0
- package/dist/compiler/tir/types/TirNativeType/native/index.js +2 -0
- package/dist/compiler/tir/types/TirNativeType/native/value.d.ts +18 -0
- package/dist/compiler/tir/types/TirNativeType/native/value.js +17 -0
- package/dist/compiler/tir/types/TirStructType.js +6 -1
- package/dist/compiler/tir/types/TirType.d.ts +3 -2
- package/dist/compiler/tir/types/TirType.js +7 -2
- package/dist/compiler/tir/types/utils/canAssignTo.js +36 -1
- package/dist/compiler/tir/types/utils/canCastTo.js +14 -1
- package/dist/compiler/tir/types/utils/getDeconstructableType.d.ts +2 -1
- package/dist/compiler/tir/types/utils/getDeconstructableType.js +2 -0
- package/dist/compiler/tir/types/utils/inferTypeArgs.d.ts +19 -0
- package/dist/compiler/tir/types/utils/inferTypeArgs.js +83 -0
- package/dist/compiler/tir/types/utils/normalizeEnumToInt.d.ts +10 -0
- package/dist/compiler/tir/types/utils/normalizeEnumToInt.js +17 -0
- package/dist/compiler/tir/types/utils/substituteTypeParams.d.ts +9 -0
- package/dist/compiler/tir/types/utils/substituteTypeParams.js +67 -0
- package/dist/diagnostics/diagnosticMessages.generated.d.ts +10 -0
- package/dist/diagnostics/diagnosticMessages.generated.js +20 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/parser/Parser.d.ts +73 -3
- package/dist/parser/Parser.js +362 -46
- package/dist/tokenizer/Token.d.ts +106 -102
- package/dist/tokenizer/Token.js +111 -109
- package/dist/tokenizer/utils/tokenFromKeyword.js +11 -6
- package/dist/utils/semverSatisfies.d.ts +1 -0
- package/dist/utils/semverSatisfies.js +161 -0
- package/dist/version.generated.d.ts +1 -0
- package/dist/version.generated.js +2 -0
- package/package.json +5 -4
- package/dist/IR/tree_utils/_ir_lazyChooseList.d.ts +0 -3
- package/dist/IR/tree_utils/_ir_lazyChooseList.js +0 -7
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { DiagnosticCode } from "../../../diagnostics/diagnosticMessages.generated.js";
|
|
2
|
+
/**
|
|
3
|
+
* walks a dotted namespace path against the given scope.
|
|
4
|
+
*
|
|
5
|
+
* - returns the final `NamespaceSymbol` if every segment resolves to a namespace.
|
|
6
|
+
* - if the first segment is not a namespace, returns `undefined`
|
|
7
|
+
* (no diagnostic emitted; the caller can fall back to a different
|
|
8
|
+
* resolution strategy, e.g. struct lookup).
|
|
9
|
+
* - if a later segment is missing or is not a namespace, emits a
|
|
10
|
+
* diagnostic and returns `undefined`.
|
|
11
|
+
*/
|
|
12
|
+
export function resolveNamespacePath(ctx, path) {
|
|
13
|
+
const segments = path.segments;
|
|
14
|
+
if (segments.length === 0)
|
|
15
|
+
return undefined;
|
|
16
|
+
const head = segments[0];
|
|
17
|
+
const first = ctx.scope.resolveNamespace(head.text);
|
|
18
|
+
if (!first)
|
|
19
|
+
return undefined;
|
|
20
|
+
const chain = [first];
|
|
21
|
+
let current = first;
|
|
22
|
+
for (let i = 1; i < segments.length; i++) {
|
|
23
|
+
const seg = segments[i];
|
|
24
|
+
const inner = current.publicScope.namespaces.get(seg.text);
|
|
25
|
+
if (!inner) {
|
|
26
|
+
ctx.error(DiagnosticCode.Namespace_0_has_no_exported_member_1, seg.range, current.name, seg.text);
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
chain.push(inner);
|
|
30
|
+
current = inner;
|
|
31
|
+
}
|
|
32
|
+
return { namespace: current, chain };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* looks up a member name in a namespace's public scope.
|
|
36
|
+
*
|
|
37
|
+
* the member can be any of:
|
|
38
|
+
* - a value (variable)
|
|
39
|
+
* - a function
|
|
40
|
+
* - a type
|
|
41
|
+
* - an interface
|
|
42
|
+
* - a nested namespace
|
|
43
|
+
*
|
|
44
|
+
* binds the resolved member into `target` (under either `originalName`
|
|
45
|
+
* or `aliasName` if provided). emits a diagnostic and returns `false`
|
|
46
|
+
* if no such member is exported, or if a conflicting binding already
|
|
47
|
+
* exists in `target`.
|
|
48
|
+
*/
|
|
49
|
+
export function bindNamespaceMember(ctx, ns, originalName, aliasName, target) {
|
|
50
|
+
const pub = ns.publicScope;
|
|
51
|
+
const name = originalName.text;
|
|
52
|
+
const localName = aliasName?.text ?? name;
|
|
53
|
+
const variable = pub.variables.get(name);
|
|
54
|
+
const fn = pub.functions.get(name);
|
|
55
|
+
const tirFunc = fn ? pub.program.functions.get(fn) : undefined;
|
|
56
|
+
const ty = pub.types.get(name);
|
|
57
|
+
const iface = pub.interfaces.get(name);
|
|
58
|
+
const nested = pub.namespaces.get(name);
|
|
59
|
+
if (!variable && !fn && !ty && !iface && !nested) {
|
|
60
|
+
ctx.error(DiagnosticCode.Namespace_0_has_no_exported_member_1, originalName.range, ns.name, name);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
let ok = true;
|
|
64
|
+
if (variable) {
|
|
65
|
+
ok = target.defineValue({ ...variable, name: localName }) && ok;
|
|
66
|
+
}
|
|
67
|
+
if (fn) {
|
|
68
|
+
target.functions.set(localName, fn);
|
|
69
|
+
if (tirFunc) {
|
|
70
|
+
target.defineValue({
|
|
71
|
+
isConstant: true,
|
|
72
|
+
name: localName,
|
|
73
|
+
type: tirFunc.type,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (ty) {
|
|
78
|
+
ok = target.defineType(localName, ty) && ok;
|
|
79
|
+
}
|
|
80
|
+
if (iface) {
|
|
81
|
+
if (target.interfaces.has(localName))
|
|
82
|
+
ok = false;
|
|
83
|
+
else
|
|
84
|
+
target.interfaces.set(localName, iface);
|
|
85
|
+
}
|
|
86
|
+
if (nested) {
|
|
87
|
+
ok = target.defineNamespace({
|
|
88
|
+
name: localName,
|
|
89
|
+
publicScope: nested.publicScope
|
|
90
|
+
}) && ok;
|
|
91
|
+
}
|
|
92
|
+
return ok;
|
|
93
|
+
}
|
|
@@ -3,11 +3,13 @@ import { DiagnosticMessage } from "../diagnostics/DiagnosticMessage.js";
|
|
|
3
3
|
import { CompilerOptions } from "../IR/toUPLC/CompilerOptions.js";
|
|
4
4
|
import { CompilerIoApi } from "./io/CompilerIoApi.js";
|
|
5
5
|
import { CheckResult } from "./SourceTypeMap.js";
|
|
6
|
+
import { TestResult } from "./test/TestResult.js";
|
|
6
7
|
export { CheckResult, SourceTypeMap, TypeEntry, MemberInfo } from "./SourceTypeMap.js";
|
|
8
|
+
export { TestBudget, TestIterationResult, TestResult, TestKind, } from "./test/TestResult.js";
|
|
7
9
|
export declare class Compiler extends DiagnosticEmitter {
|
|
8
10
|
readonly io: CompilerIoApi;
|
|
9
11
|
readonly cfg: CompilerOptions;
|
|
10
|
-
constructor(io
|
|
12
|
+
constructor(io: CompilerIoApi, cfg: CompilerOptions, diagnostics?: DiagnosticMessage[]);
|
|
11
13
|
check(config?: Partial<CompilerOptions>): Promise<CheckResult>;
|
|
12
14
|
compile(config?: Partial<CompilerOptions>): Promise<Uint8Array>;
|
|
13
15
|
export(config: Partial<ExportOptions> & HasFuncitonName): Promise<Uint8Array>;
|
|
@@ -16,6 +18,12 @@ export declare class Compiler extends DiagnosticEmitter {
|
|
|
16
18
|
budgetSpent: import("@harmoniclabs/plutus-machine").ExBudget;
|
|
17
19
|
logs: string[];
|
|
18
20
|
}>;
|
|
21
|
+
test(config?: Partial<CompilerOptions> & {
|
|
22
|
+
nameFilter?: string | RegExp;
|
|
23
|
+
propertyIterations?: number;
|
|
24
|
+
seed?: number;
|
|
25
|
+
}): Promise<TestResult[]>;
|
|
26
|
+
private _runOneTest;
|
|
19
27
|
runRepl(config?: Partial<CompilerOptions>): Promise<{
|
|
20
28
|
result: import("@harmoniclabs/plutus-machine").CEKValueObj;
|
|
21
29
|
budgetSpent: import("@harmoniclabs/plutus-machine").ExBudget;
|
|
@@ -1,25 +1,35 @@
|
|
|
1
|
-
import { compileUPLC, Force, parseUPLC, UPLCProgram } from "@harmoniclabs/uplc";
|
|
2
|
-
import { Machine } from "@harmoniclabs/plutus-machine";
|
|
1
|
+
import { Application, compileUPLC, Force, parseUPLC, UPLCConst, UPLCProgram } from "@harmoniclabs/uplc";
|
|
2
|
+
import { CEKError, Machine } from "@harmoniclabs/plutus-machine";
|
|
3
|
+
import { DiagnosticCategory } from "../diagnostics/DiagnosticCategory.js";
|
|
3
4
|
import { DiagnosticEmitter } from "../diagnostics/DiagnosticEmitter.js";
|
|
4
|
-
import {
|
|
5
|
+
import { COMPILER_VERSION } from "../version.generated.js";
|
|
6
|
+
import { semverSatisfies } from "../utils/semverSatisfies.js";
|
|
5
7
|
import { AstCompiler } from "./AstCompiler/AstCompiler.js";
|
|
6
8
|
import { createMemoryCompilerIoApi } from "./io/CompilerIoApi.js";
|
|
7
9
|
import { compileTypedProgram } from "./TirCompiler/compileTirProgram.js";
|
|
8
|
-
import {
|
|
9
|
-
import { __VERY_UNSAFE_FORGET_VAR_SYM_HASHES_ONLY_USE_AT_END_OF_UPLC_COMPILATION } from "../IR/IRNodes/utils/hashVarSym.js";
|
|
10
|
-
import { __unsafe_clear_hoisted_hash_to_symbol } from "../IR/IRNodes/IRHoisted.js";
|
|
11
|
-
import { __unsafe_clear_letted_hash_to_symbol } from "../IR/IRNodes/IRLetted.js";
|
|
12
|
-
import { __unsafe_clear_hoisted_cache } from "../IR/toUPLC/subRoutines/replaceHoistedWithLetted.js";
|
|
13
|
-
import { __unsafe_clear_mapToType_cache } from "./TirCompiler/expressify/expressifyVars.js";
|
|
10
|
+
import { CompilationCtx, withCompilationCtx } from "../IR/CompilationCtx.js";
|
|
14
11
|
import { compileIRToUPLC } from "../IR/toUPLC/compileIRToUPLC.js";
|
|
12
|
+
import { TirFuncExpr } from "./tir/expressions/TirFuncExpr.js";
|
|
13
|
+
import { addBudget, zeroBudget, } from "./test/TestResult.js";
|
|
14
|
+
import { PRNG } from "./test/fuzz/PRNG.js";
|
|
15
15
|
export { SourceTypeMap } from "./SourceTypeMap.js";
|
|
16
16
|
export class Compiler extends DiagnosticEmitter {
|
|
17
17
|
io;
|
|
18
18
|
cfg;
|
|
19
|
-
constructor(io = createMemoryCompilerIoApi({ useConsoleAsOutput: true }), cfg
|
|
19
|
+
constructor(io = createMemoryCompilerIoApi({ useConsoleAsOutput: true }), cfg, diagnostics) {
|
|
20
20
|
super(diagnostics);
|
|
21
21
|
this.io = io;
|
|
22
22
|
this.cfg = cfg;
|
|
23
|
+
const range = cfg?.compilerVersion;
|
|
24
|
+
if (typeof range !== "string" || range.length === 0) {
|
|
25
|
+
throw new Error(`Pebble compiler config is missing "compilerVersion". ` +
|
|
26
|
+
`Starting from @harmoniclabs/pebble@0.2.0 this field is required ` +
|
|
27
|
+
`and must be an npm-style semver range (e.g. "^0.2.0").`);
|
|
28
|
+
}
|
|
29
|
+
if (!semverSatisfies(COMPILER_VERSION, range)) {
|
|
30
|
+
throw new Error(`Pebble compiler version ${COMPILER_VERSION} does not satisfy ` +
|
|
31
|
+
`the configured "compilerVersion" range "${range}".`);
|
|
32
|
+
}
|
|
23
33
|
if (cfg.silent === true) {
|
|
24
34
|
this.io.stdout = { write() { } };
|
|
25
35
|
}
|
|
@@ -42,7 +52,6 @@ export class Compiler extends DiagnosticEmitter {
|
|
|
42
52
|
const program = await astCompiler.compile();
|
|
43
53
|
if (this.diagnostics.length > 0) {
|
|
44
54
|
let msg;
|
|
45
|
-
// globalThis.console && console.log( this.diagnostics );
|
|
46
55
|
const fstErrorMsg = this.diagnostics[0].toString();
|
|
47
56
|
const nDiags = this.diagnostics.length;
|
|
48
57
|
for (msg of this.diagnostics) {
|
|
@@ -67,7 +76,6 @@ export class Compiler extends DiagnosticEmitter {
|
|
|
67
76
|
const program = await astCompiler.export(cfg.functionName, cfg.entry);
|
|
68
77
|
if (this.diagnostics.length > 0) {
|
|
69
78
|
let msg;
|
|
70
|
-
globalThis.console && console.log(this.diagnostics);
|
|
71
79
|
const fstErrorMsg = this.diagnostics[0].toString();
|
|
72
80
|
const nDiags = this.diagnostics.length;
|
|
73
81
|
while (msg = this.diagnostics.shift()) {
|
|
@@ -88,7 +96,6 @@ export class Compiler extends DiagnosticEmitter {
|
|
|
88
96
|
const program = await astCompiler.run();
|
|
89
97
|
if (this.diagnostics.length > 0) {
|
|
90
98
|
let msg;
|
|
91
|
-
globalThis.console && console.log(this.diagnostics);
|
|
92
99
|
const fstErrorMsg = this.diagnostics[0].toString();
|
|
93
100
|
const nDiags = this.diagnostics.length;
|
|
94
101
|
while (msg = this.diagnostics.shift()) {
|
|
@@ -102,6 +109,167 @@ export class Compiler extends DiagnosticEmitter {
|
|
|
102
109
|
// compiles to `Delay(body)`; force it so the body executes.
|
|
103
110
|
return Machine.eval(new Force(uplcProgram.body));
|
|
104
111
|
}
|
|
112
|
+
async test(config) {
|
|
113
|
+
const cfg = {
|
|
114
|
+
...this.cfg,
|
|
115
|
+
...config,
|
|
116
|
+
silent: true,
|
|
117
|
+
addMarker: false,
|
|
118
|
+
};
|
|
119
|
+
const nameFilter = config?.nameFilter;
|
|
120
|
+
const matches = (!nameFilter ? () => true :
|
|
121
|
+
typeof nameFilter === "string" ? (n) => n.includes(nameFilter) :
|
|
122
|
+
(n) => nameFilter.test(n));
|
|
123
|
+
const propertyIterations = Math.max(1, config?.propertyIterations ?? 100);
|
|
124
|
+
const seed = config?.seed ?? 0;
|
|
125
|
+
// 1) discovery pass: parse + check, populate program.tests.
|
|
126
|
+
// diagnostics from this pass are surfaced once; subsequent per-test
|
|
127
|
+
// passes use their own diagnostic arrays so we don't double-report.
|
|
128
|
+
const discovery = new AstCompiler(cfg, this.io, this.diagnostics);
|
|
129
|
+
const discoveryResult = await discovery.check();
|
|
130
|
+
const descriptors = discoveryResult.program.tests
|
|
131
|
+
.filter(t => matches(t.name))
|
|
132
|
+
.map(t => {
|
|
133
|
+
const fn = discoveryResult.program.functions.get(t.tirFuncName);
|
|
134
|
+
const paramNames = (fn instanceof TirFuncExpr) ? fn.params.map(p => p.sourceName ?? p.name) : [];
|
|
135
|
+
return {
|
|
136
|
+
name: t.name,
|
|
137
|
+
tirFuncName: t.tirFuncName,
|
|
138
|
+
sourceFile: t.sourceFile,
|
|
139
|
+
range: t.range,
|
|
140
|
+
paramNames,
|
|
141
|
+
fuzzerInfos: t.fuzzerInfos,
|
|
142
|
+
};
|
|
143
|
+
});
|
|
144
|
+
const results = new Array(descriptors.length);
|
|
145
|
+
for (let i = 0; i < descriptors.length; i++) {
|
|
146
|
+
results[i] = await this._runOneTest(cfg, descriptors[i], propertyIterations, seed);
|
|
147
|
+
}
|
|
148
|
+
return results;
|
|
149
|
+
}
|
|
150
|
+
async _runOneTest(cfg, desc, propertyIterations, seed) {
|
|
151
|
+
const isProperty = desc.fuzzerInfos.length > 0;
|
|
152
|
+
// fresh AstCompiler so the expressify pass starts from a clean program
|
|
153
|
+
const localDiagnostics = [];
|
|
154
|
+
const astCompiler = new AstCompiler(cfg, this.io, localDiagnostics);
|
|
155
|
+
await astCompiler.compileFile(cfg.entry, true);
|
|
156
|
+
if (localDiagnostics.some(d => d.category === DiagnosticCategory.Error)) {
|
|
157
|
+
return _failedTestResult(desc, "compile error: " + localDiagnostics.find(d => d.category === DiagnosticCategory.Error).toString(), isProperty ? "property" : "unit");
|
|
158
|
+
}
|
|
159
|
+
const fn = astCompiler.program.functions.get(desc.tirFuncName);
|
|
160
|
+
if (!(fn instanceof TirFuncExpr)) {
|
|
161
|
+
return _failedTestResult(desc, `test function "${desc.name}" not found after re-parse`, isProperty ? "property" : "unit");
|
|
162
|
+
}
|
|
163
|
+
astCompiler.program.contractTirFuncName = desc.tirFuncName;
|
|
164
|
+
let serialized;
|
|
165
|
+
try {
|
|
166
|
+
serialized = this._compileBackend(cfg, astCompiler.program, true);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
return _failedTestResult(desc, "backend error: " + (err instanceof Error ? err.message : String(err)), isProperty ? "property" : "unit");
|
|
170
|
+
}
|
|
171
|
+
const uplcProgram = parseUPLC(serialized);
|
|
172
|
+
if (!isProperty) {
|
|
173
|
+
const evalResult = Machine.eval(new Force(uplcProgram.body));
|
|
174
|
+
const isErr = evalResult.result instanceof CEKError;
|
|
175
|
+
const budget = {
|
|
176
|
+
cpu: BigInt(evalResult.budgetSpent.cpu),
|
|
177
|
+
mem: BigInt(evalResult.budgetSpent.mem),
|
|
178
|
+
};
|
|
179
|
+
const iter = {
|
|
180
|
+
passed: !isErr,
|
|
181
|
+
budgetSpent: budget,
|
|
182
|
+
logs: evalResult.logs.slice(),
|
|
183
|
+
error: isErr ? { msg: evalResult.result.msg } : undefined,
|
|
184
|
+
};
|
|
185
|
+
return {
|
|
186
|
+
name: desc.name,
|
|
187
|
+
sourceFile: desc.sourceFile,
|
|
188
|
+
range: desc.range,
|
|
189
|
+
kind: "unit",
|
|
190
|
+
passed: !isErr,
|
|
191
|
+
iterations: [iter],
|
|
192
|
+
totalBudget: addBudget(zeroBudget(), budget),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
// ── Property test ──────────────────────────────────────────────
|
|
196
|
+
// Check that every parameter has an executable fuzzer in v1 (Phase 1).
|
|
197
|
+
const unsupported = desc.fuzzerInfos.find(fi => fi.kind === "unsupported" || fi.kind === "via_not_implemented");
|
|
198
|
+
if (unsupported) {
|
|
199
|
+
const reason = unsupported.kind === "via_not_implemented"
|
|
200
|
+
? "user-defined fuzzers via the 'via' keyword are not yet executable (Phase 2)"
|
|
201
|
+
: unsupported.reason;
|
|
202
|
+
return {
|
|
203
|
+
name: desc.name,
|
|
204
|
+
sourceFile: desc.sourceFile,
|
|
205
|
+
range: desc.range,
|
|
206
|
+
kind: "property",
|
|
207
|
+
passed: false,
|
|
208
|
+
iterations: [],
|
|
209
|
+
totalBudget: zeroBudget(),
|
|
210
|
+
skippedReason: reason,
|
|
211
|
+
seed,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
// Run N iterations with TS-side sampling.
|
|
215
|
+
const prng = new PRNG(seed);
|
|
216
|
+
const iterations = [];
|
|
217
|
+
let totalBudget = zeroBudget();
|
|
218
|
+
let passedAll = true;
|
|
219
|
+
for (let i = 0; i < propertyIterations; i++) {
|
|
220
|
+
const inputs = [];
|
|
221
|
+
const args = [];
|
|
222
|
+
for (let p = 0; p < desc.fuzzerInfos.length; p++) {
|
|
223
|
+
const fi = desc.fuzzerInfos[p];
|
|
224
|
+
if (fi.kind !== "primitive")
|
|
225
|
+
throw new Error("unreachable: non-primitive after unsupported check");
|
|
226
|
+
const paramName = desc.paramNames[p] ?? `param${p}`;
|
|
227
|
+
if (fi.primitive === "int") {
|
|
228
|
+
const v = prng.nextIntBiased();
|
|
229
|
+
inputs.push({ name: paramName, value: v });
|
|
230
|
+
args.push(UPLCConst.int(v));
|
|
231
|
+
}
|
|
232
|
+
else // bool
|
|
233
|
+
{
|
|
234
|
+
const v = prng.nextBool();
|
|
235
|
+
inputs.push({ name: paramName, value: v });
|
|
236
|
+
args.push(UPLCConst.bool(v));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
let app = uplcProgram.body;
|
|
240
|
+
for (const arg of args)
|
|
241
|
+
app = new Application(app, arg);
|
|
242
|
+
const evalResult = Machine.eval(app);
|
|
243
|
+
const isErr = evalResult.result instanceof CEKError;
|
|
244
|
+
const budget = {
|
|
245
|
+
cpu: BigInt(evalResult.budgetSpent.cpu),
|
|
246
|
+
mem: BigInt(evalResult.budgetSpent.mem),
|
|
247
|
+
};
|
|
248
|
+
const iter = {
|
|
249
|
+
passed: !isErr,
|
|
250
|
+
budgetSpent: budget,
|
|
251
|
+
logs: evalResult.logs.slice(),
|
|
252
|
+
error: isErr ? { msg: evalResult.result.msg } : undefined,
|
|
253
|
+
inputs,
|
|
254
|
+
};
|
|
255
|
+
iterations.push(iter);
|
|
256
|
+
totalBudget = addBudget(totalBudget, budget);
|
|
257
|
+
if (isErr) {
|
|
258
|
+
passedAll = false;
|
|
259
|
+
break; // early exit on first failure (Phase 1; shrinking is Phase 2)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
name: desc.name,
|
|
264
|
+
sourceFile: desc.sourceFile,
|
|
265
|
+
range: desc.range,
|
|
266
|
+
kind: "property",
|
|
267
|
+
passed: passedAll,
|
|
268
|
+
iterations,
|
|
269
|
+
totalBudget,
|
|
270
|
+
seed,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
105
273
|
async runRepl(config) {
|
|
106
274
|
const cfg = {
|
|
107
275
|
...this.cfg,
|
|
@@ -112,7 +280,6 @@ export class Compiler extends DiagnosticEmitter {
|
|
|
112
280
|
const program = await astCompiler.runRepl();
|
|
113
281
|
if (this.diagnostics.length > 0) {
|
|
114
282
|
let msg;
|
|
115
|
-
globalThis.console && console.log(this.diagnostics);
|
|
116
283
|
const fstErrorMsg = this.diagnostics[0].toString();
|
|
117
284
|
const nDiags = this.diagnostics.length;
|
|
118
285
|
while (msg = this.diagnostics.shift()) {
|
|
@@ -126,21 +293,42 @@ export class Compiler extends DiagnosticEmitter {
|
|
|
126
293
|
// compiles to `Delay(body)`; force it so the body executes.
|
|
127
294
|
return Machine.eval(new Force(uplcProgram.body));
|
|
128
295
|
}
|
|
129
|
-
_compileBackend(cfg, program) {
|
|
130
|
-
// backend
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
296
|
+
_compileBackend(cfg, program, skipFileOutput = false) {
|
|
297
|
+
// Run the whole backend under a fresh per-compilation context. All
|
|
298
|
+
// node-level caches (hoisted/letted naming, hoisted->letted lowering,
|
|
299
|
+
// the mapToType helper cache) live on this context, so they cannot
|
|
300
|
+
// leak into or be perturbed by any other compilation — and they are
|
|
301
|
+
// dropped automatically when this scope exits, even on throw. The
|
|
302
|
+
// content-addressed `IRHash` has no global state at all, so there is
|
|
303
|
+
// nothing else to reset.
|
|
304
|
+
return withCompilationCtx(new CompilationCtx(), () => {
|
|
305
|
+
const ir = compileTypedProgram(cfg, program);
|
|
306
|
+
const uplc = compileIRToUPLC(ir, cfg);
|
|
307
|
+
const serialized = compileUPLC(new UPLCProgram(cfg.targetUplcVersion, uplc));
|
|
308
|
+
if (!skipFileOutput) {
|
|
309
|
+
const outDir = cfg.outDir;
|
|
310
|
+
const outPath = outDir + (outDir.endsWith("/") ? "" : "/") + "out.flat";
|
|
311
|
+
this.io.writeFile(outPath, serialized, cfg.root);
|
|
312
|
+
this.io.stdout.write(`compiled program written to ${outPath}\n`);
|
|
313
|
+
}
|
|
314
|
+
return serialized;
|
|
315
|
+
});
|
|
145
316
|
}
|
|
146
317
|
}
|
|
318
|
+
function _failedTestResult(desc, msg, kind = "unit") {
|
|
319
|
+
const budget = zeroBudget();
|
|
320
|
+
return {
|
|
321
|
+
name: desc.name,
|
|
322
|
+
sourceFile: desc.sourceFile,
|
|
323
|
+
range: desc.range,
|
|
324
|
+
kind,
|
|
325
|
+
passed: false,
|
|
326
|
+
iterations: [{
|
|
327
|
+
passed: false,
|
|
328
|
+
budgetSpent: budget,
|
|
329
|
+
logs: [],
|
|
330
|
+
error: { msg },
|
|
331
|
+
}],
|
|
332
|
+
totalBudget: budget,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
@@ -303,7 +303,7 @@ export class ExpressifyCtx {
|
|
|
303
303
|
if (stmt.rest) {
|
|
304
304
|
if (isDestructuredField)
|
|
305
305
|
throw new Error("rest is not implemented in destructured field array-like deconstruct. TODO: map data elems to elem type");
|
|
306
|
-
const lettedRest = this.introduceLettedConstant(stmt.rest, new TirCallExpr(TirNativeFunc.
|
|
306
|
+
const lettedRest = this.introduceLettedConstant(stmt.rest, new TirCallExpr(TirNativeFunc.dropList(elemsType), [
|
|
307
307
|
new TirLitIntExpr(BigInt(nElems), stmt.range),
|
|
308
308
|
lettedArrayExpr
|
|
309
309
|
], new TirListT(elemsType), stmt.range), stmt.range);
|
|
@@ -200,7 +200,7 @@ loopReplacements, assertions = []) {
|
|
|
200
200
|
}
|
|
201
201
|
if (stmt.rest) {
|
|
202
202
|
const uniqueRestName = getUniqueInternalName(stmt.rest);
|
|
203
|
-
const restLetted = ctx.introduceLettedConstant(uniqueRestName, new TirCallExpr(TirNativeFunc.
|
|
203
|
+
const restLetted = ctx.introduceLettedConstant(uniqueRestName, new TirCallExpr(TirNativeFunc.dropList(elemType), [
|
|
204
204
|
new TirLitIntExpr(BigInt(stmt.elements.length % modTails), stmt.range),
|
|
205
205
|
lettedArr
|
|
206
206
|
], listType, stmt.range), stmt.range);
|
|
@@ -395,8 +395,36 @@ loopReplacements, assertions = []) {
|
|
|
395
395
|
const returnTypeAndInvalidInit = getBranchStmtReturnType(reassignedAndFlow, ctx, stmt.range);
|
|
396
396
|
const forStmt = loopToForStmt(stmt);
|
|
397
397
|
const { bodyStateType, initState } = getBodyStateType(returnTypeAndInvalidInit, forStmt);
|
|
398
|
+
// Optimization: when the loop reassigns exactly one variable
|
|
399
|
+
// and has no user-written `return` inside, skip the SoP wrap
|
|
400
|
+
// around the loop's result. The recursive loop function then
|
|
401
|
+
// returns the bare variable's type, saving one `Reassigns{…}`
|
|
402
|
+
// constructor build per iteration and one IRCase at the exit.
|
|
403
|
+
// Requires the loop to be non-terminating (terminating loops
|
|
404
|
+
// are tail-positioned and their value must match the outer
|
|
405
|
+
// function's expected return type — we don't try to reconcile
|
|
406
|
+
// that here).
|
|
407
|
+
const canBareLower = (reassignedAndFlow.reassigned.length === 1
|
|
408
|
+
&& !reassignedAndFlow.returns
|
|
409
|
+
&& !definitelyTerminates);
|
|
398
410
|
const loopExprCtx = ctx.newChild();
|
|
399
|
-
const
|
|
411
|
+
const bareReturnType = canBareLower
|
|
412
|
+
? bodyStateType.constructors[0].fields[0].type
|
|
413
|
+
: undefined;
|
|
414
|
+
const loopExpr = expressifyForStmt(loopExprCtx, forStmt, returnTypeAndInvalidInit.sop, bodyStateType, initState, bareReturnType);
|
|
415
|
+
if (canBareLower) {
|
|
416
|
+
// Bind the loop's bare result as the variable's new SSA
|
|
417
|
+
// name, then evaluate the rest of the body. No outer
|
|
418
|
+
// case-match needed. Use a fresh internal name so the
|
|
419
|
+
// existing `letted[varName] = initialValue` binding (from
|
|
420
|
+
// the original `let varName = ...` statement) isn't
|
|
421
|
+
// shadowed by a no-op `introduceLettedConstant` call.
|
|
422
|
+
const [varName] = reassignedAndFlow.reassigned;
|
|
423
|
+
const loopResultName = getUniqueInternalName(varName);
|
|
424
|
+
const lettedExpr = ctx.introduceLettedConstant(loopResultName, loopExpr, stmt.range);
|
|
425
|
+
ctx.setNewVariableName(varName, lettedExpr.varName);
|
|
426
|
+
return TirAssertAndContinueExpr.fromStmtsAndContinuation(assertions, expressifyFuncBody(ctx, bodyStmts, loopReplacements));
|
|
427
|
+
}
|
|
400
428
|
const result = TirAssertAndContinueExpr.fromStmtsAndContinuation(assertions, definitelyTerminates ? loopExpr : wrapNonTerminatingFinalStmtAsCaseExpr(loopExpr, returnTypeAndInvalidInit.sop, ctx, stmt.range, reassignedAndFlow, bodyStmts, loopReplacements));
|
|
401
429
|
return result;
|
|
402
430
|
}
|
|
@@ -4,6 +4,7 @@ import { TirForOfStmt } from "../../tir/statements/TirForOfStmt.js";
|
|
|
4
4
|
import { TirForStmt } from "../../tir/statements/TirForStmt.js";
|
|
5
5
|
import { TirWhileStmt } from "../../tir/statements/TirWhileStmt.js";
|
|
6
6
|
import { TirSoPStructType } from "../../tir/types/TirStructType.js";
|
|
7
|
+
import { TirType } from "../../tir/types/TirType.js";
|
|
7
8
|
import { ExpressifyCtx } from "./ExpressifyCtx.js";
|
|
8
9
|
export declare function loopToForStmt(stmt: TirWhileStmt | TirForOfStmt | TirForStmt): TirForStmt;
|
|
9
|
-
export declare function expressifyForStmt(ctx: ExpressifyCtx, stmt: TirForStmt, returnType: TirSoPStructType, bodyStateType: TirSoPStructType, initState: TirLitNamedObjExpr): TirCallExpr;
|
|
10
|
+
export declare function expressifyForStmt(ctx: ExpressifyCtx, stmt: TirForStmt, returnType: TirSoPStructType, bodyStateType: TirSoPStructType, initState: TirLitNamedObjExpr, bareReturnType?: TirType): TirCallExpr;
|
|
@@ -68,7 +68,16 @@ export function loopToForStmt(stmt) {
|
|
|
68
68
|
stmt.body, // loopBody
|
|
69
69
|
stmt.range);
|
|
70
70
|
}
|
|
71
|
-
export function expressifyForStmt(ctx, stmt, returnType, bodyStateType, initState
|
|
71
|
+
export function expressifyForStmt(ctx, stmt, returnType, bodyStateType, initState,
|
|
72
|
+
// Optimization: when the loop has exactly one reassigned variable
|
|
73
|
+
// and no user-written `return` inside its body, the SoP wrap
|
|
74
|
+
// (`Reassigns{var}`) on every iteration is unnecessary. The caller
|
|
75
|
+
// can opt into a bare-value lowering by passing the variable's type
|
|
76
|
+
// here; the loop function then returns that type directly, and the
|
|
77
|
+
// call site is expected to bind the result without a case-match.
|
|
78
|
+
bareReturnType) {
|
|
79
|
+
const effectiveReturnType = bareReturnType ?? returnType;
|
|
80
|
+
const isBareMode = bareReturnType !== undefined;
|
|
72
81
|
let loopBody = stmt.body instanceof TirBlockStmt ? stmt.body : new TirBlockStmt([stmt.body], stmt.range);
|
|
73
82
|
loopBody = new TirBlockStmt(loopBody.stmts.slice(), loopBody.range);
|
|
74
83
|
// add final loop updates
|
|
@@ -88,9 +97,27 @@ export function expressifyForStmt(ctx, stmt, returnType, bodyStateType, initStat
|
|
|
88
97
|
], loopBody.range);
|
|
89
98
|
}
|
|
90
99
|
const loopFuncName = getUniqueInternalName("loop");
|
|
91
|
-
const loopFuncType = new TirFuncT(bodyStateType.constructors[0].fields.map(f => f.type),
|
|
100
|
+
const loopFuncType = new TirFuncT(bodyStateType.constructors[0].fields.map(f => f.type), effectiveReturnType);
|
|
92
101
|
const loopReplacements = {
|
|
93
102
|
compileBreak(ctx, stmt) {
|
|
103
|
+
if (isBareMode) {
|
|
104
|
+
// Bare-value mode: the loop's return type IS the single
|
|
105
|
+
// user variable's type. `break` yields that var's current
|
|
106
|
+
// value directly, no SoP construction.
|
|
107
|
+
const userVarField = bodyStateType.constructors[0].fields[0];
|
|
108
|
+
const resolved = ctx.getVariable(userVarField.name);
|
|
109
|
+
if (isExpressifyFuncParam(resolved)) {
|
|
110
|
+
return new TirVariableAccessExpr({
|
|
111
|
+
variableInfos: {
|
|
112
|
+
name: resolved.name,
|
|
113
|
+
type: resolved.type,
|
|
114
|
+
isConstant: false
|
|
115
|
+
},
|
|
116
|
+
isDefinedOutsideFuncScope: false
|
|
117
|
+
}, stmt.range);
|
|
118
|
+
}
|
|
119
|
+
return resolved;
|
|
120
|
+
}
|
|
94
121
|
// return first constructor of the return type
|
|
95
122
|
const ctor = returnType.constructors[0];
|
|
96
123
|
return new TirLitNamedObjExpr(new Identifier(ctor.name, stmt.range), ctor.fields.map(f => new Identifier(f.name, stmt.range)), bodyStateType.constructors[0].fields
|
|
@@ -111,10 +138,21 @@ export function expressifyForStmt(ctx, stmt, returnType, bodyStateType, initStat
|
|
|
111
138
|
}), returnType, stmt.range);
|
|
112
139
|
},
|
|
113
140
|
replaceReturnValue(ctx, stmt) {
|
|
114
|
-
//
|
|
141
|
+
// Synthetic returns inserted by `expressifyIfBranch` (and
|
|
142
|
+
// analogous callers) carry the inner-if's SoP value as the
|
|
143
|
+
// continuation of the ternary they participate in — they are
|
|
144
|
+
// NOT user-written returns escaping the loop. When the loop
|
|
145
|
+
// has no user-written `return` in its body, `returnType` has
|
|
146
|
+
// only the "break/continue" constructor: in that case the
|
|
147
|
+
// value flowing through is already the right type and we
|
|
148
|
+
// must leave it untouched. (Previously this threw "No return
|
|
149
|
+
// constructor found in return type" when a for-of body
|
|
150
|
+
// contained an `if` that mutated a captured `let` — the
|
|
151
|
+
// synthesized branch-tail returns hit this path even though
|
|
152
|
+
// the user had no `return` inside the loop.)
|
|
115
153
|
const ctor = returnType.constructors[1];
|
|
116
154
|
if (!ctor) {
|
|
117
|
-
|
|
155
|
+
return stmt.value;
|
|
118
156
|
}
|
|
119
157
|
return new TirLitNamedObjExpr(new Identifier(ctor.name, stmt.range), [new Identifier(ctor.fields[0].name, stmt.range)], [stmt.value], returnType, stmt.range);
|
|
120
158
|
},
|
|
@@ -149,7 +187,7 @@ export function expressifyForStmt(ctx, stmt, returnType, bodyStateType, initStat
|
|
|
149
187
|
}, stmt.range);
|
|
150
188
|
}
|
|
151
189
|
return resolved;
|
|
152
|
-
}),
|
|
190
|
+
}), effectiveReturnType, stmt.range);
|
|
153
191
|
},
|
|
154
192
|
};
|
|
155
193
|
const loopCompilationCtx = ctx.newChild();
|
|
@@ -165,7 +203,7 @@ export function expressifyForStmt(ctx, stmt, returnType, bodyStateType, initStat
|
|
|
165
203
|
false, // is constant
|
|
166
204
|
stmt.range)),
|
|
167
205
|
// func return type
|
|
168
|
-
|
|
206
|
+
effectiveReturnType,
|
|
169
207
|
// func body
|
|
170
208
|
new TirBlockStmt([
|
|
171
209
|
new TirReturnStmt(expressifyFuncBody(loopCompilationCtx, loopBody.stmts, loopReplacements, [] // assertions
|
|
@@ -176,5 +214,5 @@ export function expressifyForStmt(ctx, stmt, returnType, bodyStateType, initStat
|
|
|
176
214
|
);
|
|
177
215
|
return new TirCallExpr(loopFuncExpr,
|
|
178
216
|
// loop call init args
|
|
179
|
-
initState.values.map(v => expressifyVars(ctx, v.clone())),
|
|
217
|
+
initState.values.map(v => expressifyVars(ctx, v.clone())), effectiveReturnType, stmt.range);
|
|
180
218
|
}
|