Skip to content

Improve module method checks #283

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Mar 10, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
// * Note: `_pubFlds` is not used because in JS, fields are not declared
val clsParams = paramsOpt.fold(Nil)(_.paramSyms)
val ctorParams = clsParams.map(p => p -> scope.allocateName(p))
val ctorFields = ctorParams.filter: p =>
p._1.decl match
case S(Param(flags = FldFlags(value = true))) => true
case _ => false
val isModule = kind is syntax.Mod
val mtdPrefix = if isModule then "static " else ""
val privs =
Expand Down Expand Up @@ -237,9 +241,9 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
else doc""" # ${mtdPrefix}toString() { return "${sym.nme}${
if paramsOpt.isEmpty then doc"""""""
else doc"""(" + ${
ctorParams.headOption.fold("\"\"")("globalThis.Predef.render(this." + _._1.name + ")")
ctorFields.headOption.fold("\"\"")("globalThis.Predef.render(this." + _._1.name + ")")
}${
ctorParams.tailOption.fold("")(_.map(
ctorFields.tailOption.fold("")(_.map(
""" + ", " + globalThis.Predef.render(this.""" + _._1.name + ")").mkString)
} + ")""""
}; }"""
Expand Down
75 changes: 47 additions & 28 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ extends Importer:
Term.FunTy(term(lhs), term(rhs), N)
case InfixApp(lhs, Keyword.`=>`, rhs) =>
ctx.nest(N).givenIn:
val (syms, nestCtx) = params(lhs)
val (syms, nestCtx) = params(lhs, false)
Term.Lam(syms, term(rhs)(using nestCtx))
case InfixApp(lhs, Keyword.`:`, rhs) =>
Term.Asc(term(lhs), term(rhs))
Expand Down Expand Up @@ -894,7 +894,7 @@ extends Importer:
// * Add parameters to context
var newCtx = newCtx1
val pss = td.paramLists.map: ps =>
val (res, newCtx2) = params(ps)(using newCtx)
val (res, newCtx2) = params(ps, false)(using newCtx)
newCtx = newCtx2
res
// * Elaborate signature
Expand Down Expand Up @@ -972,6 +972,9 @@ extends Importer:
res :: Nil
case N => Nil
newCtx ++= tps.map(tp => tp.sym.name -> tp.sym) // TODO: correct ++?
val isDataClass = annotations.exists:
case Annot.Modifier(Keyword.`data`) => true
case _ => false
val ps =
td.paramLists.match
case Nil => N
Expand All @@ -985,35 +988,49 @@ extends Importer:
.map: ps =>
val (res, newCtx2) =
given Ctx = newCtx
params(ps)
params(ps, isDataClass)
newCtx = newCtx2
res
def withFields(using Ctx)(fn: (Ctx) ?=> (Term.Blk, Ctx)): (Term.Blk, Ctx) =
val fields = ps.map: ps =>
ps.params.map: p =>
val fields: Opt[List[TermDefinition | LetDecl | DefineVar]] = ps.map: ps =>
ps.params.flatMap: p =>
// For class-like types, "desugar" the parameters into additional class fields.
val owner = td.symbol match
// Any MemberSymbol should be an InnerSymbol, except for TypeAliasSymbol,
// but type aliases should not call this function.
case s: InnerSymbol => S(s)
case _: TypeAliasSymbol => die
val fsym = BlockMemberSymbol(p.sym.nme, Nil)
val fdef = TermDefinition(
owner,
ImmutVal,
fsym,
Nil, N, p.sign,
S(Term.Ref(p.sym)(p.sym.id, 666)), // FIXME: 666 is a dummy value
FlowSymbol("‹class-param-res›"),
TermDefFlags.empty.copy(isModMember = k is Mod),
Nil
)
sym.defn = S(fdef)
fdef
val ctxWithFields = ctx.withMembers(
fields.fold(Nil)(_.map(f => f.sym.nme -> f.sym)),
ctx.outer
)

if p.flags.value || isDataClass then
val fsym = BlockMemberSymbol(p.sym.nme, Nil)
val fdef = TermDefinition(
owner,
ImmutVal,
fsym,
Nil, N, N,
S(Term.Ref(p.sym)(p.sym.id, 666)), // FIXME: 666 is a dummy value
FlowSymbol("‹class-param-res›"),
TermDefFlags.empty.copy(isModMember = k is Mod),
Nil
)
sym.defn = S(fdef)
fdef :: Nil
else
val psym = TermSymbol(LetBind, owner, p.sym.id)
val decl = LetDecl(psym, Nil)
val defn = DefineVar(psym, Term.Ref(p.sym)(p.sym.id, 666)) // FIXME: 666 is a dummy value
decl :: defn :: Nil

val ctxWithFields = ctx
.withMembers(
fields.fold(Nil)(_.collect:
case f: TermDefinition => f.sym.nme -> f.sym // class fields
),
ctx.outer
) ++ fields.fold(Nil)(_.collect:
case d: LetDecl => d.sym.nme -> d.sym // class params
)
val ctxWithLets = ctx
val (blk, c) = fn(using ctxWithFields)
val blkWithFields = fields.fold[Term.Blk](blk)(fs => blk.copy(stats = fs ::: blk.stats))
(blkWithFields, c)
Expand All @@ -1039,8 +1056,8 @@ extends Importer:
case S(tree) =>
val (patternParams, extractionParams) = ps match // Filter out pattern parameters.
case S(ParamList(_, params, _)) => params.partition:
case param @ Param(FldFlags(false, false, false, false, true), _, _) => true
case param @ Param(FldFlags(_, _, _, _, false), _, _) => false
case param @ Param(FldFlags(false, false, false, false, true, false), _, _) => true
case param @ Param(FldFlags(_, _, _, _, false, _), _, _) => false
case N => (Nil, Nil)
// TODO: Implement extraction parameters.
if extractionParams.nonEmpty then
Expand Down Expand Up @@ -1127,7 +1144,7 @@ extends Importer:
if ctx.outer.isDefined then TermSymbol(k, ctx.outer, id)
else VarSymbol(id)

def param(t: Tree, inUsing: Bool): Ctxl[Opt[Opt[Bool] -> Param]] =
def param(t: Tree, inUsing: Bool, inDataClass: Bool): Ctxl[Opt[Opt[Bool] -> Param]] =
def go(t: Tree, inUsing: Bool, flags: FldFlags): Ctxl[Opt[Opt[Bool] -> Param]] = t match
case TypeDef(Mod, inner, N, N) =>
val ps = go(inner, inUsing, flags.copy(mod = true))
Expand All @@ -1140,23 +1157,25 @@ extends Importer:
ps
case TypeDef(Pat, inner, N, N) =>
go(inner, inUsing, flags.copy(pat = true))
case TermDef(ImmutVal, inner, _) =>
go(inner, inUsing, flags.copy(value = true))
case _ =>
t.asParam(inUsing).map: (isSpd, p, t) =>
val sym = VarSymbol(p)
val sign = t.map(term(_))
val param = Param(flags, sym, sign)
sym.decl = S(param)
isSpd -> param
go(t, inUsing, FldFlags.empty)
go(t, inUsing, if inDataClass then FldFlags.empty.copy(value = true) else FldFlags.empty)


def params(t: Tree): Ctxl[(ParamList, Ctx)] = t match
def params(t: Tree, inDataClass: Bool): Ctxl[(ParamList, Ctx)] = t match
case Tup(ps) =>
def go(ps: Ls[Tree], acc: Ls[Param], ctx: Ctx, flags: ParamListFlags): (ParamList, Ctx) =
ps match
case Nil => (ParamList(flags, acc.reverse, N), ctx)
case hd :: tl =>
param(hd, flags.ctx)(using ctx) match
param(hd, flags.ctx, inDataClass)(using ctx) match
case S((isSpd, p)) =>
val isCtx = hd match
case Modified(Keyword.`using`, _, _) => true
Expand Down
7 changes: 4 additions & 3 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ sealed abstract class ClassLikeDef extends TypeLikeDef:
val body: ObjBody
val annotations: Ls[Annot]
def extraAnnotations: Ls[Annot] = annotations.filter:
case Annot.Modifier(Keyword.`declare` | Keyword.`abstract`) => false
case Annot.Modifier(Keyword.`declare` | Keyword.`abstract` | Keyword.`data`) => false
case _ => true


Expand Down Expand Up @@ -439,19 +439,20 @@ case class TypeDef(


// TODO Store optional source locations for the flags instead of booleans
final case class FldFlags(mut: Bool, spec: Bool, genGetter: Bool, mod: Bool, pat: Bool):
final case class FldFlags(mut: Bool, spec: Bool, genGetter: Bool, mod: Bool, pat: Bool, value: Bool):
def showDbg: Str =
val flags = Buffer.empty[String]
if mut then flags += "mut"
if spec then flags += "spec"
if genGetter then flags += "gen"
if mod then flags += "module"
if pat then flags += "pattern"
if value then flags += "val"
flags.mkString(" ")
override def toString: String = "‹" + showDbg + "›"

object FldFlags:
val empty: FldFlags = FldFlags(false, false, false, false, false)
val empty: FldFlags = FldFlags(false, false, false, false, false, false)
object benign:
// * Some flags like `mut` and `module` are "benign" in the sense that they don't affect code-gen
def unapply(flags: FldFlags): Bool =
Expand Down
3 changes: 2 additions & 1 deletion hkmc2/shared/src/main/scala/hkmc2/utils/utils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,14 @@ extension (t: Product)
if isModMember then flags += "modMember"
if isModMember then flags += "modTyped"
flags.mkString("(", ", ", ")")
case FldFlags(mut, spec, genGetter, mod, pat) =>
case FldFlags(mut, spec, genGetter, mod, pat, value) =>
val flags = Buffer.empty[String]
if mut then flags += "mut"
if spec then flags += "spec"
if genGetter then flags += "gen"
if mod then flags += "module"
if pat then flags += "pat"
if value then flags += "val"
flags.mkString("(", ", ", ")")
case ParamListFlags(ctx) =>
val flags = Buffer.empty[String]
Expand Down
4 changes: 2 additions & 2 deletions hkmc2/shared/src/test/mlscript-compile/Option.mls
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ open Predef

module Option with ...

class Some(value)
data class Some(value)
object None

fun isDefined(x) = if x is
Expand All @@ -17,6 +17,6 @@ fun isDefined(x) = if x is

fun test() = 2134 |> print

class Both(fst, snd)
data class Both(fst, snd)


37 changes: 19 additions & 18 deletions hkmc2/shared/src/test/mlscript-compile/Predef.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ Predef1 = class Predef {
}
static __resume(cur2, tail) {
return (value) => {
let scrut, cont1, scrut1, scrut2, scrut3, scrut4, scrut5, scrut6, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9;
let scrut, cont1, scrut1, scrut2, scrut3, scrut4, scrut5, scrut6, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10;
scrut = cur2.resumed;
if (scrut === true) {
throw globalThis.Error("Multiple resumption");
Expand All @@ -587,7 +587,7 @@ Predef1 = class Predef {
}
cur2.resumed = true;
cont1 = cur2.next;
tmp10: while (true) {
tmp11: while (true) {
if (cont1 instanceof Predef.__Cont.class) {
tmp1 = runtime.safeCall(cont1.resume(value));
value = tmp1;
Expand All @@ -598,56 +598,57 @@ Predef1 = class Predef {
if (scrut2 === true) {
scrut3 = value.tail.next !== null;
if (scrut3 === true) {
tmp2 = runtime.safeCall(globalThis.console.log(value.tail));
throw globalThis.Error("Internal Error: unexpected continuation");
} else {
tmp2 = runtime.Unit;
tmp3 = runtime.Unit;
}
} else {
tmp2 = runtime.Unit;
tmp3 = runtime.Unit;
}
tmp3 = tmp2;
tmp4 = tmp3;
} else {
tmp3 = runtime.Unit;
tmp4 = runtime.Unit;
}
scrut4 = value.tail.next === null;
if (scrut4 === true) {
value.tail.next = cont1.next;
tmp4 = runtime.Unit;
tmp5 = runtime.Unit;
} else {
tmp4 = runtime.Unit;
tmp5 = runtime.Unit;
}
value.tail = tail;
scrut5 = cur2.handleBlockList.next !== null;
if (scrut5 === true) {
value.handleBlockList.tail.next = cur2.handleBlockList.next;
value.handleBlockList.tail = cur2.handleBlockList.tail;
tmp5 = runtime.Unit;
tmp6 = runtime.Unit;
} else {
tmp5 = runtime.Unit;
tmp6 = runtime.Unit;
}
return value
} else {
cont1 = cont1.next;
tmp6 = runtime.Unit;
tmp7 = runtime.Unit;
}
tmp7 = tmp6;
continue tmp10;
tmp8 = tmp7;
continue tmp11;
} else {
tmp7 = runtime.Unit;
tmp8 = runtime.Unit;
}
break;
}
scrut6 = cur2.handleBlockList.next === null;
if (scrut6 === true) {
return value
} else {
tmp8 = Predef.__resumeHandleBlocks(cur2.handleBlockList.next, cur2.handleBlockList.tail, value);
cur2 = tmp8;
tmp9 = Predef.__resumeHandleBlocks(cur2.handleBlockList.next, cur2.handleBlockList.tail, value);
cur2 = tmp9;
if (cur2 instanceof Predef.__EffectSig.class) {
cur2.tail = tail;
tmp9 = runtime.Unit;
tmp10 = runtime.Unit;
} else {
tmp9 = runtime.Unit;
tmp10 = runtime.Unit;
}
return cur2
}
Expand Down
19 changes: 10 additions & 9 deletions hkmc2/shared/src/test/mlscript-compile/Predef.mls
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ fun stringGet(string, i) = string.at(i)
fun stringDrop(string, n) = string.slice(n)


class MatchResult(captures)
class MatchFailure(errors)
data class MatchResult(captures)
data class MatchFailure(errors)

fun unreachable = throw Error("unreachable")

Expand Down Expand Up @@ -155,18 +155,18 @@ module TraceLogger with
else ()


class Test with
data class Test with
print("Test")
val y = 1

// Private definitions for algebraic effects

abstract class __Cont(next) with
abstract class __Cont(val next) with
fun resume(value)

class __TailList(next)
data class __TailList(next)

class __ListWithTail(next, tail) with
data class __ListWithTail(next, tail) with
fun append(elem) =
set tail.next = elem
set tail = elem
Expand All @@ -175,10 +175,10 @@ fun __mkListWithTail() =
set res.tail = res
res

class __HandleBlock(contHead, lastHandlerCont, next, handler)
data class __HandleBlock(contHead, lastHandlerCont, next, handler)

class __EffectSig(next, tail, handleBlockList, resumed, handler, handlerFun)
class __Return(value)
data class __EffectSig(next, tail, handleBlockList, resumed, handler, handlerFun)
data class __Return(value)

fun __appendInCont(eff, cont) =
if eff.tail is __TailList then
Expand Down Expand Up @@ -267,6 +267,7 @@ fun __resume(cur, tail)(value) =
if value is __EffectSig then
if value.tail.next !== cont do
if cont.next !== null and value.tail.next !== null do
console.log(value.tail)
throw Error("Internal Error: unexpected continuation")
// in tail call optimization, the continuation is not appended, we append it here
if value.tail.next === null do
Expand Down
2 changes: 1 addition & 1 deletion hkmc2/shared/src/test/mlscript-compile/Stack.mls
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ open Predef

module Stack[T] with ...

class (::) Cons[A](head: A, tail: Stack[A])
data class (::) Cons[A](head: A, tail: Stack[A])
object Nil

fun isEmpty(xs) = xs is Nil
Expand Down
Loading