Skip to content

Commit 5850017

Browse files
authored
Merge branch 'hkmc2' into adt💬
2 parents 75c2afd + f151d58 commit 5850017

File tree

149 files changed

+5313
-3058
lines changed

Some content is hidden

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

149 files changed

+5313
-3058
lines changed

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

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -402,19 +402,24 @@ class BBTyper(using elState: Elaborator.State, tl: TL):
402402
val nestCtx1 = ctx.nest
403403
val nestCtx2 = ctx.nest
404404
val patTy = pattern match
405-
case Pattern.ClassLike(sym, _, _, _) =>
406-
val (clsTy, tv, emptyTy) = sym.asCls.flatMap(_.defn) match
407-
case S(cls) =>
408-
(ClassLikeType(sym, cls.tparams.map(_ => freshWildcard(sym))), (freshVar(new TempSymbol(S(scrutinee), "scrut"))), ClassLikeType(sym, cls.tparams.map(_ => Wildcard.empty)))
409-
case _ =>
410-
error(msg"Cannot match ${scrutinee.toString} as ${sym.toString}" -> split.toLoc :: Nil)
411-
(Bot, Bot, Bot)
412-
scrutinee match // * refine
413-
case Ref(sym: LocalSymbol) =>
414-
nestCtx1 += sym -> clsTy
415-
nestCtx2 += sym -> tv
416-
case _ => () // TODO: refine all variables holding this value?
417-
clsTy | (tv & Type.mkNegType(emptyTy))
405+
case pat: Pattern.ClassLike =>
406+
pat.constructor.symbol.flatMap(_.asCls) match
407+
case S(sym) =>
408+
val (clsTy, tv, emptyTy) = sym.defn.map(sym -> _) match
409+
case S((sym, cls)) =>
410+
(ClassLikeType(sym, cls.tparams.map(_ => freshWildcard(sym))), (freshVar(new TempSymbol(S(scrutinee), "scrut"))), ClassLikeType(sym, cls.tparams.map(_ => Wildcard.empty)))
411+
case _ =>
412+
error(msg"Cannot match ${scrutinee.toString} as ${sym.toString}" -> split.toLoc :: Nil)
413+
(Bot, Bot, Bot)
414+
scrutinee match // * refine
415+
case Ref(sym: LocalSymbol) =>
416+
nestCtx1 += sym -> clsTy
417+
nestCtx2 += sym -> tv
418+
case _ => () // TODO: refine all variables holding this value?
419+
clsTy | (tv & Type.mkNegType(emptyTy))
420+
case N =>
421+
error(msg"Not a valid class: ${pat.constructor.describe}" -> pat.constructor.toLoc :: Nil)
422+
Bot
418423
case Pattern.Lit(lit) => lit match
419424
case _: Tree.BoolLit => BbCtx.boolTy
420425
case _: Tree.IntLit => BbCtx.intTy
@@ -465,7 +470,7 @@ class BBTyper(using elState: Elaborator.State, tl: TL):
465470
(lhs, rhs) match
466471
case (Term.Lam(PlainParamList(params), body), ft @ PolyFunType(args, ret, eff)) => // * annoted functions
467472
if params.length != args.length then
468-
(error(msg"Cannot type function ${lhs.toString} as ${rhs.show}" -> lhs.toLoc :: Nil), Bot)
473+
(error(msg"Cannot type function ${lhs.toString} as ${rhs.show}" -> lhs.toLoc :: Nil), Bot)
469474
else
470475
val nestCtx = ctx.nest
471476
val argsTy = params.zip(args).map:

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,16 +393,22 @@ enum Case:
393393
case Lit(lit: Literal)
394394
case Cls(cls: ClassLikeSymbol, path: Path)
395395
case Tup(len: Int, inf: Bool)
396+
/** checks field existence
397+
* @param safe true will omit the instanceof Object check
398+
*/
399+
case Field(name: Tree.Ident, safe: Bool)
396400

397401
lazy val freeVars: Set[Local] = this match
398402
case Lit(_) => Set.empty
399403
case Cls(_, path) => path.freeVars
400404
case Tup(_, _) => Set.empty
405+
case Field(_, _) => Set.empty
401406

402407
lazy val freeVarsLLIR: Set[Local] = this match
403408
case Lit(_) => Set.empty
404409
case Cls(_, path) => path.freeVarsLLIR
405410
case Tup(_, _) => Set.empty
411+
case Field(_, _) => Set.empty
406412

407413
sealed trait TrivialResult extends Result
408414

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ class BlockTransformer(subst: SymbolSubst):
202202
val path2 = applyPath(path)
203203
if (cls2 is cls) && (path2 is path) then cse else Case.Cls(cls2, path2)
204204
case Case.Tup(len, inf) => cse
205+
case Case.Field(name, safe) => cse
205206

206207
def applyHandler(hdr: Handler): Handler =
207208
val sym2 = hdr.sym.subst

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

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
220220
args(fs)(args => k(Value.Arr(args)))
221221
case ref @ st.Ref(sym) =>
222222
sym match
223-
case ctx.builtins.source.bms | ctx.builtins.js.bms | ctx.builtins.debug.bms =>
223+
case ctx.builtins.source.bms | ctx.builtins.js.bms | ctx.builtins.debug.bms | ctx.builtins.annotations.bms =>
224224
raise:
225225
ErrorReport(
226226
msg"Module '${sym.nme}' is virtual (i.e., \"compiler fiction\"); cannot be used directly" -> t.toLoc ::
@@ -466,30 +466,48 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
466466
)
467467
pat match
468468
case Pattern.Lit(lit) => mkMatch(Case.Lit(lit) -> go(tail, topLevel = false))
469-
case Pattern.ClassLike(cls: ClassSymbol, _trm, _args0, _refined)
470-
// Do not elaborate `_trm` when the `cls` is virtual.
471-
if Elaborator.ctx.builtins.virtualClasses contains cls =>
472-
// [invariant:0] Some classes (e.g., `Int`) from `Prelude` do
473-
// not exist at runtime. If we do lowering on `trm`, backends
474-
// (e.g., `JSBuilder`) will not be able to handle the corresponding selections.
475-
// In this case the second parameter of `Case.Cls` will not be used.
476-
// So we make it `Predef.unreachable` here.
477-
mkMatch(Case.Cls(cls, unreachableFn) -> go(tail, topLevel = false))
478-
case Pattern.ClassLike(cls, trm, args0, _refined) =>
479-
subTerm_nonTail(trm): st =>
480-
val args = args0.getOrElse(Nil)
481-
val clsParams = cls match
482-
case cls: ClassSymbol => cls.tree.clsParams
483-
case _: ModuleSymbol => Nil
484-
assert(args0.isEmpty || clsParams.length === args.length)
469+
case Pattern.ClassLike(ctor, argsOpt, _mode, _refined) =>
470+
/** Make a continuation that creates the match. */
471+
def k(ctorSym: ClassLikeSymbol, clsParams: Ls[TermSymbol])(st: Path): Block =
472+
val args = argsOpt.map(_.map(_.scrutinee)).getOrElse(Nil)
473+
// Normalization should reject cases where the user provides
474+
// more sub-patterns than there are actual class parameters.
475+
assert(argsOpt.isEmpty || args.length <= clsParams.length)
485476
def mkArgs(args: Ls[TermSymbol -> BlockLocalSymbol])(using Subst): Case -> Block = args match
486477
case Nil =>
487-
Case.Cls(cls, st) -> go(tail, topLevel = false)
478+
Case.Cls(ctorSym, st) -> go(tail, topLevel = false)
488479
case (param, arg) :: args =>
489480
val (cse, blk) = mkArgs(args)
490481
(cse, Assign(arg, Select(sr, param.id/*FIXME incorrect Ident?*/)(S(param)), blk))
491-
mkMatch(mkArgs(clsParams.iterator.zip(args).collect { case (s1, S(s2)) => (s1, s2) }.toList))
482+
mkMatch(mkArgs(clsParams.iterator.zip(args).toList))
483+
ctor.symbol.flatMap(_.asClsOrMod) match
484+
case S(cls: ClassSymbol) if ctx.builtins.virtualClasses contains cls =>
485+
// [invariant:0] Some classes (e.g., `Int`) from `Prelude` do
486+
// not exist at runtime. If we do lowering on `trm`, backends
487+
// (e.g., `JSBuilder`) will not be able to handle the corresponding selections.
488+
// In this case the second parameter of `Case.Cls` will not be used.
489+
// So we do not elaborate `ctor` when the `cls` is virtual
490+
// and use it `Predef.unreachable` here.
491+
k(cls, Nil)(unreachableFn)
492+
case S(cls: ClassSymbol) => subTerm_nonTail(ctor)(k(cls, cls.tree.clsParams))
493+
case S(mod: ModuleSymbol) => subTerm_nonTail(ctor)(k(mod, Nil))
494+
case N =>
495+
// Normalization have already checked the constructor
496+
// resolves to a class or module. Branches with unresolved
497+
// constructors should have been removed.
498+
lastWords("Pattern.ClassLike: constructor is neither a class nor a module")
492499
case Pattern.Tuple(len, inf) => mkMatch(Case.Tup(len, inf) -> go(tail, topLevel = false))
500+
case Pattern.Record(entries) =>
501+
val objectSym = ctx.builtins.Object
502+
mkMatch( // checking that we have an object
503+
Case.Cls(objectSym, Value.Ref(BuiltinSymbol(objectSym.nme, false, false, true, false))),
504+
entries.foldRight(go(tail, topLevel = false)):
505+
case ((fieldName, fieldSymbol), blk) =>
506+
mkMatch(
507+
Case.Field(fieldName, safe = true), // we know we have an object, no need to check again
508+
Assign(fieldSymbol, Select(sr, fieldName)(N), blk)
509+
)
510+
)
493511
case Split.Else(els) =>
494512
if k.isInstanceOf[TailOp] && isIf then term_nonTail(els)(k)
495513
else
@@ -502,11 +520,17 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
502520
Throw(Instantiate(Select(Value.Ref(State.globalThisSymbol), Tree.Ident("Error"))(N),
503521
Value.Lit(syntax.Tree.StrLit("match error")) :: Nil)) // TODO add failed-match scrutinee info
504522

505-
if k.isInstanceOf[TailOp] && isIf then go(iftrm.normalized, topLevel = true)
523+
val normalize = ucs.Normalization()
524+
val normalized = tl.scoped("ucs:normalize"):
525+
normalize(iftrm.desugared)
526+
tl.scoped("ucs:normalized"):
527+
tl.log(s"Normalized:\n${Split.display(normalized)}")
528+
529+
if k.isInstanceOf[TailOp] && isIf then go(normalized, topLevel = true)
506530
else
507531
val body = if isWhile
508-
then Label(lbl, go(iftrm.normalized, topLevel = true), End())
509-
else go(iftrm.normalized, topLevel = true)
532+
then Label(lbl, go(normalized, topLevel = true), End())
533+
else go(normalized, topLevel = true)
510534
Begin(
511535
body,
512536
if usesResTmp then k(Value.Ref(l))

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ object Printer:
1919
// ts.trmTree
2020
case ts: semantics.InnerSymbol => ts.nme
2121
case ts: semantics.BuiltinSymbol => ts.nme
22-
case _ => summon[Scope].lookup_!(l)
22+
case _ => summon[Scope].lookup(l) match
23+
case S(str) => str
24+
case N => s"‹not in scope: ${l}"
2325

2426
def mkDocument(blk: Block)(using Raise, Scope): Document = blk match
2527
case Match(scrut, arms, dflt, rest) =>
@@ -95,6 +97,10 @@ object Printer:
9597
case Value.Arr(elems) =>
9698
val docElems = elems.map(x => mkDocument(x)).mkString(", ")
9799
doc"[${docElems}]"
100+
case Value.Rcd(args) =>
101+
doc"{ ${
102+
args.map(x => x.idx.fold(doc"...")(p => mkDocument(p) :: ": ") :: mkDocument(x.value)).mkString(", ")
103+
} }"
98104

99105
def mkDocument(path: Path)(using Raise, Scope): Document = path match
100106
case Select(qual, name) =>

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,10 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
375375
case Elaborator.ctx.builtins.Int => doc"globalThis.Number.isInteger($sd)"
376376
case _ => doc"$sd instanceof ${result(pth)}"
377377
case Case.Tup(len, inf) => doc"globalThis.Array.isArray($sd) && $sd.length ${if inf then ">=" else "==="} ${len}"
378+
case Case.Field(n, safe = false) =>
379+
doc"""typeof $sd === "object" && $sd !== null && "${n.name}" in $sd"""
380+
case Case.Field(n, safe = true) =>
381+
doc""""${n.name}" in $sd"""
378382
val h = doc" # if (${ cond(hd._1) }) ${ braced(returningTerm(hd._2, endSemi = false)) }"
379383
val t = tl.foldLeft(h)((acc, arm) =>
380384
acc :: doc" else if (${ cond(arm._1) }) ${ braced(returningTerm(arm._2, endSemi = false)) }")

0 commit comments

Comments
 (0)