@harmoniclabs/pebble 0.1.8 → 0.1.9

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 +155 -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 +1 -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,144 @@ 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
+ const uniqueName = getUniqueInternalName(`${contractName}Datum`);
778
+ return new StructDecl(new Identifier(uniqueName, SourceRange.mock), [], // typeParams
779
+ stateDecls.map(s => new StructConstrDecl(new Identifier(s.name.text, s.name.range), s.fields, s.range)), defFlags, contractRange);
780
+ }
781
+ function _buildSpendCaseBlock(compiler, contractDecl, paramsInternalNamesMap, baseContextVars, contractRange) {
782
+ const mockRange = SourceRange.mock;
783
+ const hasPlainSpend = contractDecl.spendMethods.length > 0;
784
+ const hasState = contractDecl.stateDecls.length > 0;
785
+ // legacy / pure-plain-spend behavior: identical UPLC to before
786
+ if (!hasState) {
787
+ return _getMatchedPurposeBlockStatements(compiler, contractDecl.spendMethods, paramsInternalNamesMap, baseContextVars, "SpendRedeemer", contractRange);
788
+ }
789
+ // synthetic union datum type
790
+ const datumTypeDef = _deriveContractDatumTypeDef(contractDecl.name.text, contractDecl.stateDecls, contractRange);
791
+ compiler.registerInternalTypeDecl(datumTypeDef);
792
+ const datumTypeName = datumTypeDef.name.text;
793
+ // build fallback (used when datum is absent or doesn't match any state)
794
+ // we bind the plain-spend redeemer match as a 0-arg function value, then
795
+ // call it via `return fallback();` at every fallback site. the 0-arg-func
796
+ // → delay lowering keeps the body emitted only once.
797
+ let fallbackBindStmt;
798
+ let fallbackFuncName;
799
+ const makeFallbackBody = () => {
800
+ if (hasPlainSpend) {
801
+ return [
802
+ new ReturnStmt(new CallExpr(new Identifier(fallbackFuncName, mockRange), undefined, // genericTypeArgs
803
+ [], mockRange), mockRange)
804
+ ];
805
+ }
806
+ return [new FailStmt(undefined, contractRange)];
807
+ };
808
+ if (hasPlainSpend) {
809
+ fallbackFuncName = getUniqueInternalName("unknownDatum");
810
+ // pure-plain-spend behavior: identical UPLC to before
811
+ const plainBody = _getMatchedPurposeBlockStatements(compiler, contractDecl.spendMethods, paramsInternalNamesMap, baseContextVars, "SpendRedeemer", contractRange);
812
+ if (!Array.isArray(plainBody))
813
+ return undefined;
814
+ fallbackBindStmt = new VarStmt([
815
+ new SimpleVarDecl(new Identifier(fallbackFuncName, mockRange), new AstFuncType([], // params
816
+ new AstVoidType(mockRange), mockRange), new FuncExpr(new Identifier(fallbackFuncName, mockRange), CommonFlags.None, [], // typeParams
817
+ new AstFuncType([], // params
818
+ new AstVoidType(mockRange), mockRange), new BlockStmt(plainBody, contractRange), ArrowKind.None, mockRange), // initExpr
819
+ CommonFlags.Const, mockRange)
820
+ ], mockRange);
821
+ }
822
+ const datumUniqueName = getUniqueInternalName("datum");
823
+ const datumAsUnionName = getUniqueInternalName("datumUnion");
824
+ // build per-state cases
825
+ const datumMatchCases = [];
826
+ for (const stateDecl of contractDecl.stateDecls) {
827
+ let stateBody;
828
+ // deconstruct each field of the state's constructor into a fresh
829
+ // internal placeholder so the existing match-compiler accepts the
830
+ // pattern (which requires every field to be specified)
831
+ const fieldExtracts = stateDecl.fields.map(f => ({
832
+ originalName: f.name.text,
833
+ extractName: getUniqueInternalName(`${stateDecl.name.text}_${f.name.text}`),
834
+ range: f.name.range,
835
+ }));
836
+ if (stateDecl.spendMethods.length === 0) {
837
+ // readonly-state: never spendable
838
+ stateBody = [new FailStmt(undefined, stateDecl.range)];
839
+ }
840
+ else {
841
+ // synthesize a per-state single-constructor struct so that
842
+ // `state.fieldName` is type-checked against a struct that has
843
+ // only this state's fields (the multi-ctor union would reject
844
+ // the access since fields differ across constructors).
845
+ const perStateStructDecl = new StructDecl(new Identifier(getUniqueInternalName(`${contractDecl.name.text}_${stateDecl.name.text}`), mockRange), [], // typeParams
846
+ [
847
+ new StructConstrDecl(new Identifier(stateDecl.name.text, mockRange), stateDecl.fields, stateDecl.range)
848
+ ], StructDeclAstFlags.onlyDataEncoding | StructDeclAstFlags.untaggedSingleConstructor, stateDecl.range);
849
+ compiler.registerInternalTypeDecl(perStateStructDecl);
850
+ const perStateStructName = perStateStructDecl.name.text;
851
+ // synthesize: const state = StateStruct{ field1: _f1, ... };
852
+ // - state has type StateStruct (single-ctor) so state.x type-checks
853
+ // - LitNamedObjExpr at expressify short-circuits property access
854
+ // to the bound field value (the extracted variable)
855
+ const stateUniqueName = getUniqueInternalName(`state_${stateDecl.name.text}`);
856
+ const stateBindStmt = new VarStmt([
857
+ new SimpleVarDecl(new Identifier(stateUniqueName, mockRange), new AstNamedTypeExpr(new Identifier(perStateStructName, mockRange), [], // typeArgs
858
+ 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
859
+ CommonFlags.Const, mockRange)
860
+ ], mockRange);
861
+ const perStateContextVars = Object.freeze({
862
+ ...baseContextVars,
863
+ state: stateUniqueName,
864
+ });
865
+ const redeemerStmts = _getMatchedPurposeBlockStatements(compiler, stateDecl.spendMethods, paramsInternalNamesMap, perStateContextVars, `${stateDecl.name.text}Redeemer`, stateDecl.range);
866
+ if (!Array.isArray(redeemerStmts))
867
+ return undefined;
868
+ stateBody = [stateBindStmt, ...redeemerStmts];
869
+ }
870
+ const fieldsMap = new Map(fieldExtracts.map(fe => [
871
+ new Identifier(fe.originalName, mockRange),
872
+ SimpleVarDecl.onlyNameConst(fe.extractName, mockRange)
873
+ ]));
874
+ datumMatchCases.push(new MatchStmtCase(new NamedDeconstructVarDecl(new Identifier(stateDecl.name.text, mockRange), fieldsMap, undefined, // rest
875
+ undefined, // type
876
+ undefined, // initExpr
877
+ CommonFlags.Const, stateDecl.range), new BlockStmt(stateBody, stateDecl.range), stateDecl.range));
878
+ }
879
+ // bind: const datumUnion: ContractDatum = datum as ContractDatum;
880
+ // we match on this bound variable so the existing match-narrowing
881
+ // mechanism narrows it to the matched constructor inside each arm.
882
+ const datumAsUnionBindStmt = new VarStmt([
883
+ new SimpleVarDecl(new Identifier(datumAsUnionName, mockRange), new AstNamedTypeExpr(new Identifier(datumTypeName, mockRange), [], // typeArgs
884
+ mockRange), new TypeConversionExpr(new Identifier(datumUniqueName, mockRange), new AstNamedTypeExpr(new Identifier(datumTypeName, mockRange), [], // typeArgs
885
+ mockRange), mockRange), CommonFlags.Const, mockRange)
886
+ ], mockRange);
887
+ const datumMatchStmt = new MatchStmt(new Identifier(datumAsUnionName, mockRange), datumMatchCases, new MatchStmtElseCase(new BlockStmt(makeFallbackBody(), contractRange), contractRange), contractRange);
888
+ // outer match: optionalDatum -> Some{ value: datum } / Nothing
889
+ const optionalMatch = new MatchStmt(new Identifier(baseContextVars.optionalDatum, mockRange), [
890
+ new MatchStmtCase(new NamedDeconstructVarDecl(new Identifier("Some", mockRange), new Map([
891
+ [
892
+ new Identifier("value", mockRange),
893
+ SimpleVarDecl.onlyNameConst(datumUniqueName, mockRange)
894
+ ]
895
+ ]), undefined, // rest
896
+ undefined, // type
897
+ undefined, // initExpr
898
+ CommonFlags.Const, contractRange), new BlockStmt([datumAsUnionBindStmt, datumMatchStmt], contractRange), contractRange),
899
+ new MatchStmtCase(new NamedDeconstructVarDecl(new Identifier("None", mockRange), new Map(), undefined, // rest
900
+ undefined, // type
901
+ undefined, // initExpr
902
+ CommonFlags.Const, contractRange), new BlockStmt(makeFallbackBody(), contractRange), contractRange),
903
+ ], undefined, // elseCase
904
+ contractRange);
905
+ const result = [];
906
+ if (fallbackBindStmt)
907
+ result.push(fallbackBindStmt);
908
+ result.push(optionalMatch);
909
+ return result;
910
+ }
762
911
  function _deriveRedeemerTypeDef(redeemerName, methods, contractRange) {
763
912
  let defFlags = StructDeclAstFlags.onlyDataEncoding;
764
913
  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;