Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 3 additions & 1 deletion hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,8 @@ class Resolver(tl: TraceLogger)
case N =>
ictx

t.ictx = S(newICtx2)

// Resolve the implicit arguments.
newICtx2.givenIn:
// Create a new term definition for App terms. The new term
Expand Down Expand Up @@ -770,7 +772,7 @@ class Resolver(tl: TraceLogger)
val (expansionFn, pss) = expand(defn.params, identity, identity)
if defn.params.length =/= pss.length then
val expansion = expansionFn(t.duplicate)
t.expand(S(expansion))
// t.expand(S(expansion))
expansion match // * expansion may change the semantics, thus symbol is also changed
case r: Resolvable =>
resolveSymbol(r, prefer = prefer, sign = false)
Expand Down
25 changes: 25 additions & 0 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ sealed trait SelImpl(using val state: State) extends ResolvableImpl:
var resolvedTargets: Ls[flow.SelectionTarget] = Nil // * filled during flow analysis
var isErroneous: Bool = false // * to avoid reporting follow-on errors after a flow/resolution error

case class ContextualPlaceholder(val sym: FlowSymbol):
var solution: Opt[Term] = N

sealed trait ResolvableImpl:
this: Term =>

Expand All @@ -72,6 +75,14 @@ sealed trait ResolvableImpl:
*/
private[semantics]
var expansion: Opt[Opt[Term]] = N

/**
* The lexical context in which this term is resolved.
* This is for later to flow-resolve implicits.
*/
var ictx: Opt[Resolver.ICtx] = N

var resolvedContextuals: Opt[Ls[Type -> ContextualPlaceholder]] = N

def duplicate(using State): this.type =
this.match
Expand Down Expand Up @@ -127,6 +138,8 @@ sealed trait ResolvableImpl:
* `duplicate` method if it appears in its own expansion.
*
* This method is only supposed to be called by Resolver.
*
* The expansion term inherits all flow-resolved information from this term.
*/
private[semantics] def expand(expansion: Opt[Term]): this.type =
// `expansion.isDefined`: Ideally, if a term is already expanded,
Expand All @@ -140,6 +153,14 @@ sealed trait ResolvableImpl:
lastWords(s"Cannot expand the term ${this.showDbg} multiple times (to different expansions ${expansion.get.showDbg}).")

this.expansion = S(expansion)

expansion match
case S(r: Resolvable) =>
// Propagate flow-resolved information to the expansion
r.ictx = this.ictx
r.resolvedContextuals = this.resolvedContextuals
case _ => ()

this

def resolve: this.type = expand(N)
Expand Down Expand Up @@ -356,6 +377,10 @@ enum Term extends Statement:
App(this, Tup(args.toList.map(PlainFld(_)))(Tree.DummyTup))
(Tree.App(Tree.Dummy, Tree.Dummy), N, FlowSymbol(""))

def asResolvable: Opt[Resolvable] = this match
case r: Resolvable => S(r)
case _ => N

override def mkClone(using State): Term =
val that = this match
case Error => Error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ case class Constraint(lhs: Producer, rhs: Consumer):

enum Producer:
case Flow(sym: FlowSymbol)
case Fun(lhs: Consumer, rhs: Producer, captures: Ls[(Producer, Consumer)])
case Fun(lhs: Consumer, rhs: Producer, captures: Ls[(Producer, Consumer)], ctx: Bool = false) // TODO: default arg
case Tup(elems: Ls[Opt[SpreadKind] -> Producer])
case Ctor(sym: CtorSymbol, args: List[Producer])(val trm: Term) extends Producer, CtorImpl
case LeadingDotSel(trm: Term.LeadingDotSel)
Expand All @@ -39,7 +39,8 @@ enum Producer:

def show(using Scope): Document = this match
case Flow(sym) => scope.allocateOrGetName(sym)
case Fun(lhs, rhs, caps) => doc"(${lhs.showAsParams} -> ${rhs.show})"
case Fun(lhs, rhs, caps, ctx) if ctx => doc"(using ${lhs.showAsParams} -> ${rhs.show})"
case Fun(lhs, rhs, caps, ctx) => doc"(${lhs.showAsParams} -> ${rhs.show})"
case tup: Tup => Document.bracketed("[", "]")(showTupElems(tup))
case Ctor(LitSymbol(UnitLit(false)), Nil) => "()"
case Ctor(sym, args) => doc"${sym.nme}${args.map(_.showAsParams).mkDocument()}"
Expand All @@ -60,7 +61,8 @@ enum Producer:

def showDbg: Str = this match
case Flow(sym) => sym.showDbg
case Fun(lhs, rhs, caps) => s"(${lhs.showDbgAsParams} -> ${rhs.showDbg})"
case Fun(lhs, rhs, caps, ctx) if ctx => s"(using ${lhs.showDbgAsParams} -> ${rhs.showDbg})"
case Fun(lhs, rhs, caps, ctx) => s"(${lhs.showDbgAsParams} -> ${rhs.showDbg})"
case Ctor(LitSymbol(UnitLit(false)), Nil) => "()"
case Ctor(sym, Nil) => sym.nme
case Tup(args) => s"[${args.map((spd, a) => spd.fold("")(_.str) + a.showDbg).mkString(", ")}]"
Expand All @@ -87,6 +89,7 @@ enum Consumer:
case Ctor(sym: CtorSymbol, args: List[Consumer])
case Sel(nme: Ident, res: Consumer)(val trm: Term.Sel)
case Typ(typ: Type)
case Ins()


def show(using Scope): Document = this match
Expand Down
110 changes: 102 additions & 8 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx):
sym match
case cls: ClassSymbol => P.Ctor(cls, Nil)(t)
case cls: ModuleOrObjectSymbol => P.Ctor(cls, Nil)(t)
case ts: TermSymbol => P.Flow(ts.bms.get.flow)
case ts: TermSymbol =>
t.asResolvable.foreach: t =>
constrainContextualCall(t).foreach: c =>
collectedConstraints += ((src = t, c = c))
P.Flow(ts.bms.get.flow)

case Ref(sym) =>
sym match
Expand Down Expand Up @@ -152,6 +156,8 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx):
P.Flow(sym)

case sel @ Sel(pre, nme) =>
constrainContextualCall(sel).foreach: c =>
collectedConstraints += ((src = sel, c = c))
log(s"Selection ${sel.showDbg} ${sel.typ}")
checkLDS(pre): pre_t =>
sel.resolvedSym match
Expand Down Expand Up @@ -182,6 +188,8 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx):
P.Ctor(sym, args_t)(t)

case app @ App(lhs, rhs) =>
constrainContextualCall(app).foreach: c =>
collectedConstraints += ((src = app, c = c))
checkLDS(lhs): pre_t =>
val sym = app.resSym
val c = C.Fun(typeProd(rhs), C.Flow(sym))
Expand Down Expand Up @@ -251,6 +259,12 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx):
val selsToExpand: mutable.Buffer[Sel] = mutable.Buffer.empty
val leadingDotSelsToExpand: mutable.Buffer[LeadingDotSel] = mutable.Buffer.empty

/**
* A buffer containing candidate terms that possibly represent function calls that possibly accept
* some contextual parameters.
*/
val contextualCallsToExpand: mutable.Buffer[Resolvable] = mutable.Buffer.empty

def expandTerms() =
import SelectionTarget.*
selsToExpand.foreach: sel =>
Expand All @@ -259,12 +273,12 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx):
sel.resolvedTargets match
case ObjectMember(sym) :: Nil =>
assert(sel.sym.isEmpty)
sel.expansion = S(S(sel.copy()(sym = S(sym), sel.typ, sel.originalCtx)))
sel.expand(S(sel.copy()(sym = S(sym), sel.typ, sel.originalCtx)))
case CompanionMember(comp, sym) :: Nil =>
val base = Sel(comp, Tree.Ident(sym.nme))(S(sym), N, N)
val app = App(base, Tup(sel.prefix :: Nil)(Tree.DummyTup))(Tree.DummyApp, N, FlowSymbol.app())
log(s"Expansion: ${app.showDbg}")
sel.expansion = S(S(app))
sel.expand(S(app))
case Nil =>
// FIXME: actually allow that in dead code (use floodfill constraints from exported members to detect)
if !sel.isErroneous then raise:
Expand All @@ -284,19 +298,96 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx):
case CompanionMember(comp, sym) :: Nil =>
val base = Sel(comp, Tree.Ident(sym.nme))(S(sym), N, N)
log(s"Leading dot expansion: ${base.showDbg}")
sel.expansion = S(S(base))
sel.expand(S(base))
case Nil =>
// FIXME: actually allow that in dead code (use floodfill constraints from exported members to detect)
sel.expansion = S(S(Error))
sel.expand(S(Error))
raise:
ErrorReport:
msg"Cannot resolve leading dot selection" -> sel.toLoc :: Nil
case targets => sel.expansion = S(S(Error)); raise:
case targets => sel.expand(S(Error)); raise:
ErrorReport:
msg"Ambiguous selection with multiple apparent targets:" -> sel.toLoc
:: targets.map:
case CompanionMember(_, sym) => msg"companion member ${sym.nme}" -> sym.toLoc

contextualCallsToExpand.foreach: t =>
expandContextualCall(t.expanded.asResolvable.getOrElse(die))

/**
* For a possibly call to a contextual function, create a list of constraints to ensure that the
* contextual parameters are properly supplied.
*/
def constrainContextualCall(t: Resolvable): Ls[Constraint] =
def constrain(sym: TermSymbol): Ls[Constraint] =
val defn = sym.defn.getOrElse(die)
// TODO: This checks only the head param list. Check all param lists.
defn.params.headOption match
case S(ps) if ps.flags.ctx =>
log(s"Constraining contextual call ${t.showDbg}")
contextualCallsToExpand += t
val placeholders = ps.params.map: p =>
val sig = p.signType.getOrElse(die)
val sym = FlowSymbol(s"using-${p.sym.name}-flow")
val placeholder = ContextualPlaceholder(sym)
(sig, placeholder)
t.resolvedContextuals = S(placeholders)
val constraints = placeholders.map:
case (typ, placeholder) =>
val prod = P.Flow(placeholder.sym)
val cons = C.Typ(typ)
Constraint(prod, cons)
constraints
case _ => Nil

log(s"Attempting to constrain contextual call: ${t.showDbg}")
// If t is already resolved by the basic resolution phase,
// simply use the resolved symbol; otherwise, use the flow information.
t.resolvedSym match
case S(sym: TermSymbol) => constrain(sym)
case S(sym) => Nil

case N => t match // use flow info

case t: Sel => t.resolvedTargets match
case target :: Nil =>
log(s"Resolved contextual call resolution for ${t.showDbg} to target ${target}")
// TODO: It is really odd that SelectionTarget carries a MemberSymbol instead of a DefinitionSymbol...
target match
case SelectionTarget.ObjectMember(sym: TermSymbol) =>
constrain(sym)
case SelectionTarget.ObjectMember(sym: BlockMemberSymbol) =>
sym.asTrm.map(constrain(_)).getOrElse(Nil)
case SelectionTarget.ObjectMember(sym) =>
Nil
case SelectionTarget.CompanionMember(t, sym: TermSymbol) =>
constrain(sym)
case SelectionTarget.CompanionMember(t, sym: BlockMemberSymbol) =>
sym.asTrm.map(constrain(_)).getOrElse(Nil)
case SelectionTarget.CompanionMember(t, sym) =>
Nil
case target :: targets => // ambiguous targets? but does it raise error?
log(s"Unresolved contextual call resolution for ${t.showDbg} due to ambiguous targets ${targets.mkString(", ")}")
Nil // TODO
case Nil => // insufficient targets
log(s"Unresolved contextual call resolution for ${t.showDbg} due to insufficient targets")
Nil

case t => // unsupported form
Nil

def expandContextualCall(t: Resolvable): Unit = t.resolvedContextuals match
case S(cs) =>
cs.foreach:
case (typ, placeholder) if placeholder.solution.isEmpty =>
val solution = t.ictx.getOrElse(die).query(typ).getOrElse(die)
placeholder.solution = S(solution.sym.ref())
case (typ, placeholder) => ()
t.expand(S(App(t.duplicate, Tup(
cs.map:
case (typ, placeholder) => PlainFld(placeholder.solution.get)
)(Tree.DummyTup))(Tree.DummyApp, N, FlowSymbol.app())))
case N => die

def getCompanionMember(name: Str, oc: Opt[Ctx], sym: Symbol): Opt[(Term, BlockMemberSymbol)] = sym match
case ms: ModuleOrObjectSymbol => ms.defn.flatMap: d =>
d.body.members.get(name) match
Expand Down Expand Up @@ -365,7 +456,7 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx):
dig(cp.ctor, rhs, cp.path ++ path)
sym.outFlows.foreach: fs =>
dig(P.Flow(fs), rhs, fs +: path)
case (P.Fun(pl, pr, _), C.Fun(cl, cr)) =>
case (P.Fun(pl, pr, _, _ctx), C.Fun(cl, cr)) =>
dig(cl, pl, path) // FIXME path
dig(pr, cr, path) // FIXME path
case (P.Ctor(sym1, args1), C.Ctor(sym2, args2))
Expand Down Expand Up @@ -398,6 +489,7 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx):
sel.trm.resolvedTargets ::= SelectionTarget.CompanionMember(path, memb)
log(s"Found member ${memb}")
toSolve.push(Constraint(P.Flow(memb.flow), C.Flow(trm.resSym)))
constrainContextualCall(sel.trm).foreach(toSolve.push(_))
case _ =>
log(s"Could not find member ${trm.nme.name} in ${sym}")
case _ => log("Unhandled RHS for leading dot selections")
Expand All @@ -413,6 +505,7 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx):
sel.trm.resolvedTargets ::= SelectionTarget.ObjectMember(memb)
log(s"Found immediate member ${memb}")
toSolve.push(Constraint(P.Flow(memb.flow), sel.res))
constrainContextualCall(sel.trm).foreach(toSolve.push(_))
case S(memb) => TODO(memb)
case N =>
d.moduleCompanion match
Expand All @@ -432,6 +525,7 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx):
case memb: BlockMemberSymbol => P.Flow(memb.flow)
case _ => TODO(memb)
toSolve.push(Constraint(newlhs, C.Fun(P.Tup((N, lhs) :: Nil), sel.res)))
constrainContextualCall(sel.trm).foreach(toSolve.push(_))
case N => raise:
sel.trm.isErroneous = true
ErrorReport:
Expand Down
Loading
Loading