diff --git a/src/generator/writers/writeFunction.ts b/src/generator/writers/writeFunction.ts index eb30698a6..f365a47ff 100644 --- a/src/generator/writers/writeFunction.ts +++ b/src/generator/writers/writeFunction.ts @@ -466,8 +466,22 @@ export function writeStatement( return; } case "statement_destruct": { + const t = getExpType(ctx.ctx, f.expression); + if (t.kind !== "ref") { + throwInternalCompilerError( + `invalid destruct expression kind: ${t.kind}`, + f.expression.loc, + ); + } + const ty = getType(ctx.ctx, t.name); + const ids = ty.fields.map((field) => { + const id = f.identifiers.find( + (item) => item.from.text === field.name, + ); + return id ? funcIdOf(id.name) : "_"; + }); ctx.append( - `var (${f.identifiers.map((n) => funcIdOf(n)).join(", ")}) = ${writeCastedExpression(f.expression, getExpType(ctx.ctx, f.expression), ctx)};`, + `var (${ids.join(", ")}) = ${writeCastedExpression(f.expression, t, ctx)};`, ); return; } diff --git a/src/grammar/__snapshots__/grammar.spec.ts.snap b/src/grammar/__snapshots__/grammar.spec.ts.snap index 26cb88377..3032b2f7f 100644 --- a/src/grammar/__snapshots__/grammar.spec.ts.snap +++ b/src/grammar/__snapshots__/grammar.spec.ts.snap @@ -8056,7 +8056,7 @@ exports[`grammar should parse stmt-augmented-assign-logic 1`] = ` exports[`grammar should parse stmt-destructuring 1`] = ` { - "id": 87, + "id": 129, "imports": [], "items": [ { @@ -8135,7 +8135,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "attributes": [], - "id": 86, + "id": 128, "kind": "function_def", "loc": fun testFunc(): Int { let s = S{ a: 1, b: 2, c: 3 }; @@ -8244,30 +8244,63 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 30, + "id": 36, "kind": "id", "loc": s, "text": "s", }, - "id": 31, + "id": 37, "identifiers": [ { - "id": 27, - "kind": "id", + "from": { + "id": 27, + "kind": "id", + "loc": a, + "text": "a", + }, + "id": 29, + "kind": "destruct_mapping", "loc": a, - "text": "a", + "name": { + "id": 28, + "kind": "id", + "loc": a, + "text": "a", + }, }, { - "id": 28, - "kind": "id", + "from": { + "id": 30, + "kind": "id", + "loc": b, + "text": "b", + }, + "id": 32, + "kind": "destruct_mapping", "loc": b, - "text": "b", + "name": { + "id": 31, + "kind": "id", + "loc": b, + "text": "b", + }, }, { - "id": 29, - "kind": "id", + "from": { + "id": 33, + "kind": "id", + "loc": c, + "text": "c", + }, + "id": 35, + "kind": "destruct_mapping", "loc": c, - "text": "c", + "name": { + "id": 34, + "kind": "id", + "loc": c, + "text": "c", + }, }, ], "kind": "statement_destruct", @@ -8275,30 +8308,63 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 35, + "id": 47, "kind": "id", "loc": s, "text": "s", }, - "id": 36, + "id": 48, "identifiers": [ { - "id": 32, - "kind": "id", + "from": { + "id": 38, + "kind": "id", + "loc": a1, + "text": "a1", + }, + "id": 40, + "kind": "destruct_mapping", "loc": a1, - "text": "a1", + "name": { + "id": 39, + "kind": "id", + "loc": a1, + "text": "a1", + }, }, { - "id": 33, - "kind": "id", + "from": { + "id": 41, + "kind": "id", + "loc": _, + "text": "_", + }, + "id": 43, + "kind": "destruct_mapping", "loc": _, - "text": "_", + "name": { + "id": 42, + "kind": "id", + "loc": _, + "text": "_", + }, }, { - "id": 34, - "kind": "id", + "from": { + "id": 44, + "kind": "id", + "loc": _, + "text": "_", + }, + "id": 46, + "kind": "destruct_mapping", "loc": _, - "text": "_", + "name": { + "id": 45, + "kind": "id", + "loc": _, + "text": "_", + }, }, ], "kind": "statement_destruct", @@ -8306,30 +8372,63 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 40, + "id": 58, "kind": "id", "loc": s, "text": "s", }, - "id": 41, + "id": 59, "identifiers": [ { - "id": 37, - "kind": "id", + "from": { + "id": 49, + "kind": "id", + "loc": _, + "text": "_", + }, + "id": 51, + "kind": "destruct_mapping", "loc": _, - "text": "_", + "name": { + "id": 50, + "kind": "id", + "loc": _, + "text": "_", + }, }, { - "id": 38, - "kind": "id", + "from": { + "id": 52, + "kind": "id", + "loc": b1, + "text": "b1", + }, + "id": 54, + "kind": "destruct_mapping", "loc": b1, - "text": "b1", + "name": { + "id": 53, + "kind": "id", + "loc": b1, + "text": "b1", + }, }, { - "id": 39, - "kind": "id", + "from": { + "id": 55, + "kind": "id", + "loc": _, + "text": "_", + }, + "id": 57, + "kind": "destruct_mapping", "loc": _, - "text": "_", + "name": { + "id": 56, + "kind": "id", + "loc": _, + "text": "_", + }, }, ], "kind": "statement_destruct", @@ -8337,30 +8436,63 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 45, + "id": 69, "kind": "id", "loc": s, "text": "s", }, - "id": 46, + "id": 70, "identifiers": [ { - "id": 42, - "kind": "id", + "from": { + "id": 60, + "kind": "id", + "loc": _, + "text": "_", + }, + "id": 62, + "kind": "destruct_mapping", "loc": _, - "text": "_", + "name": { + "id": 61, + "kind": "id", + "loc": _, + "text": "_", + }, }, { - "id": 43, - "kind": "id", + "from": { + "id": 63, + "kind": "id", + "loc": _, + "text": "_", + }, + "id": 65, + "kind": "destruct_mapping", "loc": _, - "text": "_", + "name": { + "id": 64, + "kind": "id", + "loc": _, + "text": "_", + }, }, { - "id": 44, - "kind": "id", + "from": { + "id": 66, + "kind": "id", + "loc": c1, + "text": "c1", + }, + "id": 68, + "kind": "destruct_mapping", "loc": c1, - "text": "c1", + "name": { + "id": 67, + "kind": "id", + "loc": c1, + "text": "c1", + }, }, ], "kind": "statement_destruct", @@ -8368,30 +8500,63 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 50, + "id": 80, "kind": "id", "loc": s, "text": "s", }, - "id": 51, + "id": 81, "identifiers": [ { - "id": 47, - "kind": "id", + "from": { + "id": 71, + "kind": "id", + "loc": a2, + "text": "a2", + }, + "id": 73, + "kind": "destruct_mapping", "loc": a2, - "text": "a2", + "name": { + "id": 72, + "kind": "id", + "loc": a2, + "text": "a2", + }, }, { - "id": 48, - "kind": "id", + "from": { + "id": 74, + "kind": "id", + "loc": b2, + "text": "b2", + }, + "id": 76, + "kind": "destruct_mapping", "loc": b2, - "text": "b2", + "name": { + "id": 75, + "kind": "id", + "loc": b2, + "text": "b2", + }, }, { - "id": 49, - "kind": "id", + "from": { + "id": 77, + "kind": "id", + "loc": _, + "text": "_", + }, + "id": 79, + "kind": "destruct_mapping", "loc": _, - "text": "_", + "name": { + "id": 78, + "kind": "id", + "loc": _, + "text": "_", + }, }, ], "kind": "statement_destruct", @@ -8399,30 +8564,63 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 55, + "id": 91, "kind": "id", "loc": s, "text": "s", }, - "id": 56, + "id": 92, "identifiers": [ { - "id": 52, - "kind": "id", + "from": { + "id": 82, + "kind": "id", + "loc": a3, + "text": "a3", + }, + "id": 84, + "kind": "destruct_mapping", "loc": a3, - "text": "a3", + "name": { + "id": 83, + "kind": "id", + "loc": a3, + "text": "a3", + }, }, { - "id": 53, - "kind": "id", + "from": { + "id": 85, + "kind": "id", + "loc": _, + "text": "_", + }, + "id": 87, + "kind": "destruct_mapping", "loc": _, - "text": "_", + "name": { + "id": 86, + "kind": "id", + "loc": _, + "text": "_", + }, }, { - "id": 54, - "kind": "id", + "from": { + "id": 88, + "kind": "id", + "loc": c3, + "text": "c3", + }, + "id": 90, + "kind": "destruct_mapping", "loc": c3, - "text": "c3", + "name": { + "id": 89, + "kind": "id", + "loc": c3, + "text": "c3", + }, }, ], "kind": "statement_destruct", @@ -8430,30 +8628,63 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 60, + "id": 102, "kind": "id", "loc": s, "text": "s", }, - "id": 61, + "id": 103, "identifiers": [ { - "id": 57, - "kind": "id", + "from": { + "id": 93, + "kind": "id", + "loc": _, + "text": "_", + }, + "id": 95, + "kind": "destruct_mapping", "loc": _, - "text": "_", + "name": { + "id": 94, + "kind": "id", + "loc": _, + "text": "_", + }, }, { - "id": 58, - "kind": "id", + "from": { + "id": 96, + "kind": "id", + "loc": b4, + "text": "b4", + }, + "id": 98, + "kind": "destruct_mapping", "loc": b4, - "text": "b4", + "name": { + "id": 97, + "kind": "id", + "loc": b4, + "text": "b4", + }, }, { - "id": 59, - "kind": "id", + "from": { + "id": 99, + "kind": "id", + "loc": c4, + "text": "c4", + }, + "id": 101, + "kind": "destruct_mapping", "loc": c4, - "text": "c4", + "name": { + "id": 100, + "kind": "id", + "loc": c4, + "text": "c4", + }, }, ], "kind": "statement_destruct", @@ -8461,40 +8692,40 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 84, + "id": 126, "kind": "op_binary", "left": { - "id": 82, + "id": 124, "kind": "op_binary", "left": { - "id": 80, + "id": 122, "kind": "op_binary", "left": { - "id": 78, + "id": 120, "kind": "op_binary", "left": { - "id": 76, + "id": 118, "kind": "op_binary", "left": { - "id": 74, + "id": 116, "kind": "op_binary", "left": { - "id": 72, + "id": 114, "kind": "op_binary", "left": { - "id": 70, + "id": 112, "kind": "op_binary", "left": { - "id": 68, + "id": 110, "kind": "op_binary", "left": { - "id": 66, + "id": 108, "kind": "op_binary", "left": { - "id": 64, + "id": 106, "kind": "op_binary", "left": { - "id": 62, + "id": 104, "kind": "id", "loc": a, "text": "a", @@ -8502,7 +8733,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b, "op": "+", "right": { - "id": 63, + "id": 105, "kind": "id", "loc": b, "text": "b", @@ -8511,7 +8742,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c, "op": "+", "right": { - "id": 65, + "id": 107, "kind": "id", "loc": c, "text": "c", @@ -8520,7 +8751,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1, "op": "+", "right": { - "id": 67, + "id": 109, "kind": "id", "loc": a1, "text": "a1", @@ -8529,7 +8760,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1, "op": "+", "right": { - "id": 69, + "id": 111, "kind": "id", "loc": b1, "text": "b1", @@ -8538,7 +8769,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1, "op": "+", "right": { - "id": 71, + "id": 113, "kind": "id", "loc": c1, "text": "c1", @@ -8547,7 +8778,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2, "op": "+", "right": { - "id": 73, + "id": 115, "kind": "id", "loc": a2, "text": "a2", @@ -8556,7 +8787,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2, "op": "+", "right": { - "id": 75, + "id": 117, "kind": "id", "loc": b2, "text": "b2", @@ -8565,7 +8796,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2 + a3, "op": "+", "right": { - "id": 77, + "id": 119, "kind": "id", "loc": a3, "text": "a3", @@ -8574,7 +8805,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3, "op": "+", "right": { - "id": 79, + "id": 121, "kind": "id", "loc": c3, "text": "c3", @@ -8583,7 +8814,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4, "op": "+", "right": { - "id": 81, + "id": 123, "kind": "id", "loc": b4, "text": "b4", @@ -8592,13 +8823,13 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4 + c4, "op": "+", "right": { - "id": 83, + "id": 125, "kind": "id", "loc": c4, "text": "c4", }, }, - "id": 85, + "id": 127, "kind": "statement_return", "loc": return a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4 + c4;, }, diff --git a/src/grammar/ast.ts b/src/grammar/ast.ts index 4f263c39d..de19e33af 100644 --- a/src/grammar/ast.ts +++ b/src/grammar/ast.ts @@ -323,7 +323,7 @@ export type AstStatementForEach = { export type AstStatementDestruct = { kind: "statement_destruct"; - identifiers: AstId[]; + identifiers: AstDestructMapping[]; expression: AstExpression; id: number; loc: SrcInfo; @@ -582,6 +582,14 @@ export const selfId: AstId = { loc: dummySrcInfo, }; +export type AstDestructMapping = { + kind: "destruct_mapping"; + from: AstId; + name: AstId; + id: number; + loc: SrcInfo; +}; + export type AstNumber = { kind: "number"; base: AstNumberBase; @@ -688,6 +696,7 @@ export type AstReceiverKind = export type AstNode = | AstFuncId + | AstDestructMapping | AstExpression | AstStatement | AstTypeDecl diff --git a/src/grammar/grammar.ohm b/src/grammar/grammar.ohm index 217167950..24bd38071 100644 --- a/src/grammar/grammar.ohm +++ b/src/grammar/grammar.ohm @@ -158,7 +158,10 @@ Tact { StatementForEach = foreach "(" id "," id "in" Expression ")" "{" Statement* "}" - StatementDestruct = let "{" ListOf ","? "}" "=" Expression (";" | &"}") + StatementDestruct = let "{" ListOf ","? "}" "=" Expression (";" | &"}") + + DestructItem = id ":" id --withMap + | id --regular Expression = ExpressionConditional diff --git a/src/grammar/grammar.ts b/src/grammar/grammar.ts index 453e065c8..6d6e3337d 100644 --- a/src/grammar/grammar.ts +++ b/src/grammar/grammar.ts @@ -1001,7 +1001,7 @@ semantics.addOperation("astOfStatement", { kind: "statement_destruct", identifiers: identifiers .asIteration() - .children.map((id) => id.astOfExpression()), + .children.map((item) => item.astOfExpression()), expression: expression.astOfExpression(), loc: createRef(this), }); @@ -1134,6 +1134,22 @@ semantics.addOperation("astOfExpression", { loc: createRef(this), }); }, + DestructItem_regular(id) { + return createAstNode({ + kind: "destruct_mapping", + from: id.astOfExpression(), + name: id.astOfExpression(), + loc: createRef(this), + }); + }, + DestructItem_withMap(idFrom, _colon, id) { + return createAstNode({ + kind: "destruct_mapping", + from: idFrom.astOfExpression(), + name: id.astOfExpression(), + loc: createRef(this), + }); + }, ExpressionAdd_add(left, _plus, right) { return createAstNode({ kind: "op_binary", diff --git a/src/grammar/iterators.ts b/src/grammar/iterators.ts index 5ef7f33a5..4ae2fa033 100644 --- a/src/grammar/iterators.ts +++ b/src/grammar/iterators.ts @@ -114,7 +114,8 @@ export function traverse(node: AstNode, callback: (node: AstNode) => void) { break; case "statement_destruct": node.identifiers.forEach((e) => { - traverse(e, callback); + traverse(e.from, callback); + traverse(e.name, callback); }); traverse(node.expression, callback); break; @@ -179,6 +180,10 @@ export function traverse(node: AstNode, callback: (node: AstNode) => void) { traverse(e, callback); }); break; + case "destruct_mapping": + traverse(node.from, callback); + traverse(node.name, callback); + break; case "type_id": break; case "optional_type": diff --git a/src/interpreter.ts b/src/interpreter.ts index 58fe5073a..d5123dc40 100644 --- a/src/interpreter.ts +++ b/src/interpreter.ts @@ -1443,11 +1443,11 @@ export class Interpreter { } public interpretDestructStatement(ast: AstStatementDestruct) { - for (const name of ast.identifiers) { - if (hasStaticConstant(this.context, idText(name))) { + for (const item of ast.identifiers) { + if (hasStaticConstant(this.context, idText(item.name))) { // Attempt of shadowing a constant in a destructuring declaration throwInternalCompilerError( - `declaration of ${idText(name)} shadows a constant with the same name`, + `declaration of ${idText(item.name)} shadows a constant with the same name`, ast.loc, ); } @@ -1474,18 +1474,18 @@ export class Interpreter { ); } - for (const name of ast.identifiers) { - const v = val[idText(name)]; + for (const item of ast.identifiers) { + const v = val[idText(item.from)]; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (v === undefined) { throwErrorConstEval( `destructuring assignment expected field ${idTextErr( - name, + item.from, )}`, ast.loc, ); } - this.envStack.setNewBinding(idText(name), v); + this.envStack.setNewBinding(idText(item.name), v); } } diff --git a/src/prettyPrinter.ts b/src/prettyPrinter.ts index 6062c961a..0953d56b6 100644 --- a/src/prettyPrinter.ts +++ b/src/prettyPrinter.ts @@ -687,7 +687,13 @@ export class PrettyPrinter { } ppAstStatementDestruct(statement: AstStatementDestruct): string { - const ids = statement.identifiers.map((id) => this.ppAstId(id)); + const ids = statement.identifiers.map((item) => { + if (item.from.text === item.name.text) { + return this.ppAstId(item.name); + } else { + return `${this.ppAstId(item.from)}: ${this.ppAstId(item.name)}`; + } + }); return `${this.indent()}let {${ids.join(", ")}} = ${this.ppAstExpression(statement.expression)};`; } } diff --git a/src/types/__snapshots__/resolveStatements.spec.ts.snap b/src/types/__snapshots__/resolveStatements.spec.ts.snap index cacc89573..5d45e4107 100644 --- a/src/types/__snapshots__/resolveStatements.spec.ts.snap +++ b/src/types/__snapshots__/resolveStatements.spec.ts.snap @@ -899,22 +899,32 @@ Line 5, col 5: `; exports[`resolveStatements should fail statements for stmt-destructuring-fields 1`] = ` -":15:26: Type 'S' has 3 fields, but 4 variables are provided -Line 15, col 26: +":15:20: Field '"d"' not found in type 'S' +Line 15, col 20: 14 | let s = S{ a: 1, b: 2, c: 3 }; > 15 | let { a, b, c, d } = s; - ^ + ^ 16 | return a + b + c + d; " `; exports[`resolveStatements should fail statements for stmt-destructuring-fields2 1`] = ` -":15:20: Type 'S' has 3 fields, but 2 variables are provided +":15:20: Field '"d"' not found in type 'S' Line 15, col 20: 14 | let s = S{ a: 1, b: 2, c: 3 }; -> 15 | let { a, b } = s; +> 15 | let { a, b, c, d: e } = s; ^ - 16 | return a + b; + 16 | return a + b + c + e; +" +`; + +exports[`resolveStatements should fail statements for stmt-destructuring-fields3 1`] = ` +":15:14: Field '"bb"' not found in type 'S' +Line 15, col 14: + 14 | let s = S{ a: 1, b: 2, c: 3 }; +> 15 | let { a, bb, c } = s; + ^~ + 16 | return a + bb + c; " `; diff --git a/src/types/resolveStatements.ts b/src/types/resolveStatements.ts index 638dc0b71..7c675671d 100644 --- a/src/types/resolveStatements.ts +++ b/src/types/resolveStatements.ts @@ -691,8 +691,8 @@ function processStatements( ctx = resolveExpression(s.expression, sctx, ctx); // Check variable names - for (const name of s.identifiers) { - checkVariableExists(ctx, sctx, name); + for (const item of s.identifiers) { + checkVariableExists(ctx, sctx, item.name); } // Check type @@ -716,16 +716,20 @@ function processStatements( s.expression.loc, ); } - if (ty.fields.length !== s.identifiers.length) { - throwCompilationError( - `Type '${printTypeRef(expressionType)}' has ${ty.fields.length} fields, but ${s.identifiers.length} variables are provided`, - s.expression.loc, - ); - } // Add variables - s.identifiers.forEach((name, i) => { - sctx = addVariable(name, ty.fields[i]!.type, ctx, sctx); + s.identifiers.forEach((item) => { + if (item.name.text === "_") { + return; + } + const f = ty.fields.find((f) => eqNames(f.name, item.from)); + if (!f) { + throwCompilationError( + `Field '${idTextErr(item.from)}' not found in type '${expressionType.name}'`, + item.from.loc, + ); + } + sctx = addVariable(item.name, f.type, ctx, sctx); }); break; diff --git a/src/types/stmts-failed/stmt-destructuring-fields2.tact b/src/types/stmts-failed/stmt-destructuring-fields2.tact index fa61ef6d7..5901ebe87 100644 --- a/src/types/stmts-failed/stmt-destructuring-fields2.tact +++ b/src/types/stmts-failed/stmt-destructuring-fields2.tact @@ -12,6 +12,6 @@ struct S { fun testFunc(): Int { let s = S{ a: 1, b: 2, c: 3 }; - let { a, b } = s; - return a + b; + let { a, b, c, d: e } = s; + return a + b + c + e; } \ No newline at end of file diff --git a/src/types/stmts-failed/stmt-destructuring-fields3.tact b/src/types/stmts-failed/stmt-destructuring-fields3.tact new file mode 100644 index 000000000..603f82692 --- /dev/null +++ b/src/types/stmts-failed/stmt-destructuring-fields3.tact @@ -0,0 +1,17 @@ +primitive Int; + +trait BaseTrait { + +} + +struct S { + a: Int; + b: Int; + c: Int; +} + +fun testFunc(): Int { + let s = S{ a: 1, b: 2, c: 3 }; + let { a, bb, c } = s; + return a + bb + c; +} \ No newline at end of file diff --git a/src/types/stmts/stmt-destructuring.tact b/src/types/stmts/stmt-destructuring.tact index a5f0172e0..8cf3d2171 100644 --- a/src/types/stmts/stmt-destructuring.tact +++ b/src/types/stmts/stmt-destructuring.tact @@ -18,15 +18,15 @@ message M { fun testFunc(): Int { let s = S{ a: 1, b: 2, c: 3 }; let { a, b, c } = s; - let { a1, _, _ } = s; - let { _, b1, _ } = s; - let { _, _, c1 } = s; - let { a2, b2, _ } = s; - let { a3, _, c3 } = s; - let { _, b4, c4 } = s; + let { a: a1, _, _ } = s; + let { _, b: b1, _ } = s; + let { _, _, c: c1 } = s; + let { a: a2, b: b2, _ } = s; + let { a: a3, _, c: c3 } = s; + let { _, b: b4, c: c4 } = s; let m = M{ a: 1, b: 2 }; - let { a_m, b_m } = m; + let { a: a_m, b: b_m } = m; return a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4 + c4 + a_m + b_m; } \ No newline at end of file