Skip to content
Draft
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
b5bdd5b
Basic adt typing and fix subst pol
NeilKleistGao May 20, 2025
e8d47a0
Support type parameters
NeilKleistGao May 21, 2025
37d6f63
Distinguish generic ctors from normal ones
NeilKleistGao May 22, 2025
921a079
WIP: Add pattern matching
NeilKleistGao May 22, 2025
75c2afd
Add docs
NeilKleistGao May 23, 2025
5850017
Merge branch 'hkmc2' into adt💬
LPTK May 27, 2025
929a49a
Deal with merge conflicts
chengluyu May 27, 2025
056e138
WIP from meeting
LPTK May 28, 2025
0c534f1
Add proper Unit type
LPTK May 28, 2025
a36c6d2
Elaborate example a bit
LPTK May 28, 2025
973afbc
Reject non-variable pattern args for now
NeilKleistGao May 28, 2025
8b259cf
WIP: Fix
NeilKleistGao May 28, 2025
37a60a3
WIP: Rerun test
NeilKleistGao May 29, 2025
0194f4d
Use mutable set for cache and have more test cases
NeilKleistGao May 29, 2025
7b77eef
Add more spaces for Paper example
NeilKleistGao May 29, 2025
8999fec
Add example test file
LPTK May 30, 2025
8febb63
Changes from meeting
LPTK May 30, 2025
36560d2
Fix missing cases
NeilKleistGao May 30, 2025
27038a7
W
LPTK May 30, 2025
94bf051
W
LPTK May 30, 2025
d0915ba
Merge remote-tracking branch 'NeilKleistGao/adt💬' into adt💬
LPTK May 30, 2025
096f282
Add pmsort
NeilKleistGao May 30, 2025
84f4712
Merge
NeilKleistGao May 30, 2025
b6ddcaf
Add a clean version
NeilKleistGao May 30, 2025
d318440
Remove difftests things for now
NeilKleistGao May 30, 2025
2783f5f
Add more examples
NeilKleistGao May 30, 2025
9f48b1e
Fix problems in ExamplesInResponse.mls
LPTK Jun 2, 2025
15e5fcb
Add equivalent sig
LPTK Jun 2, 2025
4fbb6ea
Port some examples from flix
NeilKleistGao Jun 6, 2025
8be7290
Port GUI example from flix
NeilKleistGao Jun 9, 2025
1a3b41d
Add extension examples
NeilKleistGao Jun 9, 2025
9ada6c8
Categorize examples and add more documentations
NeilKleistGao Jun 10, 2025
4e8a8a1
Add else type check
NeilKleistGao Jun 16, 2025
717a864
Merge
NeilKleistGao Jun 17, 2025
2788340
WIP: Add decl-site variance
NeilKleistGao Jun 17, 2025
c0198de
Add test for pat-mat
NeilKleistGao Jun 19, 2025
3c1abef
Changes from meeting + some older changes (start making the constrain…
LPTK Jun 24, 2025
60e27b3
Merge from hkmc2
NeilKleistGao Jun 25, 2025
cd6b77c
Rename
NeilKleistGao Jun 25, 2025
a5f3219
Make region targ covariant
NeilKleistGao Jul 3, 2025
afc5e74
Merge branch 'hkmc2' of github.com:hkust-taco/mlscript into adt💬
NeilKleistGao Jul 22, 2025
32a98c0
Add case study examples
NeilKleistGao Jul 23, 2025
3cd0c3a
WIP: Add codegen for constraint solver (without tests) and minor fix
NeilKleistGao Jul 24, 2025
9cb9f26
Some fix
NeilKleistGao Jul 25, 2025
4c6c366
Update web demo code
NeilKleistGao Jul 30, 2025
240a555
Add missing updates
NeilKleistGao Aug 20, 2025
522f9c1
Minor
NeilKleistGao Aug 21, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type Cache = Set[(Type, Type)]
type ExtrudeCache = mutable.HashMap[(Uid[InfVar], Bool), InfVar]

case class CCtx(cache: Cache, parents: Ls[(Type, Type)], origin: Term, exp: Opt[GeneralType])(using Scope):
def err(using Raise) =
def err(using Raise, BbCtx) =
raise(ErrorReport(
msg"Type error in ${origin.describe}${exp match
case S(ty) => msg" with expected type ${ty.show}"
Expand Down
10 changes: 5 additions & 5 deletions hkmc2/shared/src/main/scala/hkmc2/bbml/NormalForm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ final case class Disj(conjs: Ls[Conj]) extends NormalForm with CachedBasicType:
def mkBasic: BasicType =
BasicType.union(conjs.map(_.toBasic))
def toDnf(using TL): Disj = this
override def show(using Scope): Str =
override def show(using Scope, BbCtx): Str =
if conjs.isEmpty then "⊥"
else conjs.map(_.show).mkString(" ∨ ")

Expand Down Expand Up @@ -53,7 +53,7 @@ extends NormalForm with CachedBasicType:
case (tv, false) => NegType(tv)
})
def toDnf(using TL): Disj = Disj(this :: Nil)
override def show(using Scope): Str =
override def show(using Scope, BbCtx): Str =
val s = ((i :: Nil).filterNot(_.isTop).map(_.show) :::
(u :: Nil).filterNot(_.isBot).map("¬{"+_.show+"}") :::
vars.map:
Expand Down Expand Up @@ -100,7 +100,7 @@ final case class Inter(v: Opt[ClassLikeType | FunType]) extends NormalForm:
case _ => N
def toBasic: BasicType = v.getOrElse(Top)
def toDnf(using TL): Disj = Disj(Conj(this, Union(N, Nil), Nil) :: Nil)
override def show(using Scope): Str =
override def show(using Scope, BbCtx): Str =
toBasic.show

override def showDbg: Str = toBasic.showDbg
Expand Down Expand Up @@ -130,7 +130,7 @@ extends NormalForm with CachedBasicType:
def mkBasic: BasicType =
BasicType.union(fun.toList ::: cls)
def toDnf(using TL): Disj = NormalForm.neg(this)
override def show(using Scope): Str =
override def show(using Scope, BbCtx): Str =
toType.show

override def showDbg: Str = toType.showDbg
Expand All @@ -145,7 +145,7 @@ sealed abstract class NormalForm extends TypeExt:
def subst(using map: Map[Uid[InfVar], InfVar]): ThisType =
toBasic.subst

def show(using Scope): Str
def show(using Scope, BbCtx): Str
def showDbg: Str

object NormalForm:
Expand Down
9 changes: 6 additions & 3 deletions hkmc2/shared/src/main/scala/hkmc2/bbml/PrettyPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package bbml
import scala.collection.mutable.{Set => MutSet, ListBuffer}
import utils.Scope

class PrettyPrinter(output: String => Unit)(using Scope):
class PrettyPrinter(output: String => Unit)(using Scope, BbCtx):
def print(ty: GeneralType): Unit =
output(s"Type: ${ty.show}")
ty.show match
case "()" =>
case tyStr =>
output(s"Type: ${tyStr}")
val bounds = PrettyPrinter.collectBounds(ty).distinct
if !bounds.isEmpty then
output("Where:")
Expand All @@ -14,7 +17,7 @@ class PrettyPrinter(output: String => Unit)(using Scope):
}

object PrettyPrinter:
def apply(output: String => Unit)(using Scope): PrettyPrinter = new PrettyPrinter(output)
def apply(output: String => Unit)(using Scope, BbCtx): PrettyPrinter = new PrettyPrinter(output)

type Bound = (Type, Type) // * Type <: Type

Expand Down
242 changes: 211 additions & 31 deletions hkmc2/shared/src/main/scala/hkmc2/bbml/bbML.scala

Large diffs are not rendered by default.

17 changes: 9 additions & 8 deletions hkmc2/shared/src/main/scala/hkmc2/bbml/types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import syntax.*
import semantics.*, semantics.Term.*
import utils.*
import scala.collection.mutable.{Set => MutSet}
import utils.Scope
import utils.Scope, Scope.scope
import Elaborator.State

// * General types include mono types (i.e., Type), forall quantified type, and poly function types
Expand All @@ -23,14 +23,14 @@ sealed abstract class GeneralType:
protected type ThisType <: GeneralType
def map(f: ThisType => ThisType): ThisType
def subst(using map: Map[Uid[InfVar], InfVar]): ThisType
def show(using Scope): Str
def show(using Scope, BbCtx): Str
def showDbg: Str

// * Types that can be used as class type arguments
sealed trait TypeArg:
def lvl: Int
def mapArg(f: Type => Type): TypeArg
def show(using Scope): Str
def show(using Scope, BbCtx): Str
def showDbg: Str
def & (that: TypeArg): TypeArg = (this, that) match
case (Wildcard(in1, out1), Wildcard(in2, out2)) => Wildcard(in1 | in2, out1 & out2)
Expand All @@ -52,7 +52,7 @@ sealed trait TypeArg:
case class Wildcard(in: Type, out: Type) extends TypeArg {
def mapArg(f: Type => Type): Wildcard = Wildcard(f(in), f(out))

override def show(using Scope): Str = in match
override def show(using Scope, BbCtx): Str = in match
case `out` => in.show
case Bot =>
out match
Expand Down Expand Up @@ -130,7 +130,7 @@ sealed abstract class Type extends GeneralType with TypeArg:
case ComposedType(l, r, false) => l.! | r.!
case _ => NegType(this)

protected[bbml] def paren(using Scope): Str = toBasic match
protected[bbml] def paren(using Scope, BbCtx): Str = toBasic match
case _: InfVar | _: ClassLikeType | _: NegType | Top | Bot => show
case _: ComposedType | _: FunType => s"($show)"

Expand Down Expand Up @@ -158,12 +158,13 @@ sealed abstract class BasicType extends Type:
case NegType(ty) => Type.mkNegType(f(ty))
case Top | Bot | _: InfVar => this

override def show(using scope: Scope): Str =
override def show(using Scope, BbCtx): Str =
def printEff(eff: Type) = eff match
case Bot => ""
// case ty if ty == allocSkolem => ""
case _ => s"{${eff.show}}"
this match
case ClassLikeType(sym, Nil) if sym is bbctx.getCls("Unit") => "()"
case ClassLikeType(name, targs) =>
if targs.isEmpty then s"${name.nme}" else s"${name.nme}[${targs.map(_.show).mkString(", ")}]"
case v @ InfVar(lvl, uid, _, isSkolem) =>
Expand Down Expand Up @@ -286,7 +287,7 @@ case class PolyType(tvs: Ls[InfVar], outer: Opt[InfVar], body: GeneralType) exte

override lazy val isPoly: Bool = true
override lazy val lvl: Int = (body :: tvs).map(_.lvl).max
override def show(using scope: Scope): Str =
override def show(using Scope, BbCtx): Str =
given Scope = scope.nest
val lst = (outer match {
case S(outer) =>
Expand Down Expand Up @@ -364,7 +365,7 @@ case class PolyFunType(args: Ls[GeneralType], ret: GeneralType, eff: Type) exten

lazy val isPoly: Bool = (ret :: args).exists(_.isPoly)
lazy val lvl: Int = (ret :: eff :: args).map(_.lvl).max
override def show(using Scope): Str = s"(${args.map(_.show).mkString(", ")}) ->{${eff.show}} ${ret.show}"
override def show(using Scope, BbCtx): Str = s"(${args.map(_.show).mkString(", ")}) ->{${eff.show}} ${ret.show}"
override def showDbg: Str = s"(${args.map(_.showDbg).mkString(", ")}) ->{${eff.showDbg}} ${ret.showDbg}"
private lazy val mono: Opt[FunType] = if isPoly then N else
Some(FunType(args.map {
Expand Down
51 changes: 48 additions & 3 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/BlockImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,54 @@ trait BlockImpl(using Elaborator.State):
stmt.desugared match
case PossiblyAnnotated(anns, h @ Hndl(body = N)) =>
PossiblyAnnotated(anns, h.copy(body = S(Block(stmts)))) :: Nil
case PossiblyAnnotated(anns, TypeDef(syntax.Cls, Ident(name), rhs, S(Block(Constructor(Block(ctors)) :: rest)))) =>
PossiblyAnnotated(anns, TypeDef(syntax.Cls, Ident(name), rhs, if rest.isEmpty then N else S(Block(rest)))) ::
(ctors.map(head => PossiblyAnnotated(anns, TypeDef(syntax.Cls, InfixApp(head, syntax.Keyword.`extends`, Ident(name)), N, N))))
case PossiblyAnnotated(anns, TypeDef(syntax.Cls, head, rhs, S(Block(Constructor(Block(ctors)) :: rest)))) =>
// A temp solution for ADTs, which desugars ADTs to normal class definitions.
// This will be removed after we truly support ADTs correctly.
// TODO: No raise contextual variable. Only `Error` nodes are returned if there is an error.
val (headId, headPs) = head match
case id: Ident => (id, Nil)
case App(id: Ident, TyTup(ps)) => (id, ps)
case _ => Error()
// Temporarily use `data` annotation to distinguish the following ctors:
// - Ctor(...)
// - Ctor[...](...) extends ADT[...]
// since the former will be desugared to `class Ctor[...](...) extends ADT[...]`,
// where `Ctor[...]` and `ADT[...]` share the same type parameter list
def wrapGeneric(decl: Tree, res: Tree) = decl match
case InfixApp(_, syntax.Keyword.`extends`, _) =>
Annotated(Keywrd(syntax.Keyword.data), res)
case _ => res
// Generate `extends` suffix if it is not provided by users
// Also check if the number of type parameters is correct
def genExt(decl: Tree) = decl match
case InfixApp(_, syntax.Keyword.`extends`, ext) => ext match
case _: Ident if headPs.isEmpty => ext
case App(id: Ident, TyTup(ps)) if id.name == headId.name && ps.length == headPs.length => ext
case _ => Error()
case _ => headPs match
case Nil => headId
case ps => App(headId, TyTup(ps.map(
t => Tup(Tree.Modified(syntax.Keyword.`in`, N, t) :: Tree.Modified(syntax.Keyword.`out`, N, t) :: Nil)
)))
// Insert type parameters for constructors.
// e.g. `class Foo[T] with constructor Bar(x: T)` will be desugared to
// `class Bar[T](x: T) extends Foo[T]`
// Otherwise, the elaborator will complain `T` is not defined.
def genCtorHead(decl: Tree) = decl match
case InfixApp(decl, syntax.Keyword.`extends`, _) => decl // check will be applied in genExt
case App(_: Ident, tup: TyTup) => Error()
case App(id: Ident, ps: Tup) => App(App(id, TyTup(headPs)), ps)
case id: Ident => App(id, TyTup(headPs))
case _ => Error()
// Only fields modified by `val` can be extracted by pattern matching.
// For ADTs, all fields can be extracted, but we don't want to add `val`s manumally.
def insertVal(decl: Tree): Tree = decl match
case id: Ident => id
case App(f, Tup(ps)) => App(f, Tup(ps.map(p => TermDef(syntax.ImmutVal, p, N))))
case App(f, tup: TyTup) => App(insertVal(f), tup)
case _ => Error()
PossiblyAnnotated(anns, TypeDef(syntax.Cls, head, rhs, if rest.isEmpty then N else S(Block(rest)))) ::
(ctors.map(h => PossiblyAnnotated(anns, wrapGeneric(h, TypeDef(syntax.Cls, InfixApp(insertVal(genCtorHead(h)), syntax.Keyword.`extends`, genExt(h)), N, N)))))
::: desug(stmts)
case stmt => stmt :: desug(stmts)
case Nil => Nil
Expand Down
5 changes: 5 additions & 0 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Pattern.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ object Pattern:
*/
type Argument = (scrutinee: BlockLocalSymbol, pattern: Tree, split: Opt[DeBrujinSplit])

/** A class-like pattern whose symbol is resolved to a class or a module. */
object ResolvedClassOrModule:
def unapply(p: Pattern.ClassLike): Opt[(ClassSymbol | ModuleSymbol, Opt[Ls[Argument]])] =
p.constructor.symbol.flatMap(_.asClsOrMod).map(_ -> p.arguments)

/** A class-like pattern whose symbol is resolved to a class. */
object Class:
def unapply(p: Pattern): Opt[ClassSymbol] = p match
Expand Down
9 changes: 3 additions & 6 deletions hkmc2/shared/src/test/mlscript/basics/ADTs.mls
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,8 @@ class Expr[A] with
constructor
Lit(n: Int) { A = Int }
Add(lhs: Expr[Int], rhs: Expr[Int]) { A = Int }
//│ ╔══[ERROR] Unsupported constructor in this position.
//│ ║ l.16: Lit(n: Int) { A = Int }
//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^
//│ ║ l.17: Add(lhs: Expr[Int], rhs: Expr[Int]) { A = Int }
//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//│ ═══[ERROR] Expected a valid class definition head; found ‹erroneous syntax› instead
//│ ═══[ERROR] Expected a valid class definition head; found ‹erroneous syntax› instead


// * one can also go the TypeScript way
Expand All @@ -36,7 +33,7 @@ enum Expr =
| Lit(n: Int)
| Add(lhs: Expr, rhs: Expr)
//│ ╔══[ERROR] Unrecognized definitional assignment left-hand side: juxtaposition
//│ ║ l.35: enum Expr =
//│ ║ l.32: enum Expr =
//│ ╙── ^^^^^^^^^


Loading
Loading