Skip to content

Commit 4323f94

Browse files
FlandiaYingmanLPTK
andauthored
use[T] as Summon (#310)
Co-authored-by: Lionel Parreaux <[email protected]>
1 parent bbad90d commit 4323f94

File tree

17 files changed

+267
-98
lines changed

17 files changed

+267
-98
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,12 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
610610
msg"This annotation has no effect." -> ann.toLoc ::
611611
msg"Such annotations are not supported on ${receiver.describe} terms." -> receiver.toLoc :: Nil))
612612
term(receiver)(k)
613+
case use: Summon =>
614+
warnStmt
615+
use.sym match
616+
case S(_: ErrorSymbol) => End("missing instance")
617+
case S(sym) => k(subst(Value.Ref(sym)))
618+
case N => lastWords(s"unresolved summon: ${use}")
613619
case Error => End("error")
614620

615621
// case _ =>

hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,13 @@ extends Importer:
417417
case N =>
418418
raise(ErrorReport(msg"Name not found: $name" -> tree.toLoc :: Nil))
419419
Term.Error
420+
// A use[T] construct: analogous to Scala's summon[T].
421+
case TyApp(Keywrd(Keyword.`use`), targs) =>
422+
if targs.length != 1 then
423+
raise(ErrorReport(msg"Illegal use[T] construct. Only one type is allowed." -> tree.toLoc :: Nil))
424+
Term.Error
425+
val ty = term(targs.head)
426+
Term.Summon(ty)(tree, N)
420427
case TyApp(lhs, targs) =>
421428
Term.TyApp(subterm(lhs, inTyAppPrefix = true), targs.map {
422429
case Modified(Keyword.`in`, inLoc, arg) => Term.WildcardTy(S(subterm(arg)), N)
@@ -1240,7 +1247,7 @@ extends Importer:
12401247
param(hd, flags.ctx, inDataClass)(using ctx) match
12411248
case S((isSpd, p)) =>
12421249
val isCtx = hd match
1243-
case Modified(Keyword.`using`, _, _) => true
1250+
case TermDef(k = Ins, rhs = N) => true
12441251
case _ => false
12451252
val newCtx = ctx + (p.sym.name -> p.sym)
12461253
val newFlags = if isCtx then flags.copy(ctx = true) else flags

hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,15 @@ import mlscript.utils.*, shorthands.*
55
import utils.TraceLogger
66

77
import syntax.Tree
8-
import syntax.{Fun, Ins, Mod, ImmutVal}
8+
import syntax.{Fun, Ins, Mod, ImmutVal, MutVal}
99
import syntax.Keyword.{`if`}
1010
import semantics.Term
1111
import semantics.Elaborator.State
1212
import Resolver.ICtx.Type
1313

1414
import Message.MessageContext
1515
import scala.annotation.tailrec
16-
import hkmc2.syntax.MutVal
17-
import hkmc2.semantics.ClassDef.Parameterized
18-
import hkmc2.semantics.ClassDef.Plain
19-
import hkmc2.syntax.Tree.Ident
20-
import java.sql.Ref
16+
import hkmc2.semantics.Resolver.ICtx.Instance
2117

2218
object Resolver:
2319

@@ -52,20 +48,35 @@ object Resolver:
5248
tEnv: Map[VarSymbol, Type]
5349
):
5450

55-
def +(typ: Type.Concrete, sym: Symbol): ICtx =
51+
def +(typ: Type.Specified, sym: Symbol): ICtx =
5652
val newLs = (typ -> ICtx.Instance(sym)) :: iEnv.getOrElse(typ.toSym, Nil)
5753
val newEnv = iEnv + (typ.toSym -> newLs)
5854
copy(iEnv = newEnv)
5955

6056
def withTypeArg(param: VarSymbol, arg: Type): ICtx =
6157
copy(tEnv = tEnv + (param -> arg))
6258

63-
def get(query: Type.Concrete): Opt[ICtx.Instance] =
64-
iEnv.getOrElse(query.toSym, Nil)
65-
.find: (typ, _) =>
66-
compare(query, typ)
67-
.map: (_, instance) =>
68-
instance
59+
def get(query: Type.Specified): Ls[Message -> Opt[Loc]] \/ ICtx.Instance =
60+
def resolveTpe(tpe: Type.Specified): Opt[Type.Sym] = tpe.toSym match
61+
case tpe @ Type.Sym(sym: VarSymbol) => tEnv.get(sym) match
62+
// Specified type variable. Resolve it recursively.
63+
case S(tpe: Type.Specified) => resolveTpe(tpe)
64+
// Unspecified type variable. Reject it.
65+
case S(Type.Unspecified) => N
66+
// Unbound type variable. Just use it.
67+
case N => S(tpe)
68+
case tpe => S(tpe)
69+
resolveTpe(query) match
70+
case S(tpe) => iEnv.getOrElse(tpe, Nil)
71+
.find: (typ, _) =>
72+
compare(query, typ)
73+
.map: (_, instance) =>
74+
instance
75+
.toRight:
76+
msg"Missing instance: Expected: ${describeType(query)}; Available: ${showEnv}" -> N :: Nil
77+
case N => L:
78+
msg"Illegal query for an unspecified type variable ${query.show}." -> N :: Nil
79+
6980

7081
private def compare(a: Type, b: Type): Boolean = (a, b) match
7182
case (Type.Unspecified, _) => true
@@ -86,6 +97,12 @@ object Resolver:
8697
.flatMap(_.map((typ, instance) => s"${typ.show}"))
8798
.mkString("(", ", ", ")")
8899

100+
def describeType(tpe: Type): Str = tpe match
101+
case Type.Sym(sym: VarSymbol) =>
102+
s"${tEnv.get(sym).getOrElse(Type.Unspecified).show} (${tpe.show})"
103+
case _ =>
104+
s"${tpe.show}"
105+
89106
object ICtx:
90107

91108
enum Type:
@@ -110,8 +127,8 @@ object Resolver:
110127
case Unspecified => "‹unspecified›"
111128

112129
object Type:
113-
type Concrete = Sym | App
114-
extension (t: Concrete)
130+
type Specified = Sym | App
131+
extension (t: Specified)
115132
def toSym: Sym = t match
116133
case sym: Sym => sym
117134
case App(sym, _) => sym
@@ -424,6 +441,26 @@ class Resolver(tl: TraceLogger)
424441
case Term.Ref(_) =>
425442
resolveSymbol(t)
426443
(t.termDefn, ictx)
444+
445+
case use @ Term.Summon(ty) =>
446+
traverse(ty, expect = NonModule(N))
447+
resolveType(ty) match
448+
case S(tpe: Type.Specified) =>
449+
ictx.get(tpe) match
450+
case R(i) =>
451+
log(s"Resolved type ${tpe} with instance ${i}")
452+
use.sym = S(i.sym)
453+
case L(msgs) =>
454+
use.sym = S(ErrorSymbol("Missing Instance", use.tree))
455+
raise(ErrorReport(
456+
msg"Cannot query instance for use-expression of type ${ictx.describeType(tpe)}" -> t.toLoc ::
457+
msgs
458+
))
459+
case N =>
460+
use.sym = S(ErrorSymbol("Missing Type", use.tree))
461+
// There is an error during resolving the type signature.
462+
// The error should have been reported.
463+
(t.termDefn, ictx)
427464

428465
log(s"Resolving resolvable with defn = ${defn}")
429466

@@ -651,9 +688,9 @@ class Resolver(tl: TraceLogger)
651688
log(s"Resolving implicit argument, expecting a ${p.sign}")
652689
p.sign match
653690
case S(sign) => resolveType(sign) match
654-
case S(tpe: Type.Concrete) =>
691+
case S(tpe: Type.Specified) =>
655692
ictx.get(tpe) match
656-
case S(i) =>
693+
case R(i) =>
657694
log(s"Resolved ${p.sign} with instance ${i}")
658695
val ref = i.sym.ref()
659696
traverse(ref,
@@ -663,11 +700,10 @@ class Resolver(tl: TraceLogger)
663700
else NonModule(S(msg"Module argument passed to a non-module parameter.")),
664701
)
665702
Fld(p.flags, ref, N)
666-
case N =>
703+
case L(msgs) =>
667704
raise(ErrorReport(
668-
msg"Missing instance for contextual parameter of type `${tpe.show}` in this call" -> lhs.toLoc ::
669-
msg"Required by contextual parameter declaration: " -> p.toLoc ::
670-
msg"Expected: ${tpe.show}; Available: ${ictx.showEnv}" -> N :: Nil))
705+
msg"Cannot query instance of type ${ictx.describeType(tpe)} for call: " -> lhs.toLoc ::
706+
msg"Required by contextual parameter declaration: " -> p.toLoc :: msgs))
671707
Fld(FldFlags.empty, Term.Error, N)
672708
case N =>
673709
// There is an error during resolving the type signature.
@@ -690,7 +726,7 @@ class Resolver(tl: TraceLogger)
690726
else NonModule(S(msg"Non-module parameter must have a non-module type.")),
691727
)
692728

693-
def resolveType(t: Term): Opt[ICtx.Type.Concrete] = t match
729+
def resolveType(t: Term): Opt[ICtx.Type.Specified] = t match
694730
// If the term is a type application, e.g., T[A, ...], resolve the
695731
// type constructor and arguments respectively.
696732
case Term.TyApp(con, args) =>

hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ sealed trait ResolvableImpl:
4343
case t: Term.TyApp => t.copy()(t.sym).noIArgs
4444
case t: Term.Sel => t.copy()(t.sym).noIArgs
4545
case t: Term.SynthSel => t.copy()(t.sym).noIArgs
46+
case t: Term.Summon => t.copy()(t.tree, t.sym).noIArgs
4647

4748
def instantiate(using State): Term = iargsLs match
4849
case N => lastWords(s"missing implicit arguments for term ${t}")
@@ -114,6 +115,7 @@ enum Term extends Statement:
114115
case Annotated(annot: Annot, target: Term)
115116
case Handle(lhs: LocalSymbol, rhs: Term, args: List[Term],
116117
derivedClsSym: ClassSymbol, defs: Ls[HandlerTermDefinition], body: Term)
118+
case Summon(ty: Term)(val tree: Tree, var sym: Opt[Symbol]) extends Term with ResolvableImpl
117119

118120
/**
119121
* The prelinminary symbol for the term that is resolved during
@@ -138,6 +140,7 @@ enum Term extends Statement:
138140
case sel: SelProj => sel.sym
139141
case app: App => app.sym
140142
case tyApp: TyApp => tyApp.sym
143+
case summon: Summon => summon.sym
141144
case _ => N
142145

143146
def sel(id: Tree.Ident, sym: Opt[FieldSymbol]): Sel =
@@ -251,6 +254,7 @@ sealed trait Statement extends AutoLocated with ProductWithExtraInfo:
251254
case Handle(lhs, rhs, args, derivedClsSym, defs, bod) => rhs :: args ::: defs.flatMap(_.td.subTerms) ::: bod :: Nil
252255
case Neg(e) => e :: Nil
253256
case Annotated(ann, target) => ann.subTerms ::: target :: Nil
257+
case Summon(ty) => ty :: Nil
254258

255259
// private def treeOrSubterms(t: Tree, t: Term): Ls[Located] = t match
256260
private def treeOrSubterms(t: Tree): Ls[Located] = t match
@@ -347,6 +351,7 @@ sealed trait Statement extends AutoLocated with ProductWithExtraInfo:
347351
case TypeDef(sym, tparams, rhs, _, _) =>
348352
s"type ${sym}${tparams.mkStringOr(", ", "[", "]")} = ${rhs.fold("")(x => x.showDbg)}"
349353
case Missing => "missing"
354+
case Summon(ty) => s"use[${ty.showDbg}]"
350355

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

hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ class ParseRules(using State):
305305
) { case (body, _) => Tree.Constructor(body) },
306306
Kw(`fun`)(termDefBody(Fun)),
307307
Kw(`val`)(termDefBody(ImmutVal)),
308-
Kw(`use`)(termDefBody(Ins)),
308+
Kw(`using`)(termDefBody(Ins)),
309309
typeAliasLike(`type`, Als),
310310
typeAliasLike(`pattern`, Pat),
311311
Kw(`class`)(typeDeclBody(Cls)),
@@ -333,8 +333,8 @@ class ParseRules(using State):
333333
modified(`return`),
334334
modified(`throw`),
335335
modified(`import`), // TODO improve – only allow strings
336+
singleKw(`use`)(Keywrd(`use`)),
336337
// modified(`type`),
337-
modified(`using`),
338338
singleKw(`true`)(BoolLit(true)),
339339
singleKw(`false`)(BoolLit(false)),
340340
singleKw(`undefined`)(UnitLit(false)),

hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ enum Tree extends AutoLocated:
245245
case Spread(Keyword.`...`, _, S(und: Under)) => S(S(true), new Ident("_").withLocOf(und), N)
246246
case InfixApp(lhs: Ident, Keyword.`:`, rhs) => S(N, lhs, S(rhs))
247247
case TermDef(ImmutVal, inner, _) => inner.asParam(inUsing)
248-
case Modified(Keyword.`using`, _, inner) => inner match
248+
case TermDef(Ins, inner, N) => inner match
249249
// Param of form (using name: Type). Parse it as usual.
250250
case inner: InfixApp => inner.asParam(inUsing)
251251
// Param of form (using Type). Synthesize an identifier for it.

hkmc2/shared/src/test/mlscript-compile/apps/parsing/Lexer.mls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ fun makeLineLookupTable(text: Str): LineLookupTable =
5757
LineLookupTable(ns)
5858

5959
fun lex(str: Str, options) =
60-
use LineLookupTable = makeLineLookupTable(str)
60+
using LineLookupTable = makeLineLookupTable(str)
6161

6262
fun char(idx: Int) = if idx < str.length
6363
then (Some of str.charAt of idx) else None

hkmc2/shared/src/test/mlscript/basics/BadTypeClasses.mls

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,44 +11,50 @@ module M with
1111

1212
:e
1313
M.f
14-
//│ ╔══[ERROR] Missing instance for contextual parameter of type `Int` in this call
14+
//│ ╔══[ERROR] Cannot query instance of type Int for call:
1515
//│ ║ l.13: M.f
1616
//│ ║ ^^^
1717
//│ ╟── Required by contextual parameter declaration:
1818
//│ ║ l.10: fun f(using Int)
1919
//│ ║ ^^^
20-
//│ ╙── Expected: Int; Available: ()
20+
//│ ╙── Missing instance: Expected: Int; Available: ()
2121

2222
:e
23-
use 42 = 42
23+
using 42 = 42
2424
//│ ╔══[ERROR] Expected a type symbol, got integer literal
25-
//│ ║ l.23: use 42 = 42
26-
//│ ╙── ^^
25+
//│ ║ l.23: using 42 = 42
26+
//│ ╙── ^^
2727

2828
val someInt = 42
2929

3030
:e
31-
use someInt = 42
31+
using someInt = 42
3232
//│ ╔══[ERROR] Expected a type, got reference
33-
//│ ║ l.31: use someInt = 42
34-
//│ ╙── ^^^^^^^
33+
//│ ║ l.31: using someInt = 42
34+
//│ ╙── ^^^^^^^
35+
36+
:e
37+
using someInt: Int = 42
38+
//│ ╔══[ERROR] Expected a type symbol, got block
39+
//│ ║ l.37: using someInt: Int = 42
40+
//│ ╙── ^^^^^^^^^^^^
3541

3642
module M with
3743
fun bar[A, B](a: A, b: B) = a + b
3844

3945
:e
4046
M.bar[](1, 2)
4147
//│ ╔══[ERROR] Expected 2 type arguments, got 0
42-
//│ ║ l.40: M.bar[](1, 2)
48+
//│ ║ l.46: M.bar[](1, 2)
4349
//│ ╙── ^^^^^
4450

4551
:e
4652
M.bar[Int](1, 2)
4753
//│ ╔══[ERROR] Expected 2 type arguments, got 1
48-
//│ ║ l.46: M.bar[Int](1, 2)
54+
//│ ║ l.52: M.bar[Int](1, 2)
4955
//│ ╙── ^^^^^^^^^
5056

51-
use Int = 42
57+
using Int = 42
5258

5359
module M with
5460
fun foo()(using a: Int)
@@ -57,3 +63,19 @@ module M with
5763
:todo
5864
:e
5965
M.foo()
66+
67+
:e
68+
fun f[T](using t: T) = t
69+
f
70+
//│ ╔══[ERROR] Cannot query instance of type ‹unspecified› (T) for call:
71+
//│ ║ l.69: f
72+
//│ ║ ^
73+
//│ ╟── Required by contextual parameter declaration:
74+
//│ ║ l.68: fun f[T](using t: T) = t
75+
//│ ║ ^^^^
76+
//│ ╙── Illegal query for an unspecified type variable T.
77+
78+
:fixme // graceful error
79+
:e
80+
fun f(using t: Int = 42) = t
81+
//│ /!!!\ Uncaught error: scala.MatchError: TermDef(Ins,InfixApp(Ident(t),keyword ':',Ident(Int)),Some(IntLit(42))) (of class hkmc2.syntax.Tree$TermDef)

0 commit comments

Comments
 (0)