@harmoniclabs/pebble 0.2.0 → 0.3.1
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 +22 -2
- 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 +27 -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 +12 -0
- package/dist/IR/toUPLC/CompilerOptions.js +14 -9
- 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/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 +1 -0
- package/dist/compiler/AstCompiler/AstCompiler.js +41 -4
- package/dist/compiler/AstCompiler/internal/_deriveContractBody/_deriveContractBody.js +3 -3
- package/dist/compiler/AstCompiler/internal/exprs/_compileCallExpr.js +57 -10
- package/dist/compiler/AstCompiler/internal/exprs/_compileCaseExpr.js +31 -0
- package/dist/compiler/AstCompiler/internal/exprs/_compileIsExpr.js +12 -0
- package/dist/compiler/AstCompiler/internal/exprs/_compilePropAccessExpr.js +36 -0
- package/dist/compiler/AstCompiler/internal/exprs/_compileUnaryPrefixExpr.js +13 -1
- 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/utils/getPropAccessReturnType.js +11 -0
- package/dist/compiler/Compiler.js +20 -27
- 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 +23 -8
- 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/TirFromDataExpr.js +102 -67
- package/dist/compiler/tir/expressions/TirIsExpr.js +14 -1
- package/dist/compiler/tir/expressions/TirNativeFunc.d.ts +1 -2
- package/dist/compiler/tir/expressions/TirNativeFunc.js +2 -12
- package/dist/compiler/tir/expressions/TirToDataExpr.js +3 -0
- 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/stdScope/populateStdNamespace.js +49 -4
- package/dist/compiler/tir/program/stdScope/prelude/preludeTypesSrc.js +35 -2
- package/dist/compiler/tir/program/stdScope/stdScope.d.ts +7 -0
- package/dist/compiler/tir/program/stdScope/stdScope.js +83 -40
- 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 +4 -2
- package/dist/compiler/tir/types/TirNativeType/TirNativeType.js +5 -0
- 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 +4 -1
- package/dist/compiler/tir/types/utils/canAssignTo.js +28 -0
- 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.js +4 -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.js +5 -0
- package/dist/diagnostics/diagnosticMessages.generated.d.ts +5 -0
- package/dist/diagnostics/diagnosticMessages.generated.js +10 -0
- package/dist/parser/Parser.js +29 -13
- package/dist/tokenizer/Token.d.ts +8 -7
- package/dist/tokenizer/Token.js +8 -7
- package/dist/tokenizer/utils/tokenFromKeyword.js +2 -0
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/package.json +3 -3
- package/dist/IR/tree_utils/_ir_lazyChooseList.d.ts +0 -3
- package/dist/IR/tree_utils/_ir_lazyChooseList.js +0 -7
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Case-over-Const lowering pass.
|
|
3
|
+
*
|
|
4
|
+
* The UPLC `Case` term accepts a constant scrutinee and reinterprets it
|
|
5
|
+
* as a tag-untagged constructor (bool → 0/1, int N → N, unit → 0,
|
|
6
|
+
* pair → constr 0 [fst, snd], list → constr 1 [] for `[]` or
|
|
7
|
+
* constr 0 [head, tail] for cons).
|
|
8
|
+
*
|
|
9
|
+
* This pass replaces canonical `strictIfThenElse(cond, then, else)`
|
|
10
|
+
* sequences with the equivalent `IRCase(cond, [else, then])` form. The
|
|
11
|
+
* machine resolves bool=false → constr 0, bool=true → constr 1.
|
|
12
|
+
* This subsumes the strict boolean helpers (`_not`, `_strictAnd`,
|
|
13
|
+
* `_strictOr`) whose hoisted bodies are themselves `strictIfThenElse`
|
|
14
|
+
* triple-apps — they get lowered inside their hoisted definitions.
|
|
15
|
+
*
|
|
16
|
+
* It also replaces the LAZY pattern emitted by `_ir_lazyIfThenElse`:
|
|
17
|
+
*
|
|
18
|
+
* IRForced( strictIfThenElse cond (delay t) (delay e) )
|
|
19
|
+
*
|
|
20
|
+
* — which is how `&&`, `||` and pebble `if/else` are encoded today —
|
|
21
|
+
* with `IRCase(cond, [e, t])`. Branches under `Case` are naturally
|
|
22
|
+
* lazy, so the surrounding `force`/`delay` indirection is dropped.
|
|
23
|
+
*
|
|
24
|
+
* It also prunes trailing IRError continuations: a missing branch is
|
|
25
|
+
* semantically equivalent to an evaluation-failure branch, so dropping a
|
|
26
|
+
* trailing `IRError` reduces script size while preserving meaning.
|
|
27
|
+
*
|
|
28
|
+
* NOTE: `getApplicationTerms` sees through both raw `IRApp` chains AND
|
|
29
|
+
* the case-constr-app encoding produced earlier in `performUplc…`
|
|
30
|
+
* (i.e. `IRCase(IRConstr(0, [args]), [func])`). We therefore check the
|
|
31
|
+
* app-pattern *before* the IRCase trailing-error pruning so that a case
|
|
32
|
+
* that's really a function call gets unwrapped first.
|
|
33
|
+
*/
|
|
34
|
+
import { IRCase } from "../../IRNodes/IRCase.js";
|
|
35
|
+
import { IRDelayed } from "../../IRNodes/IRDelayed.js";
|
|
36
|
+
import { IRError } from "../../IRNodes/IRError.js";
|
|
37
|
+
import { IRForced } from "../../IRNodes/IRForced.js";
|
|
38
|
+
import { IRHoisted } from "../../IRNodes/IRHoisted.js";
|
|
39
|
+
import { IRLetted } from "../../IRNodes/IRLetted.js";
|
|
40
|
+
import { IRNative } from "../../IRNodes/IRNative/index.js";
|
|
41
|
+
import { IRNativeTag } from "../../IRNodes/IRNative/IRNativeTag.js";
|
|
42
|
+
import { IRFunc } from "../../IRNodes/IRFunc.js";
|
|
43
|
+
import { _modifyChildFromTo } from "../_internal/_modifyChildFromTo.js";
|
|
44
|
+
import { getApplicationTerms } from "../utils/getApplicationTerms.js";
|
|
45
|
+
/**
|
|
46
|
+
* `_makeAllNegativeNativesHoisted` wraps every `IRNative` reference in
|
|
47
|
+
* an `IRHoisted` (despite the name, it touches positive tags too), and
|
|
48
|
+
* subsequent sharing passes may further wrap in `IRLetted`. Unwrap so
|
|
49
|
+
* the pattern detector can see the underlying native tag.
|
|
50
|
+
*/
|
|
51
|
+
function unwrapToNative(t) {
|
|
52
|
+
while (t instanceof IRHoisted)
|
|
53
|
+
t = t.hoisted;
|
|
54
|
+
while (t instanceof IRLetted)
|
|
55
|
+
t = t.value;
|
|
56
|
+
return t;
|
|
57
|
+
}
|
|
58
|
+
export function rewriteToCaseOverConstAndReturnRoot(term) {
|
|
59
|
+
const stack = [term];
|
|
60
|
+
function modifyTermAndPushToReprocess(current, newTerm) {
|
|
61
|
+
const parent = current.parent;
|
|
62
|
+
if (parent) {
|
|
63
|
+
_modifyChildFromTo(parent, current, newTerm);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
term = newTerm;
|
|
67
|
+
term.parent = undefined;
|
|
68
|
+
}
|
|
69
|
+
stack.unshift(newTerm);
|
|
70
|
+
}
|
|
71
|
+
while (stack.length > 0) {
|
|
72
|
+
const current = stack.pop();
|
|
73
|
+
// LAZY pattern emitted by `_ir_lazyIfThenElse`:
|
|
74
|
+
// force( strictIfThenElse cond (delay t) (delay e) )
|
|
75
|
+
// Rewrite to a bare `case cond [e, t]` (Case branches are lazy).
|
|
76
|
+
if (current instanceof IRForced) {
|
|
77
|
+
const innerApp = getApplicationTerms(current.forced);
|
|
78
|
+
const innerFunc = innerApp ? unwrapToNative(innerApp.func) : undefined;
|
|
79
|
+
if (innerApp
|
|
80
|
+
&& innerFunc instanceof IRNative
|
|
81
|
+
&& innerFunc.tag === IRNativeTag.strictIfThenElse
|
|
82
|
+
&& innerApp.args.length === 3
|
|
83
|
+
&& innerApp.args[1] instanceof IRDelayed
|
|
84
|
+
&& innerApp.args[2] instanceof IRDelayed) {
|
|
85
|
+
const cond = innerApp.args[0];
|
|
86
|
+
const tBranch = innerApp.args[1].delayed;
|
|
87
|
+
const eBranch = innerApp.args[2].delayed;
|
|
88
|
+
const newTerm = new IRCase(cond, [eBranch, tBranch]);
|
|
89
|
+
modifyTermAndPushToReprocess(current, newTerm);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
// `IRForced(IRCase(s, [b0, b1, …]))` where every branch is
|
|
93
|
+
// either `IRDelayed(v)` or `IRFunc(params, IRDelayed(v))` can
|
|
94
|
+
// be simplified by stripping the force/delay pair: case
|
|
95
|
+
// branches are naturally lazy in UPLC `case`. The
|
|
96
|
+
// wrap typically comes from `_ir_lazyIfThenElse` lowering an
|
|
97
|
+
// `if/else` whose condition then got rewritten to a list-case
|
|
98
|
+
// (e.g. `if(nullList(L)) ...`) — the outer force is left
|
|
99
|
+
// dangling around the resulting IRCase.
|
|
100
|
+
if (current.forced instanceof IRCase) {
|
|
101
|
+
const caseTerm = current.forced;
|
|
102
|
+
const conts = caseTerm.continuations;
|
|
103
|
+
const stripped = [];
|
|
104
|
+
let allOk = true;
|
|
105
|
+
for (let i = 0; i < conts.length; i++) {
|
|
106
|
+
const c = conts[i];
|
|
107
|
+
if (c instanceof IRDelayed) {
|
|
108
|
+
stripped.push(c.delayed);
|
|
109
|
+
}
|
|
110
|
+
else if (c instanceof IRFunc
|
|
111
|
+
&& c.body instanceof IRDelayed) {
|
|
112
|
+
stripped.push(new IRFunc(c.params.slice(), c.body.delayed));
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
allOk = false;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (allOk) {
|
|
120
|
+
const newCase = new IRCase(caseTerm.constrTerm.clone(), stripped);
|
|
121
|
+
modifyTermAndPushToReprocess(current, newCase);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
stack.unshift(...current.children());
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
// Application-pattern rewrites (raw IRApp chain OR case-constr-app
|
|
129
|
+
// encoding produced by `performUplcOptimizationsAndReturnRoot`).
|
|
130
|
+
const appTerms = getApplicationTerms(current);
|
|
131
|
+
if (appTerms) {
|
|
132
|
+
const { func, args } = appTerms;
|
|
133
|
+
const unwrappedFunc = unwrapToNative(func);
|
|
134
|
+
// strictIfThenElse cond then else → case cond [else, then]
|
|
135
|
+
if (unwrappedFunc instanceof IRNative
|
|
136
|
+
&& unwrappedFunc.tag === IRNativeTag.strictIfThenElse
|
|
137
|
+
&& args.length === 3) {
|
|
138
|
+
const [cond, thenBranch, elseBranch] = args;
|
|
139
|
+
const newTerm = new IRCase(cond, [elseBranch, thenBranch]);
|
|
140
|
+
modifyTermAndPushToReprocess(current, newTerm);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
// No app-pattern match — but if the node is itself an IRCase
|
|
144
|
+
// (a case-constr-app encoding), we still want trailing-error
|
|
145
|
+
// pruning on the original case, so don't `continue` yet.
|
|
146
|
+
if (!(current instanceof IRCase)) {
|
|
147
|
+
stack.unshift(...current.children());
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Trailing-error pruning on any IRCase node we encounter.
|
|
152
|
+
if (current instanceof IRCase) {
|
|
153
|
+
const conts = current.continuations;
|
|
154
|
+
let lastNonError = conts.length;
|
|
155
|
+
while (lastNonError > 0 && conts[lastNonError - 1] instanceof IRError) {
|
|
156
|
+
lastNonError--;
|
|
157
|
+
}
|
|
158
|
+
if (lastNonError < conts.length && lastNonError > 0) {
|
|
159
|
+
const pruned = new IRCase(current.constrTerm.clone(), Array.from({ length: lastNonError }, (_, i) => conts[i].clone()));
|
|
160
|
+
modifyTermAndPushToReprocess(current, pruned);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
stack.unshift(...current.children());
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
stack.unshift(...current.children());
|
|
167
|
+
}
|
|
168
|
+
return term;
|
|
169
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { IRCase } from "../IRNodes/IRCase.js";
|
|
2
|
+
import type { IRTerm } from "../IRTerm.js";
|
|
3
|
+
/**
|
|
4
|
+
* case list of
|
|
5
|
+
* h::t -> caseCons (h, t are bound only if `consParams` is provided)
|
|
6
|
+
* [] -> caseNil
|
|
7
|
+
*
|
|
8
|
+
* Lowers to UPLC `Case`. Branches are naturally lazy, so callers
|
|
9
|
+
* should pass plain (un-delayed) expressions — no `force` is needed around
|
|
10
|
+
* the result.
|
|
11
|
+
*/
|
|
12
|
+
export declare function _ir_caseList(listTerm: IRTerm, caseNil: IRTerm, caseCons: IRTerm, consParams?: {
|
|
13
|
+
head: symbol;
|
|
14
|
+
tail: symbol;
|
|
15
|
+
}): IRCase;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { IRCase } from "../IRNodes/IRCase.js";
|
|
2
|
+
import { IRFunc } from "../IRNodes/IRFunc.js";
|
|
3
|
+
/**
|
|
4
|
+
* case list of
|
|
5
|
+
* h::t -> caseCons (h, t are bound only if `consParams` is provided)
|
|
6
|
+
* [] -> caseNil
|
|
7
|
+
*
|
|
8
|
+
* Lowers to UPLC `Case`. Branches are naturally lazy, so callers
|
|
9
|
+
* should pass plain (un-delayed) expressions — no `force` is needed around
|
|
10
|
+
* the result.
|
|
11
|
+
*/
|
|
12
|
+
export function _ir_caseList(listTerm, caseNil, caseCons, consParams) {
|
|
13
|
+
const h = consParams?.head ?? Symbol("_caseList_h");
|
|
14
|
+
const t = consParams?.tail ?? Symbol("_caseList_t");
|
|
15
|
+
return new IRCase(listTerm, [
|
|
16
|
+
new IRFunc([h, t], caseCons),
|
|
17
|
+
caseNil
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
@@ -4,9 +4,23 @@ import { HasSourceRange } from "../../HasSourceRange.js";
|
|
|
4
4
|
import { SimpleVarDecl } from "./VarDecl/SimpleVarDecl.js";
|
|
5
5
|
export declare enum StructDeclAstFlags {
|
|
6
6
|
none = 0,
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Hint that the user used the shortcut single-constructor syntax
|
|
9
|
+
* (`struct Foo { x: int }` rather than `struct Foo { Foo { x: int } }`).
|
|
10
|
+
* Whether the resulting Data encoding is tagged (`constrData(0, ...)`)
|
|
11
|
+
* or untagged (`listData(...)`) is decided by the compiler's
|
|
12
|
+
* `encodingStrategy` option — `"default"` keeps the tagged form for
|
|
13
|
+
* backwards compatibility; `"minimal"` opts shortcut forms in to the
|
|
14
|
+
* untagged form.
|
|
15
|
+
*/
|
|
16
|
+
shortcutSingleConstructor = 1,
|
|
8
17
|
onlyDataEncoding = 2,
|
|
9
|
-
onlySopEncoding = 4
|
|
18
|
+
onlySopEncoding = 4,
|
|
19
|
+
/**
|
|
20
|
+
* Explicit `untagged` modifier. Forces the untagged listData encoding
|
|
21
|
+
* regardless of `encodingStrategy`. Requires a single constructor.
|
|
22
|
+
*/
|
|
23
|
+
untagged = 8
|
|
10
24
|
}
|
|
11
25
|
export declare class StructDecl implements HasSourceRange {
|
|
12
26
|
readonly name: Identifier;
|
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
export var StructDeclAstFlags;
|
|
2
2
|
(function (StructDeclAstFlags) {
|
|
3
3
|
StructDeclAstFlags[StructDeclAstFlags["none"] = 0] = "none";
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Hint that the user used the shortcut single-constructor syntax
|
|
6
|
+
* (`struct Foo { x: int }` rather than `struct Foo { Foo { x: int } }`).
|
|
7
|
+
* Whether the resulting Data encoding is tagged (`constrData(0, ...)`)
|
|
8
|
+
* or untagged (`listData(...)`) is decided by the compiler's
|
|
9
|
+
* `encodingStrategy` option — `"default"` keeps the tagged form for
|
|
10
|
+
* backwards compatibility; `"minimal"` opts shortcut forms in to the
|
|
11
|
+
* untagged form.
|
|
12
|
+
*/
|
|
13
|
+
StructDeclAstFlags[StructDeclAstFlags["shortcutSingleConstructor"] = 1] = "shortcutSingleConstructor";
|
|
5
14
|
StructDeclAstFlags[StructDeclAstFlags["onlyDataEncoding"] = 2] = "onlyDataEncoding";
|
|
6
15
|
StructDeclAstFlags[StructDeclAstFlags["onlySopEncoding"] = 4] = "onlySopEncoding";
|
|
16
|
+
/**
|
|
17
|
+
* Explicit `untagged` modifier. Forces the untagged listData encoding
|
|
18
|
+
* regardless of `encodingStrategy`. Requires a single constructor.
|
|
19
|
+
*/
|
|
20
|
+
StructDeclAstFlags[StructDeclAstFlags["untagged"] = 8] = "untagged";
|
|
7
21
|
})(StructDeclAstFlags || (StructDeclAstFlags = {}));
|
|
8
22
|
export class StructDecl {
|
|
9
23
|
name;
|
|
@@ -117,6 +117,7 @@ export declare class AstCompiler extends DiagnosticEmitter {
|
|
|
117
117
|
private _collectTypeDeclarations;
|
|
118
118
|
private _compileStructDecl;
|
|
119
119
|
private _compileTypeAliasDecl;
|
|
120
|
+
private _compileEnumDecl;
|
|
120
121
|
private _consumeImportsAddSymsInScope;
|
|
121
122
|
private _readFile;
|
|
122
123
|
/** MUST NOT be used as a "seen" log */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { StructDecl, StructDeclAstFlags } from "../../ast/nodes/statements/declarations/StructDecl.js";
|
|
2
2
|
import { TypeAliasDecl } from "../../ast/nodes/statements/declarations/TypeAliasDecl.js";
|
|
3
|
+
import { EnumDecl } from "../../ast/nodes/statements/declarations/EnumDecl.js";
|
|
3
4
|
import { ExportStarStmt } from "../../ast/nodes/statements/ExportStarStmt.js";
|
|
4
5
|
import { ImportStarStmt } from "../../ast/nodes/statements/ImportStarStmt.js";
|
|
5
6
|
import { ImportStmt } from "../../ast/nodes/statements/ImportStmt.js";
|
|
@@ -12,6 +13,7 @@ import { createMemoryCompilerIoApi } from "../io/CompilerIoApi.js";
|
|
|
12
13
|
import { TypedProgram } from "../tir/program/TypedProgram.js";
|
|
13
14
|
import { TirAliasType } from "../tir/types/TirAliasType.js";
|
|
14
15
|
import { TirDataStructType, TirSoPStructType, TirStructConstr, TirStructField } from "../tir/types/TirStructType.js";
|
|
16
|
+
import { TirEnumType } from "../tir/types/TirEnumType.js";
|
|
15
17
|
import { ExportStmt } from "../../ast/nodes/statements/ExportStmt.js";
|
|
16
18
|
import { ResolveStackNode } from "./utils/deps/ResolveStackNode.js";
|
|
17
19
|
import { AstCompilationCtx } from "./AstCompilationCtx.js";
|
|
@@ -675,12 +677,15 @@ export class AstCompiler extends DiagnosticEmitter {
|
|
|
675
677
|
stmt = stmt.stmt;
|
|
676
678
|
}
|
|
677
679
|
if (!(stmt instanceof StructDecl
|
|
678
|
-
|| stmt instanceof TypeAliasDecl
|
|
680
|
+
|| stmt instanceof TypeAliasDecl
|
|
681
|
+
|| stmt instanceof EnumDecl))
|
|
679
682
|
continue;
|
|
680
|
-
const isGeneric = stmt.typeParams.length > 0;
|
|
683
|
+
const isGeneric = stmt instanceof EnumDecl ? false : stmt.typeParams.length > 0;
|
|
681
684
|
const tirTypes = stmt instanceof StructDecl
|
|
682
685
|
? this._compileStructDecl(stmt, srcUid, topLevelScope)
|
|
683
|
-
:
|
|
686
|
+
: stmt instanceof EnumDecl
|
|
687
|
+
? this._compileEnumDecl(stmt, srcUid, topLevelScope)
|
|
688
|
+
: this._compileTypeAliasDecl(stmt, srcUid, topLevelScope);
|
|
684
689
|
if (!tirTypes // undefined
|
|
685
690
|
|| !(tirTypes.sop || tirTypes.data) // or both undefined
|
|
686
691
|
)
|
|
@@ -743,6 +748,16 @@ export class AstCompiler extends DiagnosticEmitter {
|
|
|
743
748
|
// data encoded type
|
|
744
749
|
if (!stmt.hasFlag(StructDeclAstFlags.onlySopEncoding)) {
|
|
745
750
|
let canEncodeToData = true;
|
|
751
|
+
// Untagged Data encoding (`listData(...)`) instead of
|
|
752
|
+
// `constrData(0, ...)`. Requires exactly one constructor and is
|
|
753
|
+
// enabled by either the explicit `untagged` keyword or the
|
|
754
|
+
// shortcut-form syntax when `encodingStrategy === "minimal"`.
|
|
755
|
+
const isUntagged = stmt.constrs.length === 1 && (stmt.hasFlag(StructDeclAstFlags.untagged)
|
|
756
|
+
|| (stmt.hasFlag(StructDeclAstFlags.shortcutSingleConstructor)
|
|
757
|
+
&& this.cfg.encodingStrategy === "minimal"));
|
|
758
|
+
if (stmt.hasFlag(StructDeclAstFlags.untagged) && stmt.constrs.length !== 1) {
|
|
759
|
+
this.error(DiagnosticCode.Not_implemented_0, stmt.name.range, "`untagged` struct must have exactly one constructor");
|
|
760
|
+
}
|
|
746
761
|
const dataType = new TirDataStructType(stmt.name.text, srcUid, stmt.constrs.map(ctor => new TirStructConstr(ctor.name.text, ctor.fields.map(field => {
|
|
747
762
|
if (!field.type)
|
|
748
763
|
return compiler.error(DiagnosticCode.Type_expected, field.name.range.atEnd());
|
|
@@ -756,7 +771,7 @@ export class AstCompiler extends DiagnosticEmitter {
|
|
|
756
771
|
}
|
|
757
772
|
return new TirStructField(field.name.text, fieldType);
|
|
758
773
|
})
|
|
759
|
-
.filter(f => f instanceof TirStructField))), methodsNames);
|
|
774
|
+
.filter(f => f instanceof TirStructField))), methodsNames, isUntagged);
|
|
760
775
|
if (canEncodeToData)
|
|
761
776
|
data = dataType;
|
|
762
777
|
else if (stmt.hasFlag(StructDeclAstFlags.onlyDataEncoding))
|
|
@@ -779,6 +794,28 @@ export class AstCompiler extends DiagnosticEmitter {
|
|
|
779
794
|
) : undefined;
|
|
780
795
|
return sop || data ? { sop, data, methodsNames } : undefined;
|
|
781
796
|
}
|
|
797
|
+
_compileEnumDecl(stmt, srcUid, _topLevelScope) {
|
|
798
|
+
if (stmt.members.length === 0) {
|
|
799
|
+
this.error(DiagnosticCode.Enum_must_have_at_least_one_member, stmt.name.range);
|
|
800
|
+
return undefined;
|
|
801
|
+
}
|
|
802
|
+
const seen = new Set();
|
|
803
|
+
const memberNames = [];
|
|
804
|
+
for (const m of stmt.members) {
|
|
805
|
+
if (m.value !== undefined) {
|
|
806
|
+
this.error(DiagnosticCode.Enum_members_cannot_have_explicit_values, m.range);
|
|
807
|
+
}
|
|
808
|
+
if (seen.has(m.name.text)) {
|
|
809
|
+
this.error(DiagnosticCode.Duplicate_enum_member_0, m.name.range, m.name.text);
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
seen.add(m.name.text);
|
|
813
|
+
memberNames.push(m.name.text);
|
|
814
|
+
}
|
|
815
|
+
const methodsNames = new Map();
|
|
816
|
+
const enumType = new TirEnumType(stmt.name.text, srcUid, memberNames, methodsNames);
|
|
817
|
+
return { sop: enumType, data: enumType, methodsNames };
|
|
818
|
+
}
|
|
782
819
|
_consumeImportsAddSymsInScope(stmts, srcAbsPath, srcImportsScope) {
|
|
783
820
|
for (let i = 0; i < stmts.length; i++) {
|
|
784
821
|
const stmt = stmts[i];
|
|
@@ -784,7 +784,7 @@ function _exprReplaceParamsAndAssertNoLitContext(compiler, expr, paramsInternalN
|
|
|
784
784
|
function _deriveContractDatumTypeDef(contractName, stateDecls, contractRange) {
|
|
785
785
|
let defFlags = StructDeclAstFlags.onlyDataEncoding;
|
|
786
786
|
if (stateDecls.length <= 1)
|
|
787
|
-
defFlags |= StructDeclAstFlags.
|
|
787
|
+
defFlags |= StructDeclAstFlags.shortcutSingleConstructor;
|
|
788
788
|
return new StructDecl(new Identifier(contractName, contractRange), [], // typeParams
|
|
789
789
|
stateDecls.map(s => new StructConstrDecl(new Identifier(s.name.text, s.name.range), s.fields, s.range)), defFlags, contractRange);
|
|
790
790
|
}
|
|
@@ -855,7 +855,7 @@ function _buildSpendCaseBlock(compiler, contractDecl, paramsInternalNamesMap, ba
|
|
|
855
855
|
const perStateStructDecl = new StructDecl(new Identifier(getUniqueInternalName(`${contractDecl.name.text}_${stateDecl.name.text}`), mockRange), [], // typeParams
|
|
856
856
|
[
|
|
857
857
|
new StructConstrDecl(new Identifier(stateDecl.name.text, mockRange), stateDecl.fields, stateDecl.range)
|
|
858
|
-
], StructDeclAstFlags.onlyDataEncoding | StructDeclAstFlags.
|
|
858
|
+
], StructDeclAstFlags.onlyDataEncoding | StructDeclAstFlags.shortcutSingleConstructor, stateDecl.range);
|
|
859
859
|
compiler.registerInternalTypeDecl(perStateStructDecl);
|
|
860
860
|
const perStateStructName = perStateStructDecl.name.text;
|
|
861
861
|
// synthesize: const state = StateStruct{ field1: _f1, ... };
|
|
@@ -921,7 +921,7 @@ function _buildSpendCaseBlock(compiler, contractDecl, paramsInternalNamesMap, ba
|
|
|
921
921
|
function _deriveRedeemerTypeDef(redeemerName, methods, contractRange) {
|
|
922
922
|
let defFlags = StructDeclAstFlags.onlyDataEncoding;
|
|
923
923
|
if (methods.length <= 1)
|
|
924
|
-
defFlags |= StructDeclAstFlags.
|
|
924
|
+
defFlags |= StructDeclAstFlags.shortcutSingleConstructor;
|
|
925
925
|
const uniqueName = getUniqueInternalName(redeemerName);
|
|
926
926
|
return new StructDecl(new Identifier(uniqueName, SourceRange.mock), [], // typeParams
|
|
927
927
|
methods.map(m => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FuncExpr } from "../../../../ast/nodes/expr/functions/FuncExpr.js";
|
|
1
2
|
import { DiagnosticCode } from "../../../../diagnostics/diagnosticMessages.generated.js";
|
|
2
3
|
import { TirCallExpr } from "../../../tir/expressions/TirCallExpr.js";
|
|
3
4
|
import { TirFuncT } from "../../../tir/types/TirNativeType/native/function.js";
|
|
@@ -48,15 +49,62 @@ export function _compileCallExpr(ctx, expr, typeHint) {
|
|
|
48
49
|
}
|
|
49
50
|
// 2) Compile arguments. We need them anyway, and they're used for
|
|
50
51
|
// inference when no explicit args were given.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
52
|
+
//
|
|
53
|
+
// For inferred-type-args calls, lambda arguments cannot be compiled
|
|
54
|
+
// without an expected function type (their param types would be
|
|
55
|
+
// unannotated). So we compile non-lambda args first, build an
|
|
56
|
+
// inference environment from them, then compile lambda args with
|
|
57
|
+
// the substituted expected type from `funcType.argTypes[i]`.
|
|
58
|
+
const tirArgs = new Array(expr.args.length);
|
|
59
|
+
const usable = Math.min(expr.args.length, funcType.argTypes.length);
|
|
60
|
+
if (explicitArgs) {
|
|
61
|
+
const explicitSubst = new Map(template.typeParams.map((tp, idx) => [tp.symbol, explicitArgs[idx]]));
|
|
62
|
+
for (let i = 0; i < expr.args.length; i++) {
|
|
63
|
+
const expected = i < funcType.argTypes.length
|
|
64
|
+
? substituteTypeParams(funcType.argTypes[i], explicitSubst)
|
|
65
|
+
: undefined;
|
|
66
|
+
const compiled = _compileExpr(ctx, expr.args[i], expected);
|
|
67
|
+
if (!compiled)
|
|
68
|
+
return undefined;
|
|
69
|
+
tirArgs[i] = compiled;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
const env = new Map();
|
|
74
|
+
// Pass 1: compile non-FuncExpr args (no type hint needed) and
|
|
75
|
+
// populate `env` from each arg's resolved type.
|
|
76
|
+
for (let i = 0; i < expr.args.length; i++) {
|
|
77
|
+
if (expr.args[i] instanceof FuncExpr)
|
|
78
|
+
continue;
|
|
79
|
+
const compiled = _compileExpr(ctx, expr.args[i], undefined);
|
|
80
|
+
if (!compiled)
|
|
81
|
+
return undefined;
|
|
82
|
+
tirArgs[i] = compiled;
|
|
83
|
+
if (i < usable) {
|
|
84
|
+
if (!inferTypeArgs(funcType.argTypes[i], compiled.type, env))
|
|
85
|
+
return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, expr.args[i].range, compiled.type.toString(), funcType.argTypes[i].toString());
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Pass 2: compile FuncExpr args with the substituted expected
|
|
89
|
+
// type so lambda param types can be inferred from context.
|
|
90
|
+
for (let i = 0; i < expr.args.length; i++) {
|
|
91
|
+
if (!(expr.args[i] instanceof FuncExpr))
|
|
92
|
+
continue;
|
|
93
|
+
const expected = i < funcType.argTypes.length
|
|
94
|
+
? substituteTypeParams(funcType.argTypes[i], env)
|
|
95
|
+
: undefined;
|
|
96
|
+
const compiled = _compileExpr(ctx, expr.args[i], expected);
|
|
97
|
+
if (!compiled)
|
|
98
|
+
return undefined;
|
|
99
|
+
tirArgs[i] = compiled;
|
|
100
|
+
if (i < usable) {
|
|
101
|
+
// re-run to capture type params bound only via the
|
|
102
|
+
// lambda's return type (e.g. `map<T, A>(f: (T) -> A, ...)`)
|
|
103
|
+
if (!inferTypeArgs(funcType.argTypes[i], compiled.type, env))
|
|
104
|
+
return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, expr.args[i].range, compiled.type.toString(), funcType.argTypes[i].toString());
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
60
108
|
// 3) Determine final type arguments
|
|
61
109
|
let resolvedArgs;
|
|
62
110
|
if (explicitArgs) {
|
|
@@ -64,7 +112,6 @@ export function _compileCallExpr(ctx, expr, typeHint) {
|
|
|
64
112
|
}
|
|
65
113
|
else {
|
|
66
114
|
const env = new Map();
|
|
67
|
-
const usable = Math.min(tirArgs.length, funcType.argTypes.length);
|
|
68
115
|
for (let i = 0; i < usable; i++) {
|
|
69
116
|
if (!inferTypeArgs(funcType.argTypes[i], tirArgs[i].type, env)) {
|
|
70
117
|
return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, expr.args[i].range, tirArgs[i].type.toString(), funcType.argTypes[i].toString());
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { Identifier } from "../../../../ast/nodes/common/Identifier.js";
|
|
2
2
|
import { ParentesizedExpr } from "../../../../ast/nodes/expr/ParentesizedExpr.js";
|
|
3
|
+
import { NamedDeconstructVarDecl as AstNamedDeconstructVarDecl } from "../../../../ast/nodes/statements/declarations/VarDecl/NamedDeconstructVarDecl.js";
|
|
4
|
+
import { SimpleVarDecl as AstSimpleVarDecl } from "../../../../ast/nodes/statements/declarations/VarDecl/SimpleVarDecl.js";
|
|
3
5
|
import { DiagnosticCode } from "../../../../diagnostics/diagnosticMessages.generated.js";
|
|
4
6
|
import { TirCaseExpr, TirCaseMatcher, TirWildcardCaseMatcher } from "../../../tir/expressions/TirCaseExpr.js";
|
|
5
7
|
import { TirNamedDeconstructVarDecl } from "../../../tir/statements/TirVarDecl/TirNamedDeconstructVarDecl.js";
|
|
6
8
|
import { TirSimpleVarDecl } from "../../../tir/statements/TirVarDecl/TirSimpleVarDecl.js";
|
|
7
9
|
import { TirDataStructType, TirSoPStructType } from "../../../tir/types/TirStructType.js";
|
|
10
|
+
import { getEnumType } from "../../../tir/types/TirEnumType.js";
|
|
8
11
|
import { canAssignTo, getStructType } from "../../../tir/types/utils/canAssignTo.js";
|
|
9
12
|
import { _compileVarDecl } from "../statements/_compileVarStmt.js";
|
|
10
13
|
import { _compileExpr } from "./_compileExpr.js";
|
|
@@ -29,6 +32,34 @@ export function _compileCaseExpr(ctx, expr, typeHint) {
|
|
|
29
32
|
return new TirCaseExpr(matchExpr, cases, wildcardCase, returnType, expr.range);
|
|
30
33
|
}
|
|
31
34
|
export function _compileCaseExprMatcher(ctx, matcher, patternType, returnTypeHint, matchedVarName) {
|
|
35
|
+
const enumType = getEnumType(patternType);
|
|
36
|
+
if (enumType) {
|
|
37
|
+
const astPattern = matcher.pattern;
|
|
38
|
+
let memberName;
|
|
39
|
+
let patternRange = astPattern.range;
|
|
40
|
+
let ctorNameRange = astPattern.range;
|
|
41
|
+
if (astPattern instanceof AstSimpleVarDecl) {
|
|
42
|
+
memberName = astPattern.name.text;
|
|
43
|
+
patternRange = astPattern.name.range;
|
|
44
|
+
ctorNameRange = astPattern.name.range;
|
|
45
|
+
}
|
|
46
|
+
else if (astPattern instanceof AstNamedDeconstructVarDecl) {
|
|
47
|
+
if (astPattern.fields.size > 0 || astPattern.rest)
|
|
48
|
+
return ctx.error(DiagnosticCode.Enum_member_pattern_cannot_have_fields, astPattern.range);
|
|
49
|
+
memberName = astPattern.name.text;
|
|
50
|
+
ctorNameRange = astPattern.name.range;
|
|
51
|
+
}
|
|
52
|
+
else
|
|
53
|
+
return ctx.error(DiagnosticCode._case_expression_must_decontructed_the_inspected_value, astPattern.range);
|
|
54
|
+
if (enumType.indexOf(memberName) < 0)
|
|
55
|
+
return ctx.error(DiagnosticCode.Constructor_0_is_not_part_of_the_definition_of_1, ctorNameRange, memberName, enumType.toString());
|
|
56
|
+
const body = _compileExpr(ctx, matcher.body, returnTypeHint);
|
|
57
|
+
if (!body)
|
|
58
|
+
return undefined;
|
|
59
|
+
if (returnTypeHint && !canAssignTo(body.type, returnTypeHint))
|
|
60
|
+
return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, matcher.body.range, body.type.toString(), returnTypeHint.toString());
|
|
61
|
+
return new TirCaseMatcher(new TirNamedDeconstructVarDecl(memberName, new Map(), undefined, enumType, undefined, true, patternRange, ctorNameRange), body, matcher.range);
|
|
62
|
+
}
|
|
32
63
|
const pattern = _compileVarDecl(ctx, matcher.pattern, patternType);
|
|
33
64
|
if (!pattern)
|
|
34
65
|
return undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DiagnosticCode } from "../../../../diagnostics/diagnosticMessages.generated.js";
|
|
2
2
|
import { TirIsExpr } from "../../../tir/expressions/TirIsExpr.js";
|
|
3
|
+
import { getEnumType } from "../../../tir/types/TirEnumType.js";
|
|
3
4
|
import { getStructType } from "../../../tir/types/utils/canAssignTo.js";
|
|
4
5
|
import { _compileExpr } from "./_compileExpr.js";
|
|
5
6
|
export function _compileIsExpr(ctx, expr, _typeHint) {
|
|
@@ -7,6 +8,17 @@ export function _compileIsExpr(ctx, expr, _typeHint) {
|
|
|
7
8
|
const target = _compileExpr(ctx, expr.instanceExpr, undefined);
|
|
8
9
|
if (!target)
|
|
9
10
|
return undefined;
|
|
11
|
+
const enumType = getEnumType(target.type);
|
|
12
|
+
if (enumType) {
|
|
13
|
+
const memberName = expr.ofConstr.text;
|
|
14
|
+
const memberIdx = enumType.indexOf(memberName);
|
|
15
|
+
if (memberIdx < 0)
|
|
16
|
+
return ctx.error(DiagnosticCode.Constructor_0_is_not_part_of_the_definition_of_1, expr.ofConstr.range, memberName, enumType.toString());
|
|
17
|
+
if (enumType.members.length === 1) {
|
|
18
|
+
ctx.warning(DiagnosticCode.This_check_is_redundant_Struct_0_has_only_one_possible_constructor, expr.range, enumType.toString());
|
|
19
|
+
}
|
|
20
|
+
return new TirIsExpr(target, memberName, memberIdx, expr.range, bool_t);
|
|
21
|
+
}
|
|
10
22
|
const structType = getStructType(target.type);
|
|
11
23
|
if (!structType)
|
|
12
24
|
return ctx.error(DiagnosticCode.Cannot_use_is_operator_on_a_value_that_is_not_a_struct_type, expr.instanceExpr.range);
|
|
@@ -15,8 +15,10 @@ import { tryResolveNamespaceChain } from "../../utils/resolveNamespaceChain.js";
|
|
|
15
15
|
import { _compileExpr } from "./_compileExpr.js";
|
|
16
16
|
import { _compileNonNullExpr } from "./_compileNonNullExpr.js";
|
|
17
17
|
import { TirLitNamedObjExpr } from "../../../tir/expressions/litteral/TirLitNamedObjExpr.js";
|
|
18
|
+
import { TirLitEnumMemberExpr } from "../../../tir/expressions/litteral/TirLitEnumMemberExpr.js";
|
|
18
19
|
import { Identifier } from "../../../../ast/nodes/common/Identifier.js";
|
|
19
20
|
import { TirSopOptT } from "../../../tir/types/TirNativeType/native/Optional/sop.js";
|
|
21
|
+
import { TirEnumType } from "../../../tir/types/TirEnumType.js";
|
|
20
22
|
export function _compilePropAccessExpr(ctx, expr, typeHint) {
|
|
21
23
|
if (expr instanceof OptionalPropAccessExpr)
|
|
22
24
|
return _compileOptionalPropAccessExpr(ctx, expr, typeHint);
|
|
@@ -95,6 +97,12 @@ export function _compileNonNullPropAccessExpr(ctx, expr, _typeHint) {
|
|
|
95
97
|
return _compileDotPropAccessExpr(ctx, new DotPropAccessExpr(nonNullObjExpr, expr.prop, expr.range), _typeHint);
|
|
96
98
|
}
|
|
97
99
|
export function _compileDotPropAccessExpr(ctx, expr, _typeHint) {
|
|
100
|
+
// Enum member access: `EnumName.Member`
|
|
101
|
+
if (expr.object instanceof Identifier) {
|
|
102
|
+
const enumRes = _tryResolveEnumMemberAccess(ctx, expr.object, expr.prop, expr);
|
|
103
|
+
if (enumRes)
|
|
104
|
+
return enumRes;
|
|
105
|
+
}
|
|
98
106
|
// if the LHS is a (chain of) identifier(s) rooted at a namespace, resolve
|
|
99
107
|
// the entire dotted chain through namespaces.
|
|
100
108
|
const nsRes = tryResolveNamespaceChain(ctx, expr);
|
|
@@ -128,3 +136,31 @@ export function _compileDotPropAccessExpr(ctx, expr, _typeHint) {
|
|
|
128
136
|
return ctx.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, expr.prop.range, expr.prop.text, objType.toString());
|
|
129
137
|
return new TirPropAccessExpr(objExpr, expr.prop, returnType, expr.range);
|
|
130
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Returns a `TirLitEnumMemberExpr` if `objId` resolves to an enum type
|
|
141
|
+
* in scope and `propId` is one of its members.
|
|
142
|
+
* Emits a diagnostic and returns a placeholder failure (undefined) if the
|
|
143
|
+
* type is an enum but `propId` is not a member.
|
|
144
|
+
* Returns `undefined` (silently) if `objId` is not an enum-typed identifier
|
|
145
|
+
* — caller falls back to other resolution strategies.
|
|
146
|
+
*/
|
|
147
|
+
function _tryResolveEnumMemberAccess(ctx, objId, propId, fullExpr) {
|
|
148
|
+
// skip if the identifier names a value in scope (variables shadow types)
|
|
149
|
+
if (ctx.scope.resolveValue(objId.text))
|
|
150
|
+
return undefined;
|
|
151
|
+
const possibleTirTypes = ctx.scope.resolveType(objId.text);
|
|
152
|
+
if (!possibleTirTypes)
|
|
153
|
+
return undefined;
|
|
154
|
+
const tirType = ctx.program.types.get(possibleTirTypes.sopTirName)
|
|
155
|
+
?? (possibleTirTypes.dataTirName
|
|
156
|
+
? ctx.program.types.get(possibleTirTypes.dataTirName)
|
|
157
|
+
: undefined);
|
|
158
|
+
if (!(tirType instanceof TirEnumType))
|
|
159
|
+
return undefined;
|
|
160
|
+
const memberIdx = tirType.indexOf(propId.text);
|
|
161
|
+
if (memberIdx < 0) {
|
|
162
|
+
ctx.error(DiagnosticCode.Property_0_is_not_a_member_of_enum_1, propId.range, propId.text, tirType.name);
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
return new TirLitEnumMemberExpr(tirType, memberIdx, fullExpr.range);
|
|
166
|
+
}
|
|
@@ -7,7 +7,9 @@ import { TirUnaryExclamation } from "../../../tir/expressions/unary/TirUnaryExcl
|
|
|
7
7
|
import { TirUnaryMinus } from "../../../tir/expressions/unary/TirUnaryMinus.js";
|
|
8
8
|
import { TirUnaryPlus } from "../../../tir/expressions/unary/TirUnaryPlus.js";
|
|
9
9
|
import { TirUnaryTilde } from "../../../tir/expressions/unary/TirUnaryTilde.js";
|
|
10
|
+
import { TirValueT } from "../../../tir/types/TirNativeType/native/value.js";
|
|
10
11
|
import { canAssignTo, canAssignToOptional } from "../../../tir/types/utils/canAssignTo.js";
|
|
12
|
+
import { getUnaliased } from "../../../tir/types/utils/getUnaliased.js";
|
|
11
13
|
import { _compileExpr } from "./_compileExpr.js";
|
|
12
14
|
export function _compileUnaryPrefixExpr(ctx, expr, _typeHint) {
|
|
13
15
|
const bool_t = ctx.program.stdTypes.bool;
|
|
@@ -26,10 +28,20 @@ export function _compileUnaryPrefixExpr(ctx, expr, _typeHint) {
|
|
|
26
28
|
}
|
|
27
29
|
else if (expr instanceof UnaryPlus
|
|
28
30
|
|| expr instanceof UnaryMinus) {
|
|
29
|
-
|
|
31
|
+
// Probe without hint so we can spot `-Value` (TirValueT) before
|
|
32
|
+
// failing the int check.
|
|
33
|
+
const operand = _compileExpr(ctx, expr.operand, undefined);
|
|
30
34
|
if (!operand)
|
|
31
35
|
return undefined;
|
|
32
36
|
const operandType = operand.type;
|
|
37
|
+
const unaliasedTy = getUnaliased(operandType);
|
|
38
|
+
if (unaliasedTy instanceof TirValueT) {
|
|
39
|
+
// unary plus on Value is the identity; unary minus negates it.
|
|
40
|
+
if (expr instanceof UnaryPlus)
|
|
41
|
+
return new TirUnaryPlus(operand, operandType, expr.range);
|
|
42
|
+
if (expr instanceof UnaryMinus)
|
|
43
|
+
return new TirUnaryMinus(operand, operandType, expr.range);
|
|
44
|
+
}
|
|
33
45
|
if (!canAssignTo(operandType, int_t)) {
|
|
34
46
|
return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, expr.operand.range, operand.type.toString(), "int");
|
|
35
47
|
}
|