progress on arrays and slices
All checks were successful
Check / Explore-Gitea-Actions (push) Successful in 8s

This commit is contained in:
sfja 2026-03-17 00:06:43 +01:00
parent 8b10163805
commit 54ee879b45
9 changed files with 196 additions and 126 deletions

View File

@ -93,9 +93,9 @@ export class Node {
case "ArrayExpr": case "ArrayExpr":
return visit(...k.values); return visit(...k.values);
case "IndexExpr": case "IndexExpr":
return visit(k.expr, k.arg); return visit(k.value, k.arg);
case "CallExpr": case "CallExpr":
return visit(k.expr, ...k.args); return visit(k.value, ...k.args);
case "UnaryExpr": case "UnaryExpr":
return visit(k.expr); return visit(k.expr);
case "BinaryExpr": case "BinaryExpr":
@ -136,8 +136,8 @@ export type NodeKind =
| { tag: "IdentExpr"; ident: string } | { tag: "IdentExpr"; ident: string }
| { tag: "IntExpr"; value: number } | { tag: "IntExpr"; value: number }
| { tag: "ArrayExpr"; values: Node[] } | { tag: "ArrayExpr"; values: Node[] }
| { tag: "IndexExpr"; expr: Node; arg: Node } | { tag: "IndexExpr"; value: Node; arg: Node }
| { tag: "CallExpr"; expr: Node; args: Node[] } | { tag: "CallExpr"; value: Node; args: Node[] }
| { tag: "UnaryExpr"; op: UnaryOp; expr: Node; tok: string } | { tag: "UnaryExpr"; op: UnaryOp; expr: Node; tok: string }
| { tag: "BinaryExpr"; op: BinaryOp; left: Node; right: Node; tok: string } | { tag: "BinaryExpr"; op: BinaryOp; left: Node; right: Node; tok: string }
| { | {

View File

@ -15,6 +15,24 @@ export class Tys {
this.checker = new Checker(this, this.syms, this.reporter); this.checker = new Checker(this, this.syms, this.reporter);
} }
fnStmt(node: ast.NodeWithKind<"FnStmt">): Ty {
if (this.nodeTys.has(node.id)) {
return this.nodeTys.get(node.id)!;
}
const ty = this.checker.checkFnStmt(node);
this.nodeTys.set(node.id, ty);
return ty;
}
place(node: ast.Node): Ty {
if (this.nodeTys.has(node.id)) {
return this.nodeTys.get(node.id)!;
}
const ty = this.checker.checkExpr(node);
this.nodeTys.set(node.id, ty);
return ty;
}
expr(node: ast.Node): Ty { expr(node: ast.Node): Ty {
if (this.nodeTys.has(node.id)) { if (this.nodeTys.has(node.id)) {
return this.nodeTys.get(node.id)!; return this.nodeTys.get(node.id)!;
@ -32,13 +50,44 @@ class Checker {
private reporter: FileReporter, private reporter: FileReporter,
) {} ) {}
checkFnStmt(stmt: ast.NodeWithKind<"FnStmt">): Ty {
const k = stmt.kind;
const params = k.params.map((param) => this.tys.expr(param));
const retTy = k.retTy ? this.tys.expr(k.retTy) : Ty.Void;
k.body.visit({
visit: (node) => {
if (node.is("ReturnStmt")) {
const ty = node.kind.expr
? this.tys.expr(node.kind.expr)
: Ty.Void;
if (!ty.compatibleWith(retTy)) {
this.error(
node.loc,
`type '${ty.pretty()}' not compatible with return type '${retTy.pretty()}'`,
);
this.info(
stmt.kind.retTy?.loc ?? stmt.loc,
`return type '${retTy}' defined here`,
);
this.fail();
}
}
},
});
const ty = Ty.create("Fn", { params, retTy });
return Ty.create("FnStmt", { stmt, ty });
}
checkPlace(node: ast.Node): Ty {
return this.checkExpr(node);
}
checkExpr(node: ast.Node): Ty { checkExpr(node: ast.Node): Ty {
const k = node.kind; const k = node.kind;
if (node.is("FnStmt")) {
return this.checkFnStmt(node);
}
if (node.is("Param")) { if (node.is("Param")) {
const sym = this.syms.get(node); const sym = this.syms.get(node);
@ -68,7 +117,7 @@ class Checker {
if (node.is("IdentExpr")) { if (node.is("IdentExpr")) {
const sym = this.syms.get(node); const sym = this.syms.get(node);
if (sym.tag === "Fn") { if (sym.tag === "Fn") {
return this.tys.expr(sym.stmt); return this.tys.fnStmt(sym.stmt);
} }
if (sym.tag === "Bool") { if (sym.tag === "Bool") {
return Ty.Bool; return Ty.Bool;
@ -108,7 +157,7 @@ class Checker {
} }
if (node.is("IndexExpr")) { if (node.is("IndexExpr")) {
const exprTy = this.tys.expr(node.kind.expr); const exprTy = this.tys.expr(node.kind.value);
const argTy = this.tys.expr(node.kind.arg); const argTy = this.tys.expr(node.kind.arg);
if ( if (
(exprTy.is("Array") || exprTy.is("Slice")) && (exprTy.is("Array") || exprTy.is("Slice")) &&
@ -243,39 +292,8 @@ class Checker {
throw new Error(`'${k.tag}' not unhandled`); throw new Error(`'${k.tag}' not unhandled`);
} }
private checkFnStmt(stmt: ast.NodeWithKind<"FnStmt">): Ty {
const k = stmt.kind;
const params = k.params.map((param) => this.tys.expr(param));
const retTy = k.retTy ? this.tys.expr(k.retTy) : Ty.Void;
k.body.visit({
visit: (node) => {
if (node.is("ReturnStmt")) {
const ty = node.kind.expr
? this.tys.expr(node.kind.expr)
: Ty.Void;
if (!ty.compatibleWith(retTy)) {
this.error(
node.loc,
`type '${ty.pretty()}' not compatible with return type '${retTy.pretty()}'`,
);
this.info(
stmt.kind.retTy?.loc ?? stmt.loc,
`return type '${retTy}' defined here`,
);
this.fail();
}
}
},
});
const ty = Ty.create("Fn", { params, retTy });
return Ty.create("FnStmt", { stmt, ty });
}
private checkCall(node: ast.NodeWithKind<"CallExpr">): Ty { private checkCall(node: ast.NodeWithKind<"CallExpr">): Ty {
const calleeTy = this.tys.expr(node.kind.expr); const calleeTy = this.tys.expr(node.kind.value);
const callableTy = calleeTy.is("Fn") const callableTy = calleeTy.is("Fn")
? calleeTy ? calleeTy

View File

@ -239,7 +239,7 @@ export class Parser {
} else if (this.eat("[")) { } else if (this.eat("[")) {
const arg = this.parseExpr(); const arg = this.parseExpr();
this.mustEat("]"); this.mustEat("]");
expr = ast.Node.create(loc, "IndexExpr", { expr, arg }); expr = ast.Node.create(loc, "IndexExpr", { value: expr, arg });
} else if (this.eat("(")) { } else if (this.eat("(")) {
const args: ast.Node[] = []; const args: ast.Node[] = [];
if (!this.test(")")) { if (!this.test(")")) {
@ -252,7 +252,7 @@ export class Parser {
} }
} }
this.mustEat(")"); this.mustEat(")");
expr = ast.Node.create(loc, "CallExpr", { expr, args }); expr = ast.Node.create(loc, "CallExpr", { value: expr, args });
} else { } else {
break; break;
} }

View File

@ -4,14 +4,14 @@ import { builtins } from "./builtins.ts";
export class Syms { export class Syms {
constructor( constructor(
private resols: Map<number, Sym>, private symMap: Map<number, Sym>,
) {} ) {}
get(node: ast.Node): Sym { get(node: ast.Node): Sym {
if (!this.resols.has(node.id)) { if (!this.symMap.has(node.id)) {
throw new Error(`'${node.kind.tag}' not resolved`); throw new Error(`'${node.kind.tag}' not resolved`);
} }
return this.resols.get(node.id)!; return this.symMap.get(node.id)!;
} }
} }

View File

@ -7,7 +7,7 @@ export class MiddleLowerer {
private fns = new Map<number, Fn>(); private fns = new Map<number, Fn>();
constructor( constructor(
private resols: Syms, private syms: Syms,
private tys: Tys, private tys: Tys,
) {} ) {}
@ -15,7 +15,7 @@ export class MiddleLowerer {
if (this.fns.has(stmt.id)) { if (this.fns.has(stmt.id)) {
return this.fns.get(stmt.id)!; return this.fns.get(stmt.id)!;
} }
const fn = new FnLowerer(this, this.resols, this.tys, stmt).lower(); const fn = new FnLowerer(this, this.syms, this.tys, stmt).lower();
this.fns.set(stmt.id, fn); this.fns.set(stmt.id, fn);
return fn; return fn;
} }
@ -28,13 +28,13 @@ class FnLowerer {
constructor( constructor(
private lowerer: MiddleLowerer, private lowerer: MiddleLowerer,
private resols: Syms, private syms: Syms,
private tys: Tys, private tys: Tys,
private stmt: ast.FnStmt, private stmt: ast.FnStmt,
) {} ) {}
lower(): Fn { lower(): Fn {
const ty = this.tys.expr(this.stmt); const ty = this.tys.fnStmt(this.stmt);
this.lowerBlock(this.stmt.kind.body.as("Block")); this.lowerBlock(this.stmt.kind.body.as("Block"));
this.pushInst(Ty.Void, "Return", { source: this.makeVoid() }); this.pushInst(Ty.Void, "Return", { source: this.makeVoid() });
this.bbs[0].insts.unshift(...this.allocs); this.bbs[0].insts.unshift(...this.allocs);
@ -51,7 +51,10 @@ class FnLowerer {
if (stmt.is("LetStmt")) { if (stmt.is("LetStmt")) {
const ty = this.tys.expr(stmt.kind.param); const ty = this.tys.expr(stmt.kind.param);
const expr = this.lowerExpr(stmt.kind.expr); const expr = this.lowerExpr(stmt.kind.expr);
const local = new Inst(Ty.create("Ptr", { ty }), { tag: "Alloca" }); const local = new Inst(
Ty.create("PtrMut", { ty }),
{ tag: "Alloca" },
);
this.allocs.push(local); this.allocs.push(local);
this.pushInst(Ty.Void, "Store", { this.pushInst(Ty.Void, "Store", {
target: local, target: local,
@ -119,8 +122,9 @@ class FnLowerer {
} }
private lowerPlace(place: ast.Node): Inst { private lowerPlace(place: ast.Node): Inst {
const ty = this.tys.place(place);
if (place.is("IdentExpr")) { if (place.is("IdentExpr")) {
const sym = this.resols.get(place); const sym = this.syms.get(place);
if (sym.tag === "Let") { if (sym.tag === "Let") {
const local = this.localMap.get(sym.param.id); const local = this.localMap.get(sym.param.id);
if (!local) { if (!local) {
@ -128,27 +132,36 @@ class FnLowerer {
} }
return local; return local;
} }
if (sym.tag === "FnParam") {
return this.lowerExpr(place);
}
throw new Error(`'${sym.tag}' not handled`); throw new Error(`'${sym.tag}' not handled`);
} }
if (place.is("UnaryExpr") && place.kind.op === "Deref") { if (place.is("UnaryExpr") && place.kind.op === "Deref") {
return this.lowerExpr(place.kind.expr); const source = this.lowerPlace(place.kind.expr);
return this.pushInst(
Ty.create("PtrMut", { ty }),
"Load",
{ source },
);
} }
if (place.is("IndexExpr")) { if (place.is("IndexExpr")) {
const exprTy = this.tys.expr(place.kind.expr); const value = place.kind.value;
if (!exprTy.is("Array") && !exprTy.is("Slice")) { const valueTy = this.tys.expr(value);
throw new Error(exprTy.pretty());
}
const arg = place.kind.arg; const arg = place.kind.arg;
const argTy = this.tys.expr(arg); const argTy = this.tys.expr(arg);
const exprInst = this.lowerExpr(place.kind.expr); if (!valueTy.is("Array") && !valueTy.is("Slice")) {
throw new Error();
}
const valueInst = this.lowerPlace(place.kind.value);
if (argTy.is("Int")) { if (argTy.is("Int")) {
const argInst = this.lowerExpr(arg); const argInst = this.lowerExpr(arg);
return this.pushInst( return this.pushInst(
Ty.create("PtrMut", { ty: exprTy.kind.ty }), Ty.create("PtrMut", { ty: valueTy.kind.ty }),
"Index", "Index",
{ value: exprInst, arg: argInst }, { base: valueInst, offset: argInst },
); );
} }
if (argTy.is("Range")) { if (argTy.is("Range")) {
@ -161,10 +174,10 @@ class FnLowerer {
this.lowerExpr(arg.kind.end); this.lowerExpr(arg.kind.end);
return this.pushInst( return this.pushInst(
Ty.create("PtrMut", { Ty.create("PtrMut", {
ty: Ty.create("Slice", { ty: exprTy.kind.ty }), ty: Ty.create("Slice", { ty: valueTy.kind.ty }),
}), }),
"Slice", "Slice",
{ value: exprInst, begin, end }, { value: valueInst, begin, end },
); );
} }
throw new Error( throw new Error(
@ -176,8 +189,9 @@ class FnLowerer {
} }
private lowerExpr(expr: ast.Node): Inst { private lowerExpr(expr: ast.Node): Inst {
const ty = this.tys.expr(expr);
if (expr.is("IdentExpr")) { if (expr.is("IdentExpr")) {
const sym = this.resols.get(expr); const sym = this.syms.get(expr);
if (sym.tag === "Fn") { if (sym.tag === "Fn") {
const fn = this.lowerer.lowerFn(sym.stmt); const fn = this.lowerer.lowerFn(sym.stmt);
return this.pushInst(fn.ty, "Fn", { fn }); return this.pushInst(fn.ty, "Fn", { fn });
@ -190,11 +204,8 @@ class FnLowerer {
throw new Error("handle elsewhere"); throw new Error("handle elsewhere");
} }
if (sym.tag === "Let") { if (sym.tag === "Let") {
const local = this.localMap.get(sym.param.id); const source = this.lowerPlace(expr);
if (!local) { return this.pushInst(ty, "Load", { source });
throw new Error();
}
return this.pushInst(local.ty, "Load", { source: local });
} }
if (sym.tag === "Bool") { if (sym.tag === "Bool") {
return this.pushInst(Ty.Bool, "Bool", { value: sym.value }); return this.pushInst(Ty.Bool, "Bool", { value: sym.value });
@ -211,18 +222,16 @@ class FnLowerer {
return this.pushInst(ty, "Array", { values }); return this.pushInst(ty, "Array", { values });
} }
if (expr.is("IndexExpr")) { if (expr.is("IndexExpr")) {
const ty = this.tys.expr(expr.kind.expr); const source = this.lowerPlace(expr);
const arg = this.lowerExpr(expr.kind.arg); return this.pushInst(ty, "Load", { source });
const value = this.lowerExpr(expr.kind.expr);
return this.pushInst(ty, "Index", { value, arg });
} }
if (expr.is("CallExpr")) { if (expr.is("CallExpr")) {
const ty = this.tys.expr(expr); const ty = this.tys.expr(expr);
const args = expr.kind.args const args = expr.kind.args
.map((arg) => this.lowerExpr(arg)); .map((arg) => this.lowerExpr(arg));
if (expr.kind.expr.is("IdentExpr")) { if (expr.kind.value.is("IdentExpr")) {
const sym = this.resols.get(expr.kind.expr); const sym = this.syms.get(expr.kind.value);
if (sym.tag === "Builtin") { if (sym.tag === "Builtin") {
if (sym.id === "__add") { if (sym.id === "__add") {
const [left, right] = args; const [left, right] = args;
@ -235,7 +244,7 @@ class FnLowerer {
} }
} }
const callee = this.lowerExpr(expr.kind.expr); const callee = this.lowerExpr(expr.kind.value);
return this.pushInst(ty, "Call", { callee, args }); return this.pushInst(ty, "Call", { callee, args });
} }
if (expr.is("UnaryExpr")) { if (expr.is("UnaryExpr")) {
@ -260,7 +269,7 @@ class FnLowerer {
if (expr.kind.op === "Ref" || expr.kind.op === "RefMut") { if (expr.kind.op === "Ref" || expr.kind.op === "RefMut") {
const place = expr.kind.expr; const place = expr.kind.expr;
if (place.is("IdentExpr")) { if (place.is("IdentExpr")) {
const sym = this.resols.get(place); const sym = this.syms.get(place);
if (sym.tag === "Let") { if (sym.tag === "Let") {
const local = this.localMap.get(sym.param.id); const local = this.localMap.get(sym.param.id);
if (!local) { if (!local) {

View File

@ -128,7 +128,7 @@ export class Inst {
case "Param": case "Param":
return `${k.idx}`; return `${k.idx}`;
case "Index": case "Index":
return `${r(k.value)} [${r(k.arg)}]`; return `${r(k.base)} &[${r(k.offset)}]`;
case "Slice": case "Slice":
return `${r(k.value)} [${k.begin ? r(k.begin) : ""}..${ return `${r(k.value)} [${k.begin ? r(k.begin) : ""}..${
k.end ? r(k.end) : "" k.end ? r(k.end) : ""
@ -144,7 +144,7 @@ export class Inst {
case "Jump": case "Jump":
return `bb${cx.bbId(k.target)}`; return `bb${cx.bbId(k.target)}`;
case "Branch": case "Branch":
return `if ${r(k.cond)} then bb${cx.bbId(k.truthy)} else bb${ return `if ${r(k.cond)}: bb${cx.bbId(k.truthy)}, else: bb${
cx.bbId(k.falsy) cx.bbId(k.falsy)
}`; }`;
case "Return": case "Return":
@ -184,7 +184,7 @@ export type InstKind =
| { tag: "Array"; values: Inst[] } | { tag: "Array"; values: Inst[] }
| { tag: "Fn"; fn: Fn } | { tag: "Fn"; fn: Fn }
| { tag: "Param"; idx: number } | { tag: "Param"; idx: number }
| { tag: "Index"; value: Inst; arg: Inst } | { tag: "Index"; base: Inst; offset: Inst }
| { tag: "Slice"; value: Inst; begin: Inst | null; end: Inst | null } | { tag: "Slice"; value: Inst; begin: Inst | null; end: Inst | null }
| { tag: "Call"; callee: Inst; args: Inst[] } | { tag: "Call"; callee: Inst; args: Inst[] }
| { tag: "Alloca" } | { tag: "Alloca" }

View File

@ -13,13 +13,14 @@ export class FnInterpreter {
} }
eval(): Val { eval(): Val {
const cx = new mir.PrettyCx();
while (this.instIdx < this.bb.insts.length) { while (this.instIdx < this.bb.insts.length) {
const inst = this.bb.insts[this.instIdx]; const inst = this.bb.insts[this.instIdx];
this.instIdx += 1; this.instIdx += 1;
// console.log( // console.log(poin
// `[${this.instIdx.toString().padStart(2, " ")}] ${ // `[${this.instIdx.toString().padStart(2, " ")}] ${
// inst.pretty(new mir.PrettyCx()) // inst.pretty(cx)
// }`, // }`,
// ); // );
@ -49,39 +50,70 @@ export class FnInterpreter {
case "Param": case "Param":
this.regs.set(inst, this.args[k.idx]); this.regs.set(inst, this.args[k.idx]);
break; break;
// case "Index": {
// const idx = this.regs.get(k.arg)!;
// if (idx.kind.tag !== "Int") {
// throw new Error();
// }
// const value = this.regs.get(k.value)!;
// if (value.kind.tag === "Array") {
// if (idx.kind.value >= value.kind.values.length) {
// throw new Error();
// }
// this.regs.set(inst, value.kind.values[idx.kind.value]);
// } else if (value.kind.tag === "Slice") {
// if (value.kind.value.kind.tag !== "Array") {
// throw new Error();
// }
// const values = value.kind.value.kind.values;
// const begin = value.kind.value;
// const end = value.kind.end;
// if (
// begin.kind.tag !== "Int" || end.kind.tag !== "Int"
// ) {
// throw new Error();
// }
// if (
// begin.kind.value + idx.kind.value < 0 ||
// end.kind.value + idx.kind.value >= values.length
// ) {
// throw new Error();
// }
// this.regs.set(
// inst,
// values[begin.kind.value + idx.kind.value],
// );
// } else {
// throw new Error();
// }
// break;
// }
case "Index": { case "Index": {
const idx = this.regs.get(k.arg)!; const offset = this.regs.get(k.offset)!;
if (idx.kind.tag !== "Int") { if (offset.kind.tag !== "Int") {
throw new Error(); throw new Error();
} }
const value = this.regs.get(k.value)!; const base = this.regs.get(k.base)!;
if (value.kind.tag === "Array") { if (base.kind.tag === "Ptr") {
if (idx.kind.value >= value.kind.values.length) { const array = base.kind.value;
if (array.kind.tag !== "Array") {
console.log({ array });
throw new Error(); throw new Error();
} }
this.regs.set(inst, value.kind.values[idx.kind.value]); if (offset.kind.value >= array.kind.values.length) {
} else if (value.kind.tag === "Slice") {
if (value.kind.value.kind.tag !== "Array") {
throw new Error();
}
const values = value.kind.value.kind.values;
const begin = value.kind.value;
const end = value.kind.end;
if (
begin.kind.tag !== "Int" || end.kind.tag !== "Int"
) {
throw new Error();
}
if (
begin.kind.value + idx.kind.value < 0 ||
end.kind.value + idx.kind.value >= values.length
) {
throw new Error(); throw new Error();
} }
this.regs.set( this.regs.set(
inst, inst,
values[begin.kind.value + idx.kind.value], new Val({
tag: "ArrayElemPtr",
values: array.kind.values,
idx: offset.kind.value,
mutable: base.kind.mutable,
}),
); );
} else if (base.kind.tag === "Slice") {
throw new Error();
} else { } else {
throw new Error(); throw new Error();
} }
@ -96,7 +128,11 @@ export class FnInterpreter {
) { ) {
throw new Error(); throw new Error();
} }
const value = this.regs.get(k.value)!; const ptr = this.regs.get(k.value)!;
if (ptr.kind.tag !== "Ptr") {
throw new Error();
}
const value = ptr.kind.value;
if (value.kind.tag !== "Array") { if (value.kind.tag !== "Array") {
throw new Error(); throw new Error();
} }
@ -150,19 +186,29 @@ export class FnInterpreter {
} }
case "Load": { case "Load": {
const source = this.regs.get(k.source)!; const source = this.regs.get(k.source)!;
if (source.kind.tag !== "Ptr") { if (source.kind.tag === "Ptr") {
this.regs.set(inst, source.kind.value);
} else if (source.kind.tag === "ArrayElemPtr") {
this.regs.set(
inst,
source.kind.values[source.kind.idx],
);
} else {
throw new Error(); throw new Error();
} }
this.regs.set(inst, source.kind.value);
break; break;
} }
case "Store": { case "Store": {
const target = this.regs.get(k.target)!; const target = this.regs.get(k.target)!;
if (target.kind.tag !== "Ptr") { if (target.kind.tag === "Ptr") {
const source = this.regs.get(k.source)!;
target.kind.value = source;
} else if (target.kind.tag === "ArrayElemPtr") {
const source = this.regs.get(k.source)!;
target.kind.values[target.kind.idx] = source;
} else {
throw new Error(); throw new Error();
} }
const source = this.regs.get(k.source)!;
target.kind.value = source;
break; break;
} }
case "Jump": { case "Jump": {
@ -309,6 +355,7 @@ class Val {
case "Bool": case "Bool":
return `${k.value}`; return `${k.value}`;
case "Ptr": case "Ptr":
case "ArrayElemPtr":
return `<pointer>`; return `<pointer>`;
case "Slice": case "Slice":
if (k.value.kind.tag !== "Array") { if (k.value.kind.tag !== "Array") {
@ -340,6 +387,7 @@ type ValKind =
| { tag: "Int"; value: number } | { tag: "Int"; value: number }
| { tag: "Bool"; value: boolean } | { tag: "Bool"; value: boolean }
| { tag: "Ptr"; mutable: boolean; value: Val } | { tag: "Ptr"; mutable: boolean; value: Val }
| { tag: "ArrayElemPtr"; mutable: boolean; values: Val[]; idx: number }
| { tag: "Slice"; value: Val; begin: Val; end: Val } | { tag: "Slice"; value: Val; begin: Val; end: Val }
| { tag: "Array"; values: Val[] } | { tag: "Array"; values: Val[] }
| { tag: "Fn"; fn: mir.Fn }; | { tag: "Fn"; fn: mir.Fn };

View File

@ -3,25 +3,20 @@ fn main()
{ {
let array: [int; 3] = [1, 2, 3]; let array: [int; 3] = [1, 2, 3];
// let a = 4; let elem: int = array[0];
// let b = a; // expect: 1
// array[0] = a; print_int(elem);
// print_int(array[0]);
// let elem: int = array[0]; let ptr_to_array: *[int; 3] = &array;
// // e xpect: 1 // expect: 2
// print_int(elem); print_int(ptr_to_array.*[1]);
//
// let ptr_to_array: *[int; 3] = &array; let slice: *[int] = &array[..];
// // e xpect: 2 // // e xpect: 2
// print_int(ptr_to_array.*[1]);
// let slice: *[int] = &array[..];
// e xpect: 2
// print_int(slice.*[2]); // print_int(slice.*[2]);
// let slice_1: *mut [int] = &mut array[1..3]; // let slice_mut: *mut [int] = &mut array[1..3];
// slice_1.*[0] = 4; // slice_mut.*[0] = 4;
// // e xpect: 4 // // e xpect: 4
// print_int(array[1]); // print_int(array[1]);
} }