Skip to content

Commit ee836f9

Browse files
committed
Add preliminary support for inheritance and refined new
1 parent fb4b043 commit ee836f9

File tree

89 files changed

+851
-573
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+851
-573
lines changed

hkmc2/shared/src/main/scala/hkmc2/bbml/bbML.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ class BBTyper(using elState: Elaborator.State, tl: TL):
502502
case t @ Term.App(lhs, Term.Tup(rhs)) =>
503503
val (funTy, lhsEff) = typeCheck(lhs)
504504
app((funTy, lhsEff), rhs, t)
505-
case Term.New(cls, args) =>
505+
case Term.New(cls, args, N) =>
506506
cls.symbol.flatMap(_.asCls.flatMap(_.defn)) match
507507
case S(clsDfn: ClassDef.Parameterized) =>
508508
require(clsDfn.paramsOpt.forall(_.restParam.isEmpty))

hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ final case class ClsLikeDefn(
231231
preCtor: Block,
232232
ctor: Block,
233233
) extends Defn:
234+
publicFields.foreach: f =>
235+
require(f.owner.contains(isym))
234236
val innerSym = S(isym)
235237

236238
final case class Handler(

hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx):
417417

418418
private def genContClass(b: Block)(using HandlerCtx): Opt[ClsLikeDefn] =
419419
val clsSym = ClassSymbol(
420-
Tree.TypeDef(syntax.Cls, Tree.Error(), N, N),
420+
Tree.DummyTypeDef(syntax.Cls),
421421
Tree.Ident("Cont$")
422422
)
423423

hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala

Lines changed: 71 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ class Lowering(lowerHandlers: Bool, stackLimit: Option[Int])(using TL, Raise, St
164164
msg"Unexpected arguments for builtin symbol '${sym.nme}'" -> arg.toLoc :: Nil, S(arg),
165165
source = Diagnostic.Source.Compilation)
166166
End("error")
167+
case st.TyApp(f, ts) => term(f)(k) // * Type arguments are erased
167168
case st.App(f, arg) =>
168169
val isMlsFun = f.symbol.fold(f.isInstanceOf[st.Lam]):
169170
case _: sem.BuiltinSymbol => true
@@ -200,7 +201,7 @@ class Lowering(lowerHandlers: Bool, stackLimit: Option[Int])(using TL, Raise, St
200201
conclude(Select(p, nme)(sel.sym))
201202
case _ => subTerm(f)(conclude)
202203

203-
case st.Blk((h @ Handle(lhs, rhs, args, cls, defs)) :: stmts, res) =>
204+
case st.Blk((h @ Handle(lhs, rhs, as, cls, defs)) :: stmts, res) =>
204205
if !lowerHandlers then
205206
raise(ErrorReport(
206207
msg"Effect handlers are not enabled" ->
@@ -218,14 +219,10 @@ class Lowering(lowerHandlers: Bool, stackLimit: Option[Int])(using TL, Raise, St
218219
}.collect{ case Some(v) => v }
219220
val resSym = TempSymbol(S(t))
220221
subTerm(rhs): par =>
221-
def rec(as: Ls[st], asr: Ls[Path]): Block = as match
222-
case Nil => HandleBlock(lhs, resSym, par, asr.reverse, cls, handlers,
222+
subTerms(as): asr =>
223+
HandleBlock(lhs, resSym, par, asr, cls, handlers,
223224
term_nonTail(st.Blk(stmts, res))(HandleBlockReturn(_)),
224225
k(Value.Ref(resSym)))
225-
case a :: as =>
226-
subTerm_nonTail(a): ar =>
227-
rec(as, ar :: asr)
228-
rec(args, Nil)
229226

230227
case st.Blk(Nil, res) => term(res)(k)
231228
case st.Blk((t: sem.Term) :: stats, res) =>
@@ -255,33 +252,25 @@ class Lowering(lowerHandlers: Bool, stackLimit: Option[Int])(using TL, Raise, St
255252
term(st.Blk(stats, res))(k)
256253
case cls: ClassLikeDef =>
257254
reportAnnotations(cls, cls.extraAnnotations)
258-
val bodBlk = cls.body.blk
259-
val (mtds, rest1) = bodBlk.stats.partitionMap:
260-
case td: TermDefinition if td.k is syntax.Fun => L(td)
261-
case s => R(s)
262-
val (privateFlds, rest2) = rest1.partitionMap:
263-
case decl @ LetDecl(sym: TermSymbol, annotations) =>
264-
reportAnnotations(decl, annotations)
265-
L(sym)
266-
case s => R(s)
267-
val publicFlds = rest2.collect:
268-
case td @ TermDefinition(k = (_: syntax.Val)) => td
269-
Define(ClsLikeDefn(cls.owner, cls.sym, cls.bsym, cls.kind, cls.paramsOpt, N,
270-
mtds.flatMap: td =>
271-
td.body.map: bod =>
272-
val (paramLists, bodyBlock) = setupFunctionDef(td.params, bod, S(td.sym.nme))
273-
FunDefn(td.owner, td.sym, paramLists, bodyBlock)
274-
,
275-
privateFlds,
276-
publicFlds,
277-
End(),
278-
term_nonTail(Blk(rest2, bodBlk.res))(ImplctRet)
279-
// * This is just a minor improvement to get `constructor() {}` instead of `constructor() { null }`
280-
.mapTail:
281-
case Return(Value.Lit(syntax.Tree.UnitLit(true)), true) => End()
282-
case t => t
283-
),
284-
term_nonTail(st.Blk(stats, res))(k))
255+
val (mtds, publicFlds, privateFlds, ctor) = gatherMembers(cls.body)
256+
cls.ext match
257+
case N =>
258+
Define(
259+
ClsLikeDefn(cls.owner, cls.sym, cls.bsym, cls.kind, cls.paramsOpt, N,
260+
mtds, privateFlds, publicFlds, End(), ctor),
261+
term_nonTail(st.Blk(stats, res))(k)
262+
)
263+
case S(ext) =>
264+
assert(k isnt syntax.Mod) // modules can't extend things and can't have super calls
265+
subTerm(ext.cls): clsp =>
266+
val pctor = // TODO dedup with `New` case
267+
args(ext.args): args =>
268+
Return(Call(Value.Ref(State.builtinOpsMap("super")), args)(true, true), implct = true)
269+
Define(
270+
ClsLikeDefn(cls.owner, cls.sym, cls.bsym, cls.kind, cls.paramsOpt, S(clsp),
271+
mtds, privateFlds, publicFlds, pctor, ctor),
272+
term_nonTail(st.Blk(stats, res))(k)
273+
)
285274
case _ =>
286275
// TODO handle
287276
term(st.Blk(stats, res))(k)
@@ -463,15 +452,22 @@ class Lowering(lowerHandlers: Bool, stackLimit: Option[Int])(using TL, Raise, St
463452
subTerm_nonTail(fld): f =>
464453
k(DynSelect(p, f, ai))
465454

466-
467-
case New(cls, as) =>
455+
456+
case New(cls, as, N) =>
468457
subTerm(cls): sr =>
469-
def rec(as: Ls[st], asr: Ls[Path]): Block = as match
470-
case Nil => k(Instantiate(sr, asr.reverse))
471-
case a :: as =>
472-
subTerm_nonTail(a): ar =>
473-
rec(as, ar :: asr)
474-
rec(as, Nil)
458+
subTerms(as): asr =>
459+
k(Instantiate(sr, asr))
460+
461+
case New(cls, as, S((isym, rft))) =>
462+
subTerm(cls): clsp =>
463+
val sym = new BlockMemberSymbol(isym.name, Nil)
464+
val (mtds, publicFlds, privateFlds, ctor) = gatherMembers(rft)
465+
val pctor =
466+
args(as): args =>
467+
Return(Call(Value.Ref(State.builtinOpsMap("super")), args)(true, true), implct = true)
468+
val clsDef = ClsLikeDefn(N, isym, sym, syntax.Cls, N, S(clsp),
469+
mtds, privateFlds, publicFlds, pctor, ctor)
470+
Define(clsDef, term_nonTail(New(sym.ref(), Nil, N))(k))
475471

476472
case Try(sub, finallyDo) =>
477473
val l = new TempSymbol(S(sub))
@@ -488,12 +484,8 @@ class Lowering(lowerHandlers: Bool, stackLimit: Option[Int])(using TL, Raise, St
488484
Assign(reg, Instantiate(Select(Value.Ref(State.globalThisSymbol), Tree.Ident("Region"))(N), Nil),
489485
term_nonTail(body)(k))
490486
case RegRef(reg, value) =>
491-
def rec(as: Ls[st], asr: Ls[Path]): Block = as match
492-
case Nil => k(Instantiate(Select(Value.Ref(State.globalThisSymbol), Tree.Ident("Ref"))(N), asr.reverse))
493-
case a :: as =>
494-
subTerm_nonTail(a): ar =>
495-
rec(as, ar :: asr)
496-
rec(reg :: value :: Nil, Nil)
487+
subTerms(reg :: value :: Nil): args =>
488+
k(Instantiate(Select(Value.Ref(State.globalThisSymbol), Tree.Ident("Ref"))(N), args))
497489
case Deref(ref) =>
498490
subTerm(ref): r =>
499491
k(Select(r, Tree.Ident("value"))(N))
@@ -520,6 +512,36 @@ class Lowering(lowerHandlers: Bool, stackLimit: Option[Int])(using TL, Raise, St
520512
// case _ =>
521513
// subTerm(t)(k)
522514

515+
def gatherMembers(clsBody: ObjBody)(using Subst): (Ls[FunDefn], Ls[TermDefinition], Ls[TermSymbol], Block) =
516+
val mtds = clsBody.methods
517+
.flatMap: td =>
518+
td.body.map: bod =>
519+
val (paramLists, bodyBlock) = setupFunctionDef(td.params, bod, S(td.sym.nme))
520+
FunDefn(td.owner, td.sym, paramLists, bodyBlock)
521+
val publicFlds = clsBody.publicFlds
522+
val privateFlds = clsBody.nonMethods.collect:
523+
case decl @ LetDecl(sym: TermSymbol, annotations) =>
524+
reportAnnotations(decl, annotations)
525+
sym
526+
val ctor =
527+
term_nonTail(Blk(clsBody.nonMethods, clsBody.blk.res))(ImplctRet)
528+
// * This is just a minor improvement to get `constructor() {}` instead of `constructor() { null }`
529+
.mapTail:
530+
case Return(Value.Lit(syntax.Tree.UnitLit(true)), true) => End()
531+
case t => t
532+
(mtds, publicFlds, privateFlds, ctor)
533+
534+
inline def args(ts: Ls[st])(k: Ls[Arg] => Block)(using Subst): Block =
535+
subTerms(ts)(asr => k(asr.map(Arg(false, _))))
536+
inline def subTerms(ts: Ls[st])(k: Ls[Path] => Block)(using Subst): Block =
537+
// @tailrec // TODO
538+
def rec(as: Ls[st], asr: Ls[Path]): Block = as match
539+
case Nil => k(asr.reverse)
540+
case a :: as =>
541+
subTerm_nonTail(a): ar =>
542+
rec(as, ar :: asr)
543+
rec(ts, Nil)
544+
523545
def subTerm_nonTail(t: st, inStmtPos: Bool = false)(k: Path => Block)(using Subst): Block =
524546
subTerm(t: st, inStmtPos: Bool)(k)
525547

@@ -591,7 +613,7 @@ trait LoweringSelSanityChecks
591613
Pattern.Lit(syntax.Tree.UnitLit(false)),
592614
Split.Else(
593615
Term.Throw(Term.New(SynthSel(State.globalThisSymbol.ref(), Tree.Ident("Error"))(N),
594-
Term.Lit(syntax.Tree.StrLit(s"Access to required field '${nme.name}' yielded 'undefined'")) :: Nil)
616+
Term.Lit(syntax.Tree.StrLit(s"Access to required field '${nme.name}' yielded 'undefined'")) :: Nil, N)
595617
))),
596618
Split.Else(selRes.ref()))
597619
Assign(

hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,18 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
136136
case Value.Arr(es) if es.isEmpty => doc"[]"
137137
case Value.Arr(es) =>
138138
doc"[ #{ # ${es.map(argument).mkDocument(doc", # ")} #} # ]"
139-
def returningTerm(t: Block)(using Raise, Scope): Document = t match
139+
140+
def returningTerm(t: Block, endSemi: Bool)(using Raise, Scope): Document =
141+
def mkSemi = if endSemi then ";" else ""
142+
t match
140143
case _: (HandleBlockReturn | HandleBlock) =>
141144
errStmt(msg"This code requires effect handler instrumentation but was compiled without it.")
142145
case Assign(l, r, rst) =>
143-
doc" # ${getVar(l)} = ${result(r)};${returningTerm(rst)}"
146+
doc" # ${getVar(l)} = ${result(r)};${returningTerm(rst, endSemi)}"
144147
case AssignField(p, n, r, rst) =>
145-
doc" # ${result(p)}.${n.name} = ${result(r)};${returningTerm(rst)}"
148+
doc" # ${result(p)}.${n.name} = ${result(r)};${returningTerm(rst, endSemi)}"
146149
case AssignDynField(p, f, ai, r, rst) =>
147-
doc" # ${result(p)}[${result(f)}] = ${result(r)};${returningTerm(rst)}"
150+
doc" # ${result(p)}[${result(f)}] = ${result(r)};${returningTerm(rst, endSemi)}"
148151
case Define(defn, rst) =>
149152
def mkThis(sym: InnerSymbol): Document =
150153
result(Value.This(sym))
@@ -153,9 +156,9 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
153156
val sym = defn.sym
154157
own match
155158
case N =>
156-
doc"${getVar(sym)} = ${result(p)};${returningTerm(rst)}"
159+
doc"${getVar(sym)} = ${result(p)};${returningTerm(rst, endSemi)}"
157160
case S(owner) =>
158-
doc"${mkThis(owner)}.${sym.nme} = ${result(p)};${returningTerm(rst)}"
161+
doc"${mkThis(owner)}.${sym.nme} = ${result(p)};${returningTerm(rst, endSemi)}"
159162
case defn: (FunDefn | ClsLikeDefn) =>
160163
val outerScope = scope
161164
val (thisProxy, res) = scope.nestRebindThis(
@@ -183,16 +186,18 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
183186
val nme = scp.allocateName(fld)
184187
doc" # $mtdPrefix#$nme;"
185188
.mkDocument(doc"")
186-
val preCtorCode = ctorParams.foldLeft(body(preCtor)):
189+
val preCtorCode = ctorParams.foldLeft(body(preCtor, endSemi = true)):
187190
case (acc, (sym, nme)) =>
188191
doc"$acc # this.${sym.name} = $nme;"
189-
val ctorCode = doc"$preCtorCode${body(ctor)}"
192+
val ctorCode = doc"$preCtorCode${body(ctor, endSemi = false)}"
190193
val ctorOrStatic = if isModule
191194
then doc"static"
192195
else doc"constructor(${
193196
ctorParams.unzip._2.mkDocument(", ")
194197
})"
195-
val clsJS = doc"class ${scope.lookup_!(isym)}${par.map(p => s" extends ${result(p)}").getOrElse("")} { #{ ${
198+
val clsJS = doc"class ${scope.lookup_!(isym)}${
199+
par.map(p => s" extends ${result(p)}").getOrElse("")
200+
} { #{ ${
196201
privs
197202
} # $ctorOrStatic ${ braced(ctorCode) }${
198203
mtds.map:
@@ -203,7 +208,7 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
203208
val (params, bodyDoc) = setupFunction(some(td.sym.nme), ps, result)
204209
doc" # $mtdPrefix${td.sym.nme}($params) ${ braced(bodyDoc) }"
205210
case td @ FunDefn(_, _, Nil, bod) =>
206-
doc" # ${mtdPrefix}get ${td.sym.nme}() ${ braced(body(bod)) }"
211+
doc" # ${mtdPrefix}get ${td.sym.nme}() ${ braced(body(bod, endSemi = true)) }"
207212
.mkDocument(" ")
208213
}${
209214
if mtds.exists(_.sym.nme == "toString")
@@ -255,17 +260,17 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
255260
thisProxy match
256261
case S(proxy) if !scope.thisProxyDefined =>
257262
scope.thisProxyDefined = true
258-
doc"const $proxy = this; # $res${returningTerm(rst)}"
259-
case _ => doc"$res${returningTerm(rst)}"
263+
doc"const $proxy = this; # $res${returningTerm(rst, endSemi)}"
264+
case _ => doc"$res${returningTerm(rst, endSemi = false)}"
260265
doc" # $resJS"
261-
case Return(res, true) => doc" # ${result(res)}"
262-
case Return(res, false) => doc" # return ${result(res)};"
266+
case Return(res, true) => doc" # ${result(res)}${mkSemi}"
267+
case Return(res, false) => doc" # return ${result(res)}${mkSemi}"
263268

264269
case Match(scrut, Nil, els, rest) =>
265270
val e = els match
266-
case S(el) => returningTerm(el)
271+
case S(el) => returningTerm(el, endSemi = true)
267272
case N => doc""
268-
e :: returningTerm(rest)
273+
e :: returningTerm(rest, endSemi)
269274
case Match(scrut, hd :: tl, els, rest) =>
270275
val sd = result(scrut)
271276
def cond(cse: Case) = cse match
@@ -280,16 +285,17 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
280285
case Elaborator.ctx.Builtins.Int => doc"globalThis.Number.isInteger($sd)"
281286
case _ => doc"$sd instanceof ${result(pth)}"
282287
case Case.Tup(len, inf) => doc"globalThis.Array.isArray($sd) && $sd.length ${if inf then ">=" else "==="} ${len}"
283-
val h = doc" # if (${ cond(hd._1) }) ${ braced(returningTerm(hd._2)) }"
284-
val t = tl.foldLeft(h)((acc, arm) => acc :: doc" else if (${ cond(arm._1) }) ${ braced(returningTerm(arm._2)) }")
288+
val h = doc" # if (${ cond(hd._1) }) ${ braced(returningTerm(hd._2, endSemi = false)) }"
289+
val t = tl.foldLeft(h)((acc, arm) =>
290+
acc :: doc" else if (${ cond(arm._1) }) ${ braced(returningTerm(arm._2, endSemi = false)) }")
285291
val e = els match
286292
case S(el) =>
287-
doc" else ${ braced(returningTerm(el)) }"
293+
doc" else ${ braced(returningTerm(el, endSemi = false)) }"
288294
case N => doc""
289-
t :: e :: returningTerm(rest)
295+
t :: e :: returningTerm(rest, endSemi)
290296

291297
case Begin(sub, thn) =>
292-
doc"${returningTerm(sub)}${returningTerm(thn)}"
298+
doc"${returningTerm(sub, endSemi = true)}${returningTerm(thn, endSemi)}"
293299

294300
case End("") => doc""
295301
case End(msg) =>
@@ -307,12 +313,14 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
307313
case Label(lbl, bod, rst) =>
308314
scope.allocateName(lbl)
309315
doc" # ${getVar(lbl)}: while (true) { #{ ${
310-
returningTerm(bod)
311-
} # break; #} # }${returningTerm(rst)}"
316+
returningTerm(bod, endSemi = false)
317+
} # break; #} # }${returningTerm(rst, endSemi)}"
312318

313319
case TryBlock(sub, fin, rst) =>
314-
doc" # try ${ braced(returningTerm(sub)) } finally ${ braced(returningTerm(fin)) } # ${
315-
returningTerm(rst).stripBreaks}"
320+
doc" # try ${ braced(returningTerm(sub, endSemi = false)) } finally ${
321+
braced(returningTerm(fin, endSemi = false))
322+
} # ${
323+
returningTerm(rst, endSemi).stripBreaks}"
316324

317325
// case _ => ???
318326

@@ -361,7 +369,7 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
361369
then "./" + os.Path(path).relativeTo(wd).toString
362370
else path
363371
doc"""import ${getVar(i._1)} from "${relPath}";"""
364-
imps.mkDocument(doc" # ") :/: block(p.main).stripBreaks :: (
372+
imps.mkDocument(doc" # ") :/: block(p.main, endSemi = false).stripBreaks :: (
365373
exprt match
366374
case S(sym) => doc"\nlet ${sym.nme} = ${scope.lookup_!(sym)}; export default ${sym.nme};\n"
367375
case N => doc""
@@ -375,7 +383,7 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
375383
val v = doc"this.${getVar(i._1)}"
376384
doc"""$v = await import("${i._2.toString
377385
}"); # if ($v.default !== undefined) $v = $v.default;"""
378-
blockPreamble(p.main) -> (imps.mkDocument(doc" # ") :/: returningTerm(p.main).stripBreaks)
386+
blockPreamble(p.main) -> (imps.mkDocument(doc" # ") :/: returningTerm(p.main, endSemi = false).stripBreaks)
379387

380388
def blockPreamble(t: Block)(using Raise, Scope): Document =
381389
// TODO document: mutable var assnts require the lookup
@@ -387,11 +395,11 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
387395
.toList.mkDocument(", ")
388396
:: doc";"
389397

390-
def block(t: Block)(using Raise, Scope): Document =
391-
blockPreamble(t) :: returningTerm(t)
398+
def block(t: Block, endSemi: Bool)(using Raise, Scope): Document =
399+
blockPreamble(t) :: returningTerm(t, endSemi)
392400

393-
def body(t: Block)(using Raise, Scope): Document = scope.nest givenIn:
394-
block(t)
401+
def body(t: Block, endSemi: Bool)(using Raise, Scope): Document = scope.nest givenIn:
402+
block(t, endSemi)
395403

396404
def braced(t: Document)(using Raise, Scope): Document =
397405
if t.isEmpty then
@@ -404,7 +412,7 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
404412
val paramsList = params.params.map(p => scope.allocateName(p.sym))
405413
.++(params.restParam.map(p => "..." + scope.allocateName(p.sym)))
406414
.mkDocument(", ")
407-
(paramsList, this.body(body))
415+
(paramsList, this.body(body, endSemi = false))
408416

409417

410418

@@ -523,7 +531,7 @@ trait JSBuilderArgNumSanityChecks
523531
val restAssign = paramRest match
524532
case N => doc""
525533
case S(p) => doc"\nlet $p = globalThis.Predef.tupleSlice($paramsStr, ${params.paramCountLB}, 0);"
526-
(doc"...$paramsStr", doc"$checkArgsNum$paramsAssign$restAssign${this.body(body)}")
534+
(doc"...$paramsStr", doc"$checkArgsNum$paramsAssign$restAssign${this.body(body, endSemi = false)}")
527535
else
528536
super.setupFunction(name, params, body)
529537

0 commit comments

Comments
 (0)