@harmoniclabs/pebble 0.1.8 → 0.1.10

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.
Files changed (42) hide show
  1. package/dist/ast/nodes/expr/IsExpr.d.ts +10 -1
  2. package/dist/ast/nodes/expr/IsExpr.js +9 -13
  3. package/dist/ast/nodes/expr/PebbleExpr.d.ts +2 -1
  4. package/dist/ast/nodes/expr/PebbleExpr.js +2 -1
  5. package/dist/ast/nodes/statements/declarations/ContractDecl.d.ts +3 -1
  6. package/dist/ast/nodes/statements/declarations/ContractDecl.js +3 -1
  7. package/dist/ast/nodes/statements/declarations/StateDecl.d.ts +12 -0
  8. package/dist/ast/nodes/statements/declarations/StateDecl.js +12 -0
  9. package/dist/compiler/AstCompiler/internal/_deriveContractBody/_deriveContractBody.js +154 -6
  10. package/dist/compiler/AstCompiler/internal/exprs/_compileCaseExpr.d.ts +1 -1
  11. package/dist/compiler/AstCompiler/internal/exprs/_compileCaseExpr.js +30 -4
  12. package/dist/compiler/AstCompiler/internal/exprs/_compileExpr.js +4 -1
  13. package/dist/compiler/AstCompiler/internal/exprs/_compileIsExpr.d.ts +5 -1
  14. package/dist/compiler/AstCompiler/internal/exprs/_compileIsExpr.js +20 -41
  15. package/dist/compiler/AstCompiler/internal/exprs/_compileTernaryExpr.js +7 -2
  16. package/dist/compiler/AstCompiler/internal/statements/_compileAssertStmt.js +4 -0
  17. package/dist/compiler/AstCompiler/internal/statements/_compileIfStmt.js +7 -2
  18. package/dist/compiler/AstCompiler/internal/statements/_compileMatchStmt.d.ts +1 -1
  19. package/dist/compiler/AstCompiler/internal/statements/_compileMatchStmt.js +17 -4
  20. package/dist/compiler/AstCompiler/scope/AstScope.d.ts +12 -0
  21. package/dist/compiler/AstCompiler/scope/AstScope.js +25 -2
  22. package/dist/compiler/AstCompiler/utils/extractIsNarrowings.d.ts +30 -0
  23. package/dist/compiler/AstCompiler/utils/extractIsNarrowings.js +110 -0
  24. package/dist/compiler/Compiler.js +7 -3
  25. package/dist/compiler/TirCompiler/expressify/expressifyVars.js +6 -0
  26. package/dist/compiler/tir/expressions/TirCallExpr.js +4 -0
  27. package/dist/compiler/tir/expressions/TirCaseExpr.js +38 -7
  28. package/dist/compiler/tir/expressions/TirExpr.d.ts +2 -1
  29. package/dist/compiler/tir/expressions/TirExpr.js +2 -1
  30. package/dist/compiler/tir/expressions/TirFuncExpr.js +4 -1
  31. package/dist/compiler/tir/expressions/TirIsExpr.d.ts +23 -0
  32. package/dist/compiler/tir/expressions/TirIsExpr.js +87 -0
  33. package/dist/compiler/tir/types/TirStructType.d.ts +33 -2
  34. package/dist/compiler/tir/types/TirStructType.js +69 -4
  35. package/dist/compiler/tir/types/utils/canAssignTo.js +14 -0
  36. package/dist/parser/Parser.d.ts +2 -0
  37. package/dist/parser/Parser.js +87 -22
  38. package/dist/tokenizer/Token.d.ts +79 -78
  39. package/dist/tokenizer/Token.js +79 -78
  40. package/dist/tokenizer/utils/tokenFromKeyword.js +7 -2
  41. package/dist/tokenizer/utils/tokenIsAlsoIdentifier.js +1 -0
  42. package/package.json +2 -1
@@ -1 +1,10 @@
1
- export {};
1
+ import { SourceRange } from "../../Source/SourceRange.js";
2
+ import { Identifier } from "../common/Identifier.js";
3
+ import { HasSourceRange } from "../HasSourceRange.js";
4
+ import { PebbleExpr } from "./PebbleExpr.js";
5
+ export declare class IsExpr implements HasSourceRange {
6
+ instanceExpr: PebbleExpr;
7
+ readonly ofConstr: Identifier;
8
+ readonly range: SourceRange;
9
+ constructor(instanceExpr: PebbleExpr, ofConstr: Identifier, range: SourceRange);
10
+ }
@@ -1,14 +1,10 @@
1
- export {};
2
- // TODO:
3
- // should optionally check for destructuring
4
- /*
5
- export class IsExpr
6
- implements HasSourceRange
7
- {
8
- constructor(
9
- readonly instanceExpr: PebbleExpr,
10
- readonly ofType: Identifier,
11
- readonly range: SourceRange
12
- ) {}
1
+ export class IsExpr {
2
+ instanceExpr;
3
+ ofConstr;
4
+ range;
5
+ constructor(instanceExpr, ofConstr, range) {
6
+ this.instanceExpr = instanceExpr;
7
+ this.ofConstr = ofConstr;
8
+ this.range = range;
9
+ }
13
10
  }
14
- //*/
@@ -2,6 +2,7 @@ import { CaseExpr } from "./CaseExpr.js";
2
2
  import { ElemAccessExpr } from "./ElemAccessExpr.js";
3
3
  import { CallExpr } from "./functions/CallExpr.js";
4
4
  import { FuncExpr } from "./functions/FuncExpr.js";
5
+ import { IsExpr } from "./IsExpr.js";
5
6
  import { LitteralExpr } from "./litteral/LitteralExpr.js";
6
7
  import { NonNullExpr } from "./unary/NonNullExpr.js";
7
8
  import { ParentesizedExpr } from "./ParentesizedExpr.js";
@@ -15,5 +16,5 @@ import { BinaryExpr } from "./binary/BinaryExpr.js";
15
16
  * an expression is anything that can be evaluated to a value
16
17
  * it must therefore have a type
17
18
  */
18
- export type PebbleExpr = UnaryPrefixExpr | NonNullExpr | LitteralExpr | ParentesizedExpr | FuncExpr | CallExpr | CaseExpr | TypeConversionExpr | NonNullExpr | ElemAccessExpr | TernaryExpr | Identifier | BinaryExpr | PropAccessExpr;
19
+ export type PebbleExpr = UnaryPrefixExpr | NonNullExpr | LitteralExpr | ParentesizedExpr | FuncExpr | CallExpr | CaseExpr | TypeConversionExpr | NonNullExpr | IsExpr | ElemAccessExpr | TernaryExpr | Identifier | BinaryExpr | PropAccessExpr;
19
20
  export declare function isPebbleExpr(thing: any): thing is PebbleExpr;
@@ -2,6 +2,7 @@ import { CaseExpr } from "./CaseExpr.js";
2
2
  import { ElemAccessExpr } from "./ElemAccessExpr.js";
3
3
  import { CallExpr } from "./functions/CallExpr.js";
4
4
  import { FuncExpr } from "./functions/FuncExpr.js";
5
+ import { IsExpr } from "./IsExpr.js";
5
6
  import { isLitteralExpr } from "./litteral/LitteralExpr.js";
6
7
  import { NonNullExpr } from "./unary/NonNullExpr.js";
7
8
  import { ParentesizedExpr } from "./ParentesizedExpr.js";
@@ -21,7 +22,7 @@ export function isPebbleExpr(thing) {
21
22
  || thing instanceof CaseExpr
22
23
  || thing instanceof TypeConversionExpr
23
24
  || thing instanceof NonNullExpr
24
- // || thing instanceof IsExpr
25
+ || thing instanceof IsExpr
25
26
  || thing instanceof ElemAccessExpr
26
27
  || thing instanceof TernaryExpr
27
28
  // || thing instanceof CommaExpr
@@ -3,6 +3,7 @@ import { Identifier } from "../../common/Identifier.js";
3
3
  import { HasSourceRange } from "../../HasSourceRange.js";
4
4
  import { FuncDecl } from "./FuncDecl.js";
5
5
  import { SimpleVarDecl } from "./VarDecl/SimpleVarDecl.js";
6
+ import { StateDecl } from "./StateDecl.js";
6
7
  export declare class ContractDecl implements HasSourceRange {
7
8
  readonly name: Identifier;
8
9
  readonly params: SimpleVarDecl[];
@@ -12,6 +13,7 @@ export declare class ContractDecl implements HasSourceRange {
12
13
  readonly withdrawMethods: FuncDecl[];
13
14
  readonly proposeMethods: FuncDecl[];
14
15
  readonly voteMethods: FuncDecl[];
16
+ readonly stateDecls: StateDecl[];
15
17
  readonly range: SourceRange;
16
- constructor(name: Identifier, params: SimpleVarDecl[], spendMethods: FuncDecl[], mintMethods: FuncDecl[], certifyMethods: FuncDecl[], withdrawMethods: FuncDecl[], proposeMethods: FuncDecl[], voteMethods: FuncDecl[], range: SourceRange);
18
+ constructor(name: Identifier, params: SimpleVarDecl[], spendMethods: FuncDecl[], mintMethods: FuncDecl[], certifyMethods: FuncDecl[], withdrawMethods: FuncDecl[], proposeMethods: FuncDecl[], voteMethods: FuncDecl[], stateDecls: StateDecl[], range: SourceRange);
17
19
  }
@@ -7,8 +7,9 @@ export class ContractDecl {
7
7
  withdrawMethods;
8
8
  proposeMethods;
9
9
  voteMethods;
10
+ stateDecls;
10
11
  range;
11
- constructor(name, params, spendMethods, mintMethods, certifyMethods, withdrawMethods, proposeMethods, voteMethods, range) {
12
+ constructor(name, params, spendMethods, mintMethods, certifyMethods, withdrawMethods, proposeMethods, voteMethods, stateDecls, range) {
12
13
  this.name = name;
13
14
  this.params = params;
14
15
  this.spendMethods = spendMethods;
@@ -17,6 +18,7 @@ export class ContractDecl {
17
18
  this.withdrawMethods = withdrawMethods;
18
19
  this.proposeMethods = proposeMethods;
19
20
  this.voteMethods = voteMethods;
21
+ this.stateDecls = stateDecls;
20
22
  this.range = range;
21
23
  }
22
24
  }
@@ -0,0 +1,12 @@
1
+ import { SourceRange } from "../../../Source/SourceRange.js";
2
+ import { Identifier } from "../../common/Identifier.js";
3
+ import { HasSourceRange } from "../../HasSourceRange.js";
4
+ import { FuncDecl } from "./FuncDecl.js";
5
+ import { SimpleVarDecl } from "./VarDecl/SimpleVarDecl.js";
6
+ export declare class StateDecl implements HasSourceRange {
7
+ readonly name: Identifier;
8
+ readonly fields: SimpleVarDecl[];
9
+ readonly spendMethods: FuncDecl[];
10
+ readonly range: SourceRange;
11
+ constructor(name: Identifier, fields: SimpleVarDecl[], spendMethods: FuncDecl[], range: SourceRange);
12
+ }
@@ -0,0 +1,12 @@
1
+ export class StateDecl {
2
+ name;
3
+ fields;
4
+ spendMethods;
5
+ range;
6
+ constructor(name, fields, spendMethods, range) {
7
+ this.name = name;
8
+ this.fields = fields;
9
+ this.spendMethods = spendMethods;
10
+ this.range = range;
11
+ }
12
+ }
@@ -18,6 +18,7 @@ import { LitThisExpr } from "../../../../ast/nodes/expr/litteral/LitThisExpr.js"
18
18
  import { LitTrueExpr } from "../../../../ast/nodes/expr/litteral/LitTrueExpr.js";
19
19
  import { LitUndefExpr } from "../../../../ast/nodes/expr/litteral/LitUndefExpr.js";
20
20
  import { LitVoidExpr } from "../../../../ast/nodes/expr/litteral/LitVoidExpr.js";
21
+ import { IsExpr } from "../../../../ast/nodes/expr/IsExpr.js";
21
22
  import { ParentesizedExpr } from "../../../../ast/nodes/expr/ParentesizedExpr.js";
22
23
  import { isPropAccessExpr } from "../../../../ast/nodes/expr/PropAccessExpr.js";
23
24
  import { TernaryExpr } from "../../../../ast/nodes/expr/TernaryExpr.js";
@@ -28,6 +29,8 @@ import { isExplicitAssignmentStmt, isImplicitAssignmentStmt } from "../../../../
28
29
  import { BlockStmt } from "../../../../ast/nodes/statements/BlockStmt.js";
29
30
  import { BreakStmt } from "../../../../ast/nodes/statements/BreakStmt.js";
30
31
  import { ContinueStmt } from "../../../../ast/nodes/statements/ContinueStmt.js";
32
+ import { ArrowKind } from "../../../../ast/nodes/expr/functions/ArrowKind.js";
33
+ import { AstFuncType, AstVoidType } from "../../../../ast/nodes/types/AstNativeTypeExpr.js";
31
34
  import { StructConstrDecl, StructDecl, StructDeclAstFlags } from "../../../../ast/nodes/statements/declarations/StructDecl.js";
32
35
  import { NamedDeconstructVarDecl } from "../../../../ast/nodes/statements/declarations/VarDecl/NamedDeconstructVarDecl.js";
33
36
  import { SimpleVarDecl } from "../../../../ast/nodes/statements/declarations/VarDecl/SimpleVarDecl.js";
@@ -64,7 +67,8 @@ export function _deriveContractBody(compiler, contractDecl, paramsInternalNamesM
64
67
  && contractDecl.certifyMethods.length === 0
65
68
  && contractDecl.withdrawMethods.length === 0
66
69
  && contractDecl.proposeMethods.length === 0
67
- && contractDecl.voteMethods.length === 0)
70
+ && contractDecl.voteMethods.length === 0
71
+ && contractDecl.stateDecls.length === 0)
68
72
  return new BlockStmt([
69
73
  new FailStmt(undefined, contractRange)
70
74
  ], contractRange);
@@ -143,7 +147,8 @@ export function _deriveContractBody(compiler, contractDecl, paramsInternalNamesM
143
147
  );
144
148
  */
145
149
  const purposeMatchCases = [];
146
- if (contractDecl.spendMethods.length > 0) {
150
+ if (contractDecl.spendMethods.length > 0
151
+ || contractDecl.stateDecls.length > 0) {
147
152
  const spendingRefUniqueName = getUniqueInternalName("spendingRef");
148
153
  const optionalDatumUniqueName = getUniqueInternalName("optionalDatum");
149
154
  const fields = new Map([
@@ -156,15 +161,14 @@ export function _deriveContractBody(compiler, contractDecl, paramsInternalNamesM
156
161
  SimpleVarDecl.onlyNameConst(optionalDatumUniqueName, mockRange)
157
162
  ],
158
163
  ]);
159
- const bodyStmts = _getMatchedPurposeBlockStatements(compiler, contractDecl.spendMethods, paramsInternalNamesMap,
160
- // contextVarsMapping
161
- Object.freeze({
164
+ const baseContextVars = Object.freeze({
162
165
  tx: txUniqueName,
163
166
  purposeData: purposeUniqueName,
164
167
  redeemerData: redeemerUniqueName,
165
168
  spendingRef: spendingRefUniqueName,
166
169
  optionalDatum: optionalDatumUniqueName,
167
- }), "SpendRedeemer", contractRange);
170
+ });
171
+ const bodyStmts = _buildSpendCaseBlock(compiler, contractDecl, paramsInternalNamesMap, baseContextVars, contractRange);
168
172
  if (!Array.isArray(bodyStmts))
169
173
  return undefined;
170
174
  purposeMatchCases.push(new MatchStmtCase(new NamedDeconstructVarDecl(new Identifier("Spend", mockRange), fields, undefined, // rest
@@ -675,6 +679,13 @@ function _exprReplaceParamsAndAssertNoLitContext(compiler, expr, paramsInternalN
675
679
  expr.expr = newInner;
676
680
  return expr;
677
681
  }
682
+ if (expr instanceof IsExpr) {
683
+ const newInner = _exprReplaceParamsAndAssertNoLitContext(compiler, expr.instanceExpr, paramsInternalNamesMap, renamedVariables);
684
+ if (!newInner)
685
+ return undefined;
686
+ expr.instanceExpr = newInner;
687
+ return expr;
688
+ }
678
689
  if (isPropAccessExpr(expr)) {
679
690
  const newObject = _exprReplaceParamsAndAssertNoLitContext(compiler, expr.object, paramsInternalNamesMap, renamedVariables);
680
691
  if (!newObject)
@@ -759,6 +770,143 @@ function _exprReplaceParamsAndAssertNoLitContext(compiler, expr, paramsInternalN
759
770
  console.error(expr);
760
771
  throw new Error("unreachable::_exprReplaceParamsAndAssertNoLitContext");
761
772
  }
773
+ function _deriveContractDatumTypeDef(contractName, stateDecls, contractRange) {
774
+ let defFlags = StructDeclAstFlags.onlyDataEncoding;
775
+ if (stateDecls.length <= 1)
776
+ defFlags |= StructDeclAstFlags.untaggedSingleConstructor;
777
+ return new StructDecl(new Identifier(contractName, contractRange), [], // typeParams
778
+ stateDecls.map(s => new StructConstrDecl(new Identifier(s.name.text, s.name.range), s.fields, s.range)), defFlags, contractRange);
779
+ }
780
+ function _buildSpendCaseBlock(compiler, contractDecl, paramsInternalNamesMap, baseContextVars, contractRange) {
781
+ const mockRange = SourceRange.mock;
782
+ const hasPlainSpend = contractDecl.spendMethods.length > 0;
783
+ const hasState = contractDecl.stateDecls.length > 0;
784
+ // legacy / pure-plain-spend behavior: identical UPLC to before
785
+ if (!hasState) {
786
+ return _getMatchedPurposeBlockStatements(compiler, contractDecl.spendMethods, paramsInternalNamesMap, baseContextVars, "SpendRedeemer", contractRange);
787
+ }
788
+ // synthetic union datum type
789
+ const datumTypeDef = _deriveContractDatumTypeDef(contractDecl.name.text, contractDecl.stateDecls, contractRange);
790
+ compiler.registerInternalTypeDecl(datumTypeDef);
791
+ const datumTypeName = datumTypeDef.name.text;
792
+ // build fallback (used when datum is absent or doesn't match any state)
793
+ // we bind the plain-spend redeemer match as a 0-arg function value, then
794
+ // call it via `return fallback();` at every fallback site. the 0-arg-func
795
+ // → delay lowering keeps the body emitted only once.
796
+ let fallbackBindStmt;
797
+ let fallbackFuncName;
798
+ const makeFallbackBody = () => {
799
+ if (hasPlainSpend) {
800
+ return [
801
+ new ReturnStmt(new CallExpr(new Identifier(fallbackFuncName, mockRange), undefined, // genericTypeArgs
802
+ [], mockRange), mockRange)
803
+ ];
804
+ }
805
+ return [new FailStmt(undefined, contractRange)];
806
+ };
807
+ if (hasPlainSpend) {
808
+ fallbackFuncName = getUniqueInternalName("unknownDatum");
809
+ // pure-plain-spend behavior: identical UPLC to before
810
+ const plainBody = _getMatchedPurposeBlockStatements(compiler, contractDecl.spendMethods, paramsInternalNamesMap, baseContextVars, "SpendRedeemer", contractRange);
811
+ if (!Array.isArray(plainBody))
812
+ return undefined;
813
+ fallbackBindStmt = new VarStmt([
814
+ new SimpleVarDecl(new Identifier(fallbackFuncName, mockRange), new AstFuncType([], // params
815
+ new AstVoidType(mockRange), mockRange), new FuncExpr(new Identifier(fallbackFuncName, mockRange), CommonFlags.None, [], // typeParams
816
+ new AstFuncType([], // params
817
+ new AstVoidType(mockRange), mockRange), new BlockStmt(plainBody, contractRange), ArrowKind.None, mockRange), // initExpr
818
+ CommonFlags.Const, mockRange)
819
+ ], mockRange);
820
+ }
821
+ const datumUniqueName = getUniqueInternalName("datum");
822
+ const datumAsUnionName = getUniqueInternalName("datumUnion");
823
+ // build per-state cases
824
+ const datumMatchCases = [];
825
+ for (const stateDecl of contractDecl.stateDecls) {
826
+ let stateBody;
827
+ // deconstruct each field of the state's constructor into a fresh
828
+ // internal placeholder so the existing match-compiler accepts the
829
+ // pattern (which requires every field to be specified)
830
+ const fieldExtracts = stateDecl.fields.map(f => ({
831
+ originalName: f.name.text,
832
+ extractName: getUniqueInternalName(`${stateDecl.name.text}_${f.name.text}`),
833
+ range: f.name.range,
834
+ }));
835
+ if (stateDecl.spendMethods.length === 0) {
836
+ // readonly-state: never spendable
837
+ stateBody = [new FailStmt(undefined, stateDecl.range)];
838
+ }
839
+ else {
840
+ // synthesize a per-state single-constructor struct so that
841
+ // `state.fieldName` is type-checked against a struct that has
842
+ // only this state's fields (the multi-ctor union would reject
843
+ // the access since fields differ across constructors).
844
+ const perStateStructDecl = new StructDecl(new Identifier(getUniqueInternalName(`${contractDecl.name.text}_${stateDecl.name.text}`), mockRange), [], // typeParams
845
+ [
846
+ new StructConstrDecl(new Identifier(stateDecl.name.text, mockRange), stateDecl.fields, stateDecl.range)
847
+ ], StructDeclAstFlags.onlyDataEncoding | StructDeclAstFlags.untaggedSingleConstructor, stateDecl.range);
848
+ compiler.registerInternalTypeDecl(perStateStructDecl);
849
+ const perStateStructName = perStateStructDecl.name.text;
850
+ // synthesize: const state = StateStruct{ field1: _f1, ... };
851
+ // - state has type StateStruct (single-ctor) so state.x type-checks
852
+ // - LitNamedObjExpr at expressify short-circuits property access
853
+ // to the bound field value (the extracted variable)
854
+ const stateUniqueName = getUniqueInternalName(`state_${stateDecl.name.text}`);
855
+ const stateBindStmt = new VarStmt([
856
+ new SimpleVarDecl(new Identifier(stateUniqueName, mockRange), new AstNamedTypeExpr(new Identifier(perStateStructName, mockRange), [], // typeArgs
857
+ mockRange), new LitNamedObjExpr(new Identifier(stateDecl.name.text, mockRange), fieldExtracts.map(fe => new Identifier(fe.originalName, mockRange)), fieldExtracts.map(fe => new Identifier(fe.extractName, mockRange)), mockRange, new Identifier(perStateStructName, mockRange)), // initExpr
858
+ CommonFlags.Const, mockRange)
859
+ ], mockRange);
860
+ const perStateContextVars = Object.freeze({
861
+ ...baseContextVars,
862
+ state: stateUniqueName,
863
+ });
864
+ const redeemerStmts = _getMatchedPurposeBlockStatements(compiler, stateDecl.spendMethods, paramsInternalNamesMap, perStateContextVars, `${stateDecl.name.text}Redeemer`, stateDecl.range);
865
+ if (!Array.isArray(redeemerStmts))
866
+ return undefined;
867
+ stateBody = [stateBindStmt, ...redeemerStmts];
868
+ }
869
+ const fieldsMap = new Map(fieldExtracts.map(fe => [
870
+ new Identifier(fe.originalName, mockRange),
871
+ SimpleVarDecl.onlyNameConst(fe.extractName, mockRange)
872
+ ]));
873
+ datumMatchCases.push(new MatchStmtCase(new NamedDeconstructVarDecl(new Identifier(stateDecl.name.text, mockRange), fieldsMap, undefined, // rest
874
+ undefined, // type
875
+ undefined, // initExpr
876
+ CommonFlags.Const, stateDecl.range), new BlockStmt(stateBody, stateDecl.range), stateDecl.range));
877
+ }
878
+ // bind: const datumUnion: ContractDatum = datum as ContractDatum;
879
+ // we match on this bound variable so the existing match-narrowing
880
+ // mechanism narrows it to the matched constructor inside each arm.
881
+ const datumAsUnionBindStmt = new VarStmt([
882
+ new SimpleVarDecl(new Identifier(datumAsUnionName, mockRange), new AstNamedTypeExpr(new Identifier(datumTypeName, mockRange), [], // typeArgs
883
+ mockRange), new TypeConversionExpr(new Identifier(datumUniqueName, mockRange), new AstNamedTypeExpr(new Identifier(datumTypeName, mockRange), [], // typeArgs
884
+ mockRange), mockRange), CommonFlags.Const, mockRange)
885
+ ], mockRange);
886
+ const datumMatchStmt = new MatchStmt(new Identifier(datumAsUnionName, mockRange), datumMatchCases, new MatchStmtElseCase(new BlockStmt(makeFallbackBody(), contractRange), contractRange), contractRange);
887
+ // outer match: optionalDatum -> Some{ value: datum } / Nothing
888
+ const optionalMatch = new MatchStmt(new Identifier(baseContextVars.optionalDatum, mockRange), [
889
+ new MatchStmtCase(new NamedDeconstructVarDecl(new Identifier("Some", mockRange), new Map([
890
+ [
891
+ new Identifier("value", mockRange),
892
+ SimpleVarDecl.onlyNameConst(datumUniqueName, mockRange)
893
+ ]
894
+ ]), undefined, // rest
895
+ undefined, // type
896
+ undefined, // initExpr
897
+ CommonFlags.Const, contractRange), new BlockStmt([datumAsUnionBindStmt, datumMatchStmt], contractRange), contractRange),
898
+ new MatchStmtCase(new NamedDeconstructVarDecl(new Identifier("None", mockRange), new Map(), undefined, // rest
899
+ undefined, // type
900
+ undefined, // initExpr
901
+ CommonFlags.Const, contractRange), new BlockStmt(makeFallbackBody(), contractRange), contractRange),
902
+ ], undefined, // elseCase
903
+ contractRange);
904
+ const result = [];
905
+ if (fallbackBindStmt)
906
+ result.push(fallbackBindStmt);
907
+ result.push(optionalMatch);
908
+ return result;
909
+ }
762
910
  function _deriveRedeemerTypeDef(redeemerName, methods, contractRange) {
763
911
  let defFlags = StructDeclAstFlags.onlyDataEncoding;
764
912
  if (methods.length <= 1)
@@ -3,4 +3,4 @@ import { TirCaseExpr, TirCaseMatcher } from "../../../tir/expressions/TirCaseExp
3
3
  import { TirType } from "../../../tir/types/TirType.js";
4
4
  import { AstCompilationCtx } from "../../AstCompilationCtx.js";
5
5
  export declare function _compileCaseExpr(ctx: AstCompilationCtx, expr: CaseExpr, typeHint: TirType | undefined): TirCaseExpr | undefined;
6
- export declare function _compileCaseExprMatcher(ctx: AstCompilationCtx, matcher: CaseExprMatcher, patternType: TirType, returnTypeHint: TirType | undefined): TirCaseMatcher | undefined;
6
+ export declare function _compileCaseExprMatcher(ctx: AstCompilationCtx, matcher: CaseExprMatcher, patternType: TirType, returnTypeHint: TirType | undefined, matchedVarName?: string): TirCaseMatcher | undefined;
@@ -1,15 +1,21 @@
1
+ import { Identifier } from "../../../../ast/nodes/common/Identifier.js";
2
+ import { ParentesizedExpr } from "../../../../ast/nodes/expr/ParentesizedExpr.js";
1
3
  import { DiagnosticCode } from "../../../../diagnostics/diagnosticMessages.generated.js";
2
4
  import { TirCaseExpr, TirCaseMatcher, TirWildcardCaseMatcher } from "../../../tir/expressions/TirCaseExpr.js";
3
5
  import { TirNamedDeconstructVarDecl } from "../../../tir/statements/TirVarDecl/TirNamedDeconstructVarDecl.js";
4
6
  import { TirSimpleVarDecl } from "../../../tir/statements/TirVarDecl/TirSimpleVarDecl.js";
5
- import { canAssignTo } from "../../../tir/types/utils/canAssignTo.js";
7
+ import { TirDataStructType, TirSoPStructType } from "../../../tir/types/TirStructType.js";
8
+ import { canAssignTo, getStructType } from "../../../tir/types/utils/canAssignTo.js";
6
9
  import { _compileVarDecl } from "../statements/_compileVarStmt.js";
7
10
  import { _compileExpr } from "./_compileExpr.js";
8
11
  export function _compileCaseExpr(ctx, expr, typeHint) {
9
12
  const matchExpr = _compileExpr(ctx, expr.matchExpr, typeHint);
10
13
  if (!matchExpr)
11
14
  return undefined;
12
- const cases = expr.cases.map(branch => _compileCaseExprMatcher(ctx, branch, matchExpr.type, typeHint)); // we early return in case of undefined so this is safe
15
+ // if the matched expression is a plain variable, we can narrow its
16
+ // type inside each arm body to the matched constructor.
17
+ const matchedVarName = unwrapToIdentifierName(expr.matchExpr);
18
+ const cases = expr.cases.map(branch => _compileCaseExprMatcher(ctx, branch, matchExpr.type, typeHint, matchedVarName)); // we early return in case of undefined so this is safe
13
19
  if (cases.some(c => !c))
14
20
  return undefined;
15
21
  const returnType = cases[0]?.body.type ?? typeHint;
@@ -22,7 +28,7 @@ export function _compileCaseExpr(ctx, expr, typeHint) {
22
28
  return undefined;
23
29
  return new TirCaseExpr(matchExpr, cases, wildcardCase, returnType, expr.range);
24
30
  }
25
- export function _compileCaseExprMatcher(ctx, matcher, patternType, returnTypeHint) {
31
+ export function _compileCaseExprMatcher(ctx, matcher, patternType, returnTypeHint, matchedVarName) {
26
32
  const pattern = _compileVarDecl(ctx, matcher.pattern, patternType);
27
33
  if (!pattern)
28
34
  return undefined;
@@ -30,7 +36,22 @@ export function _compileCaseExprMatcher(ctx, matcher, patternType, returnTypeHin
30
36
  return ctx.error(DiagnosticCode._case_expression_must_decontructed_the_inspected_value, matcher.pattern.range);
31
37
  if (!canAssignTo(pattern.type, patternType))
32
38
  return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, matcher.pattern.range, pattern.type.toString(), patternType.toString());
33
- const body = _compileExpr(ctx, matcher.body, returnTypeHint);
39
+ let bodyCtx = ctx;
40
+ if (matchedVarName && pattern instanceof TirNamedDeconstructVarDecl) {
41
+ const parentStruct = getStructType(patternType);
42
+ if (parentStruct) {
43
+ const localIdx = parentStruct.constructors.findIndex(c => c.name === pattern.constrName);
44
+ if (localIdx >= 0) {
45
+ const parentIdx = parentStruct.parentCtorIdx(localIdx);
46
+ bodyCtx = ctx.newBranchChildScope();
47
+ if (parentStruct instanceof TirDataStructType
48
+ || parentStruct instanceof TirSoPStructType) {
49
+ bodyCtx.scope.narrowVariable(matchedVarName, parentStruct.narrowTo([parentIdx]));
50
+ }
51
+ }
52
+ }
53
+ }
54
+ const body = _compileExpr(bodyCtx, matcher.body, returnTypeHint);
34
55
  if (!body)
35
56
  return undefined;
36
57
  if (returnTypeHint && !canAssignTo(body.type, returnTypeHint))
@@ -45,3 +66,8 @@ function _compileCaseWildcardMatcher(ctx, wildcardCase, returnTypeHint) {
45
66
  return undefined;
46
67
  return new TirWildcardCaseMatcher(bodyExpr, wildcardCase.range);
47
68
  }
69
+ function unwrapToIdentifierName(expr) {
70
+ while (expr instanceof ParentesizedExpr)
71
+ expr = expr.expr;
72
+ return expr instanceof Identifier ? expr.text : undefined;
73
+ }
@@ -4,6 +4,7 @@ import { CaseExpr } from "../../../../ast/nodes/expr/CaseExpr.js";
4
4
  import { ElemAccessExpr } from "../../../../ast/nodes/expr/ElemAccessExpr.js";
5
5
  import { CallExpr } from "../../../../ast/nodes/expr/functions/CallExpr.js";
6
6
  import { FuncExpr } from "../../../../ast/nodes/expr/functions/FuncExpr.js";
7
+ import { IsExpr } from "../../../../ast/nodes/expr/IsExpr.js";
7
8
  import { isLitteralExpr } from "../../../../ast/nodes/expr/litteral/LitteralExpr.js";
8
9
  import { ParentesizedExpr } from "../../../../ast/nodes/expr/ParentesizedExpr.js";
9
10
  import { isPropAccessExpr } from "../../../../ast/nodes/expr/PropAccessExpr.js";
@@ -17,6 +18,7 @@ import { _compileCallExpr } from "./_compileCallExpr.js";
17
18
  import { _compileCaseExpr } from "./_compileCaseExpr.js";
18
19
  import { _compileElemAccessExpr } from "./_compileElemAccessExpr.js";
19
20
  import { _compileFuncExpr } from "./_compileFuncExpr.js";
21
+ import { _compileIsExpr } from "./_compileIsExpr.js";
20
22
  import { _compileLitteralExpr } from "./_compileLitteralExpr.js";
21
23
  import { _compileNonNullExpr } from "./_compileNonNullExpr.js";
22
24
  import { _compilePropAccessExpr } from "./_compilePropAccessExpr.js";
@@ -63,7 +65,8 @@ typeHint) {
63
65
  return _compileTypeConversionExpr(ctx, expr, typeHint);
64
66
  if (expr instanceof NonNullExpr)
65
67
  return _compileNonNullExpr(ctx, expr, typeHint);
66
- // if( expr instanceof IsExpr ) return _compileIsExpr( ctx, expr, typeHint );
68
+ if (expr instanceof IsExpr)
69
+ return _compileIsExpr(ctx, expr, typeHint);
67
70
  if (expr instanceof ElemAccessExpr)
68
71
  return _compileElemAccessExpr(ctx, expr, typeHint);
69
72
  if (expr instanceof TernaryExpr)
@@ -1 +1,5 @@
1
- export {};
1
+ import { IsExpr } from "../../../../ast/nodes/expr/IsExpr.js";
2
+ import { TirIsExpr } from "../../../tir/expressions/TirIsExpr.js";
3
+ import { TirType } from "../../../tir/types/TirType.js";
4
+ import { AstCompilationCtx } from "../../AstCompilationCtx.js";
5
+ export declare function _compileIsExpr(ctx: AstCompilationCtx, expr: IsExpr, _typeHint: TirType | undefined): TirIsExpr | undefined;
@@ -1,43 +1,22 @@
1
- export {};
2
- /*
3
- export function _compileIsExpr(
4
- ctx: AstCompilationCtx,
5
- expr: IsExpr,
6
- _typeHint: TirType | undefined
7
- ): TirIsExpr | undefined
8
- {
1
+ import { DiagnosticCode } from "../../../../diagnostics/diagnosticMessages.generated.js";
2
+ import { TirIsExpr } from "../../../tir/expressions/TirIsExpr.js";
3
+ import { getStructType } from "../../../tir/types/utils/canAssignTo.js";
4
+ import { _compileExpr } from "./_compileExpr.js";
5
+ export function _compileIsExpr(ctx, expr, _typeHint) {
9
6
  const bool_t = ctx.program.stdTypes.bool;
10
-
11
- const target = _compileExpr( ctx, expr.instanceExpr, undefined );
12
- if( !target ) return undefined;
13
-
14
- const structType = getStructType( target.type );
15
- if( !structType ) return ctx.error(
16
- DiagnosticCode.Cannot_use_is_operator_on_a_value_that_is_not_a_struct_type,
17
- expr.instanceExpr.range
18
- );
19
-
20
- const targetCtorId = expr.ofType;
21
- const targetCtorName = targetCtorId.text;
22
-
23
- const structCtors = structType.constructors;
24
- const targetCtor = structCtors.find( ctor => ctor.name === targetCtorName );
25
- if( !targetCtor ) return ctx.error(
26
- DiagnosticCode.Constructor_0_is_not_part_of_the_definition_of_1,
27
- expr.ofType.range, targetCtorName, structType.toString()
28
- );
29
-
30
- if( structCtors.length === 1 )
31
- ctx.warning(
32
- DiagnosticCode.This_check_is_redundant_Struct_0_has_only_one_possible_constructor,
33
- expr.range, structType.toString()
34
- );
35
-
36
- return new TirIsExpr(
37
- target,
38
- expr.ofType,
39
- bool_t,
40
- expr.range
41
- );
7
+ const target = _compileExpr(ctx, expr.instanceExpr, undefined);
8
+ if (!target)
9
+ return undefined;
10
+ const structType = getStructType(target.type);
11
+ if (!structType)
12
+ return ctx.error(DiagnosticCode.Cannot_use_is_operator_on_a_value_that_is_not_a_struct_type, expr.instanceExpr.range);
13
+ const targetCtorName = expr.ofConstr.text;
14
+ const localIdx = structType.constructors.findIndex(ctor => ctor.name === targetCtorName);
15
+ if (localIdx < 0)
16
+ return ctx.error(DiagnosticCode.Constructor_0_is_not_part_of_the_definition_of_1, expr.ofConstr.range, targetCtorName, structType.toString());
17
+ if (structType.constructors.length === 1) {
18
+ ctx.warning(DiagnosticCode.This_check_is_redundant_Struct_0_has_only_one_possible_constructor, expr.range, structType.toString());
19
+ }
20
+ const parentCtorIdx = structType.parentCtorIdx(localIdx);
21
+ return new TirIsExpr(target, targetCtorName, parentCtorIdx, expr.range, bool_t);
42
22
  }
43
- //*/
@@ -1,6 +1,7 @@
1
1
  import { DiagnosticCode } from "../../../../diagnostics/diagnosticMessages.generated.js";
2
2
  import { TirTernaryExpr } from "../../../tir/expressions/TirTernaryExpr.js";
3
3
  import { canAssignTo } from "../../../tir/types/utils/canAssignTo.js";
4
+ import { applyNarrowingsToScope, extractIsNarrowings } from "../../utils/extractIsNarrowings.js";
4
5
  import { _compileExpr } from "./_compileExpr.js";
5
6
  export function _compileTernaryExpr(ctx, expr, typeHint) {
6
7
  const bool_t = ctx.program.stdTypes.bool;
@@ -9,11 +10,15 @@ export function _compileTernaryExpr(ctx, expr, typeHint) {
9
10
  return undefined;
10
11
  if (!canAssignTo(cond.type, bool_t))
11
12
  return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, expr.condition.range, cond.type.toString(), bool_t.toString());
12
- const thenExpr = _compileExpr(ctx, expr.ifTrue, typeHint);
13
+ const thenCtx = ctx.newBranchChildScope();
14
+ applyNarrowingsToScope(thenCtx.scope, extractIsNarrowings(cond, true));
15
+ const thenExpr = _compileExpr(thenCtx, expr.ifTrue, typeHint);
13
16
  if (!thenExpr)
14
17
  return undefined;
15
18
  const returnType = thenExpr.type;
16
- const elseExpr = _compileExpr(ctx, expr.ifFalse, returnType);
19
+ const elseCtx = ctx.newBranchChildScope();
20
+ applyNarrowingsToScope(elseCtx.scope, extractIsNarrowings(cond, false));
21
+ const elseExpr = _compileExpr(elseCtx, expr.ifFalse, returnType);
17
22
  if (!elseExpr)
18
23
  return undefined;
19
24
  if (!canAssignTo(elseExpr.type, returnType))
@@ -2,6 +2,7 @@ import { DiagnosticCode } from "../../../../diagnostics/diagnosticMessages.gener
2
2
  import { TirTypeConversionExpr } from "../../../tir/expressions/TirTypeConversionExpr.js";
3
3
  import { TirAssertStmt } from "../../../tir/statements/TirAssertStmt.js";
4
4
  import { canAssignTo, canAssignToOptional } from "../../../tir/types/utils/canAssignTo.js";
5
+ import { applyNarrowingsToScope, extractIsNarrowings } from "../../utils/extractIsNarrowings.js";
5
6
  import { _compileExpr } from "../exprs/_compileExpr.js";
6
7
  export function _compileAssertStmt(ctx, stmt) {
7
8
  const bool_t = ctx.program.stdTypes.bool;
@@ -26,5 +27,8 @@ export function _compileAssertStmt(ctx, stmt) {
26
27
  if (!canAssignTo(failMsgExpr.type, string_t))
27
28
  return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, stmt.elseExpr.range, failMsgExpr.type.toString(), string_t.toString());
28
29
  }
30
+ // After a successful `assert`, propagate any `is`-derived narrowings
31
+ // into the current scope so subsequent statements see narrowed types.
32
+ applyNarrowingsToScope(ctx.scope, extractIsNarrowings(tirCond, true));
29
33
  return [new TirAssertStmt(tirCond, failMsgExpr, stmt.range)];
30
34
  }
@@ -3,6 +3,7 @@ import { TirIfStmt } from "../../../tir/statements/TirIfStmt.js";
3
3
  import { canAssignTo, canAssignToOptional } from "../../../tir/types/utils/canAssignTo.js";
4
4
  import { bool_t } from "../../../tir/program/stdScope/stdScope.js";
5
5
  import { wrapManyStatements } from "../../utils/wrapManyStatementsOrReturnSame.js";
6
+ import { applyNarrowingsToScope, extractIsNarrowings } from "../../utils/extractIsNarrowings.js";
6
7
  import { _compileExpr } from "../exprs/_compileExpr.js";
7
8
  import { _compileStatement } from "./_compileStatement.js";
8
9
  export function _compileIfStmt(ctx, stmt) {
@@ -12,12 +13,16 @@ export function _compileIfStmt(ctx, stmt) {
12
13
  if (!canAssignTo(coditionExpr.type, bool_t)
13
14
  && !canAssignToOptional(coditionExpr.type))
14
15
  return ctx.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, stmt.condition.range, coditionExpr.type.toString(), "boolean | Optional<T>");
15
- const thenBranch = wrapManyStatements(_compileStatement(ctx.newBranchChildScope(), stmt.thenBranch), stmt.thenBranch.range);
16
+ const thenCtx = ctx.newBranchChildScope();
17
+ applyNarrowingsToScope(thenCtx.scope, extractIsNarrowings(coditionExpr, true));
18
+ const thenBranch = wrapManyStatements(_compileStatement(thenCtx, stmt.thenBranch), stmt.thenBranch.range);
16
19
  if (!thenBranch)
17
20
  return undefined;
18
21
  let elseBranch = undefined;
19
22
  if (stmt.elseBranch) {
20
- elseBranch = wrapManyStatements(_compileStatement(ctx.newBranchChildScope(), stmt.elseBranch), stmt.elseBranch.range);
23
+ const elseCtx = ctx.newBranchChildScope();
24
+ applyNarrowingsToScope(elseCtx.scope, extractIsNarrowings(coditionExpr, false));
25
+ elseBranch = wrapManyStatements(_compileStatement(elseCtx, stmt.elseBranch), stmt.elseBranch.range);
21
26
  if (!elseBranch)
22
27
  return undefined;
23
28
  }
@@ -3,4 +3,4 @@ import { TirMatchStmt, TirMatchStmtCase } from "../../../tir/statements/TirMatch
3
3
  import { DeconstructableTirType } from "../../../tir/types/utils/getDeconstructableType.js";
4
4
  import { AstCompilationCtx } from "../../AstCompilationCtx.js";
5
5
  export declare function _compileMatchStmt(ctx: AstCompilationCtx, stmt: MatchStmt): [TirMatchStmt] | undefined;
6
- export declare function _compileTirMatchStmtCase(ctx: AstCompilationCtx, matchCase: MatchStmtCase, deconstructableType: DeconstructableTirType, constrNamesAlreadySpecified: string[]): TirMatchStmtCase | undefined;
6
+ export declare function _compileTirMatchStmtCase(ctx: AstCompilationCtx, matchCase: MatchStmtCase, deconstructableType: DeconstructableTirType, constrNamesAlreadySpecified: string[], matchedVarName?: string): TirMatchStmtCase | undefined;