Skip to content

Commit

Permalink
Fix problems with selections and related features
Browse files Browse the repository at this point in the history
  • Loading branch information
LPTK committed Jan 9, 2025
1 parent 1b5d920 commit 9efe83a
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 37 deletions.
16 changes: 13 additions & 3 deletions hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,23 @@ class Lowering(using TL, Raise, Elaborator.State):
TODO("Other argument forms")
case spd: Spd => true -> spd.term
val l = new TempSymbol(S(t))
subTerm(f): fr =>
def conclude(fr: Path) =
def rec(as: Ls[Bool -> st], asr: Ls[Arg]): Block = as match
case Nil => k(Call(fr, asr.reverse)(isMlsFun))
case (spd, a) :: as =>
subTerm(a): ar =>
rec(as, Arg(spd, ar) :: asr)
rec(as, Nil)
f match
// * Due to whacky JS semantics, we need to make sure that selections leading to a call
// * are preserved in the call and not moved to a temporary variable.
case sel @ Sel(prefix, nme) =>
subTerm(prefix): p =>
conclude(Select(p, nme)(sel.sym))
case sel @ SelProj(prefix, _, nme) =>
subTerm(prefix): p =>
conclude(Select(p, nme)(sel.sym))
case _ => subTerm(f)(conclude)
case _ =>
TODO("Other argument list forms")
case st.Blk(Nil, res) => term(res)(k)
Expand Down Expand Up @@ -325,8 +335,8 @@ class Lowering(using TL, Raise, Elaborator.State):
End("error")

// * BbML-specific cases: t.Cls#field and mutable operations
case SelProj(prefix, _, proj) =>
setupSelection(prefix, proj, N)(k)
case sp @ SelProj(prefix, _, proj) =>
setupSelection(prefix, proj, sp.sym)(k)
case Region(reg, body) =>
Assign(reg, Instantiate(Select(Value.Ref(State.globalThisSymbol), Tree.Ident("Region"))(N), Nil), term(body)(k))
case RegRef(reg, value) =>
Expand Down
21 changes: 13 additions & 8 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -307,12 +307,19 @@ extends Importer:
Term.CompType(term(lhs), term(rhs), false)
case App(Ident(":="), Tree.Tup(lhs :: rhs :: Nil)) =>
Term.SetRef(term(lhs), term(rhs))
case App(Ident("#"), Tree.Tup(SynthSel(pre, idn: Ident) :: (idp: Ident) :: Nil)) =>
Term.SelProj(term(pre), term(idn), idp)
case App(Ident("#"), Tree.Tup(SynthSel(pre, Ident(name)) :: App(Ident(proj), args) :: Nil)) =>
term(App(App(Ident("#"), Tree.Tup(SynthSel(pre, Ident(name)) :: Ident(proj) :: Nil)), args))
case App(Ident("#"), Tree.Tup(Sel(pre, idn: Ident) :: (idp: Ident) :: Nil)) =>
Term.SelProj(term(pre), term(idn), idp)
val c = cls(idn, inAppPrefix = false)
val f = c.symbol.flatMap(_.asCls) match
case S(cls: ClassSymbol) =>
cls.tree.allSymbols.get(idp.name) match
case S(fld: FieldSymbol) => S(fld)
case _ =>
raise(ErrorReport(msg"Class '${cls.nme}' does not contain member '${idp.name}'." -> idp.toLoc :: Nil))
N
case _ =>
raise(ErrorReport(msg"Identifier `${idn.name}` does not name a known class symbol." -> idn.toLoc :: Nil))
N
Term.SelProj(term(pre), c, idp)(N)
case App(Ident("#"), Tree.Tup(Sel(pre, Ident(name)) :: App(Ident(proj), args) :: Nil)) =>
term(App(App(Ident("#"), Tree.Tup(Sel(pre, Ident(name)) :: Ident(proj) :: Nil)), args))
case App(Ident("!"), Tree.Tup(rhs :: Nil)) =>
Expand Down Expand Up @@ -369,9 +376,7 @@ extends Importer:
case Sel(pre, nme) =>
val preTrm = term(pre)
val sym = resolveField(nme, preTrm.symbol, nme)
if inAppPrefix
then Term.SynthSel(preTrm, nme)(sym)
else Term.Sel(preTrm, nme)(sym)
Term.Sel(preTrm, nme)(sym)
case tree @ Tup(fields) =>
Term.Tup(fields.map(fld(_)))(tree)
case New(body) => // TODO handle Under
Expand Down
6 changes: 4 additions & 2 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ enum Term extends Statement:
case Quoted(body: Term)
case Unquoted(body: Term)
case New(cls: Term, args: Ls[Term])
case SelProj(prefix: Term, cls: Term, proj: Tree.Ident)
case SelProj(prefix: Term, cls: Term, proj: Tree.Ident)(val sym: Opt[FieldSymbol])
case Asc(term: Term, ty: Term)
case CompType(lhs: Term, rhs: Term, pol: Bool)
case Neg(rhs: Term)
Expand All @@ -46,6 +46,7 @@ enum Term extends Statement:
case Ref(sym) => S(sym)
case sel: SynthSel => sel.sym
case sel: Sel => sel.sym
case sel: SelProj => sel.sym
case _ => N

def describe: Str = this match
Expand Down Expand Up @@ -84,7 +85,7 @@ import Term.*
sealed trait Statement extends AutoLocated with ProductWithExtraInfo:

def extraInfo: Str = this match
case trm @ (_: Sel | _: SynthSel) => trm.symbol.mkString
case trm @ (_: Sel | _: SynthSel | _: SelProj) => trm.symbol.mkString
case _ => ""

def subStatements: Ls[Statement] = this match
Expand Down Expand Up @@ -205,6 +206,7 @@ sealed trait Statement extends AutoLocated with ProductWithExtraInfo:
cls.paramsOpt.fold("")(_.toString)} ${cls.body}"
case Import(sym, file) => s"import ${sym} from ${file}"
case Annotated(annotation, target) => s"@${annotation.showDbg} ${target.showDbg}"
case Throw(res) => s"throw ${res.showDbg}"

final case class LetDecl(sym: LocalSymbol, annotations: Ls[Term]) extends Statement

Expand Down
2 changes: 2 additions & 0 deletions hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala
Original file line number Diff line number Diff line change
Expand Up @@ -340,4 +340,6 @@ trait TypeDefImpl(using semantics.Elaborator.State) extends TypeOrTermDef:
case (S(spd), id, _) => ??? // spreads are not allowed in class parameters
case (N, id, _) => semantics.TermSymbol(ParamBind, symbol.asClsLike, id)
.toList

lazy val allSymbols = definedSymbols ++ clsParams.map(s => s.nme -> s).toMap

29 changes: 16 additions & 13 deletions hkmc2/shared/src/test/mlscript-compile/bbml/Predef.mjs
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
const Predef$class = class Predef {
constructor() {}
checkArgs(functionName, expected, got) {
let scrut, name, scrut1, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6;
scrut = got != expected;
checkArgs(functionName, expected, isUB, got) {
let scrut, name, scrut1, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9;
tmp = got < expected;
tmp1 = got > expected;
tmp2 = isUB && tmp1;
scrut = tmp || tmp2;
if (scrut) {
scrut1 = functionName.length > 0;
if (scrut1) {
tmp = " '".concat(functionName) ?? null;
tmp1 = tmp.concat("'") ?? null;
tmp3 = " '" + functionName;
tmp4 = tmp3 + "'";
} else {
tmp1 = "";
tmp4 = "";
}
name = tmp1;
tmp2 = "Function".concat(name) ?? null;
tmp3 = tmp2.concat(" expected ") ?? null;
tmp4 = tmp3.concat(expected) ?? null;
tmp5 = tmp4.concat(" arguments but got ") ?? null;
tmp6 = tmp5.concat(got) ?? null;
throw new Error.class(tmp6);
name = tmp4;
tmp5 = "Function" + name;
tmp6 = tmp5 + " expected ";
tmp7 = tmp6 + expected;
tmp8 = tmp7 + " arguments but got ";
tmp9 = tmp8 + got;
throw globalThis.Error(tmp9) ?? null;
} else {
return null;
}
Expand Down
10 changes: 5 additions & 5 deletions hkmc2/shared/src/test/mlscript-compile/bbml/Predef.mls
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module Predef with ...

fun checkArgs(functionName, expected, got) =
if got != expected then
let name = if functionName.Str#length > 0 then " '".Str#concat(functionName).Str#concat("'") else ""
throw new Error("Function".Str#concat(name).Str#concat(" expected ").Str#concat(expected).Str#concat(" arguments but got ").Str#concat(got))
else ()
fun checkArgs(functionName, expected, isUB, got) =
if got < expected || isUB && got > expected do
let name = if functionName.length > 0 then " '" + functionName + "'" else ""
throw globalThis.Error("Function" + name + " expected " + expected + " arguments but got " + got)

22 changes: 22 additions & 0 deletions hkmc2/shared/src/test/mlscript/basics/ExplicitSelections.mls
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
:js


class Foo() with
val x = 1
fun f() = x + 1


Foo().x
//│ = 1

Foo().Foo#x
//│ = 1


Foo().f()
//│ = 2

Foo().Foo#f()
//│ = 2


29 changes: 27 additions & 2 deletions hkmc2/shared/src/test/mlscript/bbml/bbErrors.mls
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,39 @@
//│ ╔══[ERROR] Name not found: Foo
//│ ║ l.23: (1).Foo#a()
//│ ╙── ^^^^
//│ ╔══[ERROR] Identifier `Foo` does not name a known class symbol.
//│ ║ l.23: (1).Foo#a()
//│ ╙── ^^^^
//│ ═══[ERROR] Not a valid class: <error>
//│ Type: ⊥


fun Oops() = 1
Oops().Oops#a()
//│ ╔══[ERROR] Identifier `Oops` does not name a known class symbol.
//│ ║ l.35: Oops().Oops#a()
//│ ╙── ^^^^^
//│ ╔══[ERROR] Not a valid class: selection
//│ ║ l.35: Oops().Oops#a()
//│ ╙── ^^^^^
//│ Type: ⊥

class Oops2()
(new Oops2()).Oops2#a()
//│ ╔══[ERROR] Class 'Oops2' does not contain member 'a'.
//│ ║ l.45: (new Oops2()).Oops2#a()
//│ ╙── ^
//│ ╔══[ERROR] a is not a valid member in class Oops2
//│ ║ l.45: (new Oops2()).Oops2#a()
//│ ╙── ^^^^^^^^^^^^^^^^
//│ Type: ⊥



fun inc(x) = x + 1
inc("oops")
//│ ╔══[ERROR] Type error in string literal with expected type 'x
//│ ║ l.32: inc("oops")
//│ ║ l.57: inc("oops")
//│ ║ ^^^^^^
//│ ╟── because: cannot constrain Str <: 'x
//│ ╟── because: cannot constrain Str <: 'x
Expand All @@ -41,7 +66,7 @@ inc("oops")
fun inc(x) = x + 1
inc : Int
//│ ╔══[ERROR] Type error in selection with expected type Int
//│ ║ l.42: inc : Int
//│ ║ l.67: inc : Int
//│ ║ ^^^
//│ ╙── because: cannot constrain 'x -> Int <: Int
//│ Type: Int
Expand Down
2 changes: 1 addition & 1 deletion hkmc2/shared/src/test/mlscript/bbml/bbRec.mls
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ new Foo(123)

:todo proper error
fun f(x) = f(Foo.a(x))
//│ ╔══[ERROR] Term shape not yet supported by BbML: SynthSel(SynthSel(Ref(globalThis:block#5),Ident(Foo)),Ident(a))
//│ ╔══[ERROR] Term shape not yet supported by BbML: Sel(SynthSel(Ref(globalThis:block#5),Ident(Foo)),Ident(a))
//│ ║ l.47: fun f(x) = f(Foo.a(x))
//│ ╙── ^^^^^
//│ Type: ⊤
Expand Down
2 changes: 1 addition & 1 deletion hkmc2/shared/src/test/mlscript/codegen/Do.mls
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ val f = case
//│ Desugared:
//│ > if
//│ > caseScrut is 0 then "null"
//│ > let $doTemp = globalThis:import#Prelude#666(.)console‹member:console›(.)log("non-null")
//│ > let $doTemp = globalThis:import#Prelude#666(.)console‹member:console›.log("non-null")
//│ > caseScrut is 1 then "unit"
//│ > let res = "other"
//│ > let $doTemp = member:Predef#666(.)print‹member:print›(res#666)
Expand Down
4 changes: 3 additions & 1 deletion hkmc2/shared/src/test/mlscript/decls/Prelude.mls
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ declare class Object
declare class Bool
declare class Int
declare class Num
declare class Str
declare class Str with
fun length: Int
fun concat: Str -> Str
declare class Set
declare class Error(msg)

Expand Down
2 changes: 1 addition & 1 deletion hkmc2/shared/src/test/mlscript/nofib/NofibPrelude.mls
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Lazy[out A](init: () -> A) with
set cached = Some(v)
v
fun lazy(x) = Lazy(x)
fun force(x) = if x is Lazy then x.get()
fun force(x) = if x is Lazy then x.Lazy#get()

abstract class List[out T]: Cons[T] | Nil
class (::) Cons[out T](head: T, tail: List[T]) extends List[T] with
Expand Down

0 comments on commit 9efe83a

Please sign in to comment.