diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala index 8814e38c62..f8e72bb404 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala @@ -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 @@ -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) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala index d0101b003c..79b86d9817 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala @@ -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 => @@ -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 @@ -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, @@ -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) @@ -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 diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/Constraint.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/Constraint.scala index cd2a23a0bd..ead7fea46b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/Constraint.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/Constraint.scala @@ -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) @@ -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()}" @@ -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(", ")}]" @@ -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 diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala index eeede5a2c1..3f68d9588f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala @@ -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 @@ -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 @@ -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)) @@ -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 => @@ -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: @@ -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 @@ -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)) @@ -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") @@ -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 @@ -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: diff --git a/hkmc2/shared/src/test/mlscript/flows/Contextuals.mls b/hkmc2/shared/src/test/mlscript/flows/Contextuals.mls new file mode 100644 index 0000000000..cc9fe6c3aa --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/flows/Contextuals.mls @@ -0,0 +1,136 @@ +:js +:flow +:sf + +//│ Flowed: +//│ import ".../Predef.mjs" as Predef⁰ +//│ where +//│ + + +fun f(using x: Int) = x + 1 +//│ Flowed: +//│ fun f⁰ctx (x⁰: Int⁰{ ~> Int⁰ }) ‹flow:f⁰› = +(x⁰, 1)‹app⁰› +//│ where +//│ x⁰ <~ type Int +//│ flow:f⁰ <~ ((type Int) -> app⁰) + +using Int = 42 +//│ Flowed: +//│ using instance$Ident(Int)⁰: Int⁰{ ~> Int⁰ } ‹flow:instance$Ident(Int)⁰› = 42 +//│ where +//│ flow:instance$Ident(Int)⁰ <~ 42 + + +:df +f +//│ Typing producer: { member:f } +//│ | Typing producer: member:f‹term:f› +//│ | | Typing producer: member:f +//│ | | : flow:f‹555› +//│ | | Attempting to constrain contextual call: member:f‹term:f› +//│ | | Constraining contextual call member:f‹term:f› +//│ | : flow:f‹555› +//│ : flow:f‹555› +//│ Handling constraint: using-x-flow‹566› <: type class:Int (from member:f‹term:f›) +//│ | Solving: using-x-flow‹566› <: type class:Int (Flow, Typ) +//│ | New flow using-x-flow ~> Typ(Ref(class:Int,List())) +//│ Flowed: +//│ f⁰{ ~> f⁰(instance$Ident(Int)⁰)‹app¹› } +//│ where +//│ +//│ = 43 + +fun id(x) = x +//│ Flowed: +//│ fun id⁰(x¹) ‹flow:id⁰› = x¹ +//│ where +//│ flow:id⁰ <~ ((x¹) -> x¹) + +class Foo with + fun f(using x: Int) = x + 1 +//│ Flowed: +//│ class Foo { +//│ fun f¹ctx (x²: Int⁰{ ~> Int⁰ }) ‹flow:f¹› = +(x², 1)‹app²› +//│ } +//│ where +//│ x² <~ type Int +//│ flow:f¹ <~ ((type Int) -> app²) + +:df +(new Foo).f +//│ Typing producer: { new member:Foo.f } +//│ | Typing producer: new member:Foo.f‹term:class:Foo.f› +//│ | | Typing producer: new member:Foo.f‹member:f› +//│ | | | Attempting to constrain contextual call: new member:Foo.f‹member:f› +//│ | | | Selection new member:Foo.f‹member:f› None +//│ | | | Typing producer: new member:Foo +//│ | | | : Foo +//│ | | | RES f in new member:Foo.f‹member:f› +//│ | | : flow:f‹587› +//│ | | Attempting to constrain contextual call: new member:Foo.f‹term:class:Foo.f› +//│ | | Constraining contextual call new member:Foo.f‹term:class:Foo.f› +//│ | : flow:f‹587› +//│ : flow:f‹587› +//│ Handling constraint: using-x-flow‹594› <: type class:Int (from new member:Foo.f‹term:class:Foo.f›) +//│ | Solving: using-x-flow‹594› <: type class:Int (Flow, Typ) +//│ | New flow using-x-flow ~> Typ(Ref(class:Int,List())) +//│ Flowed: +//│ new Foo⁰.f‹?›{ ~> new Foo⁰.f¹(instance$Ident(Int)⁰)‹app³› } +//│ where +//│ +//│ = 43 + + +:df +id(new Foo).f +//│ Typing producer: { member:id(new member:Foo).f } +//│ | Typing producer: member:id(new member:Foo).f +//│ | | Attempting to constrain contextual call: member:id(new member:Foo).f +//│ | | Unresolved contextual call resolution for member:id(new member:Foo).f due to insufficient targets +//│ | | Selection member:id(new member:Foo).f None +//│ | | Typing producer: member:id(new member:Foo) +//│ | | | Attempting to constrain contextual call: member:id(new member:Foo) +//│ | | | Typing producer: member:id‹term:id› +//│ | | | | Typing producer: member:id +//│ | | | | : flow:id‹575› +//│ | | | | Attempting to constrain contextual call: member:id‹term:id› +//│ | | | : flow:id‹575› +//│ | | | Typing producer: [new member:Foo] +//│ | | | | Typing producer: new member:Foo +//│ | | | | : Foo +//│ | | | : [Foo] +//│ | | : app‹599› +//│ | : ⋅f‹600› +//│ : ⋅f‹600› +//│ Handling constraint: flow:id‹575› <: ((Foo) -> app‹599›) (from member:id(new member:Foo)) +//│ | Solving: flow:id‹575› <: ((Foo) -> app‹599›) (Flow, Fun) +//│ | New flow flow:id ~> Fun(Tup(List((None,Ctor(class:Foo,List())))),Flow(app)) +//│ | Solving: ((x‹571›) -> x‹571›) <: ((Foo) -> app‹599›) (Fun, Fun) +//│ | Solving: [Foo] <: [x‹571›] (Tup, Tup) +//│ | Solving: Foo <: x‹571› (Ctor, Flow) +//│ | New flow Ctor(class:Foo,List()) ~> x +//│ | Solving: x‹571› <: app‹599› (Flow, Flow) +//│ | New flow x ~> app +//│ | Solving: Foo <: app‹599› (Ctor, Flow) +//│ | New flow Ctor(class:Foo,List()) ~> app +//│ Handling constraint: app‹599› <: {f: ⋅f‹600›} (from member:id(new member:Foo).f) +//│ | Solving: app‹599› <: {f: ⋅f‹600›} (Flow, Sel) +//│ | New flow app ~> Sel(Ident(f),Flow(⋅f)) +//│ | Solving: Foo <: {f: ⋅f‹600›} (Ctor, Sel) +//│ | Found immediate member member:f +//│ | Attempting to constrain contextual call: member:id(new member:Foo).f +//│ | Resolved contextual call resolution for member:id(new member:Foo).f to target ObjectMember(member:f) +//│ | Constraining contextual call member:id(new member:Foo).f +//│ | Solving: using-x-flow‹601› <: type class:Int (Flow, Typ) +//│ | New flow using-x-flow ~> Typ(Ref(class:Int,List())) +//│ | Solving: flow:f‹587› <: ⋅f‹600› (Flow, Flow) +//│ | New flow flow:f ~> ⋅f +//│ | Solving: ((type class:Int) -> app‹584›) <: ⋅f‹600› (Fun, Flow) +//│ | New flow Fun(Tup(List(Typ(Ref(class:Int,List()))),None),Flow(app),List(),false) ~> ⋅f +//│ Resolved targets for member:id(new member:Foo).f: ObjectMember(member:f) +//│ Flowed: +//│ id⁰(new Foo⁰)‹app⁴›.f¹(instance$Ident(Int)⁰)‹app⁵› +//│ where +//│ +//│ = 43 diff --git a/hkmc2/shared/src/test/mlscript/flows/ContextualsBasics.mls b/hkmc2/shared/src/test/mlscript/flows/ContextualsBasics.mls new file mode 100644 index 0000000000..a8767db028 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/flows/ContextualsBasics.mls @@ -0,0 +1,154 @@ +:js +:flow + + +fun id(x) = x + + +using Int = 42 + + +fun f(using x: Int) = x + 1 + +:expect 43 +:sf +f +//│ Flowed: +//│ f⁰{ ~> f⁰(instance$Ident(Int)⁰)‹app⁰› } +//│ where +//│ +//│ = 43 + + + +class Foo with + fun f(using x: Int) = x + 1 +module Foo with + fun g(foo: Foo)(using x: Int) = x + 2 + + + +:expect 43 +:sf +(new Foo).f +//│ Flowed: +//│ new Foo⁰.f‹?›{ ~> new Foo⁰.f¹(instance$Ident(Int)⁰)‹app¹› } +//│ where +//│ +//│ = 43 + + +:todo +:expect 44 +:sf +(new Foo).g +//│ Flowed: +//│ new Foo⁰.g‹?›{ ~> Foo⁰.g⁰(new Foo⁰)‹app²› } +//│ where +//│ +//│ ═══[RUNTIME ERROR] Expected: '44', got: 'fun' +//│ = fun + + +:expect 43 +:sf +id(new Foo).f +//│ Flowed: +//│ id⁰(new Foo⁰)‹app³›.f¹(instance$Ident(Int)⁰)‹app⁴› +//│ where +//│ +//│ = 43 + + +:todo +:expect 44 +:sf +id(new Foo).g +//│ ╔══[ERROR] Ambiguous selection with multiple apparent targets +//│ ║ l.66: id(new Foo).g +//│ ║ ^^^^^^^^^^^^^ +//│ ╟── companion member g +//│ ║ l.27: fun g(foo: Foo)(using x: Int) = x + 2 +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── companion member g +//│ ║ l.27: fun g(foo: Foo)(using x: Int) = x + 2 +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ Flowed: +//│ id⁰{ ~> id⁰ }(new Foo⁰)‹app⁵›.g‹?› +//│ where +//│ app⁵ <~ Foo Foo +//│ app⁵ ~> {g: ⋅g⁰} +//│ ═══[RUNTIME ERROR] Error: Access to required field 'g' yielded 'undefined' +//│ ═══[RUNTIME ERROR] Expected: '44', got: 'undefined' + + +:expect 43 +:sf +let foo = new Foo +foo.f +//│ Flowed: +//│ let foo⁰, +//│ foo⁰ = new Foo⁰, +//│ foo⁰.f¹(instance$Ident(Int)⁰)‹app⁶› +//│ where +//│ foo⁰ <~ Foo +//│ foo⁰ ~> {f: ⋅f⁰} +//│ = 43 +//│ foo = Foo + + +:todo +:expect 44 +:sf +let foo = new Foo +foo.g +//│ Flowed: +//│ let foo¹, +//│ foo¹ = new Foo⁰, +//│ foo¹.g‹?›{ ~> Foo⁰.g⁰(foo¹)‹app⁷› } +//│ where +//│ foo¹ <~ Foo +//│ foo¹ ~> {g: ⋅g¹} +//│ ═══[RUNTIME ERROR] Expected: '44', got: 'fun' +//│ = fun +//│ foo = Foo + + +:expect 43 +:sf +fun bar(foo) = + foo.f +bar(foo) +//│ Flowed: +//│ fun bar⁰(foo²) ‹flow:bar⁰› = foo².f¹(instance$Ident(Int)⁰)‹app⁸›, +//│ bar⁰{ ~> bar⁰ }(foo¹)‹app⁹› +//│ where +//│ foo¹ <~ Foo +//│ foo¹ ~> {g: ⋅g¹} +//│ foo¹ -> foo² +//│ foo² <~ Foo +//│ foo² ~> {f: ⋅f¹} +//│ flow:bar⁰ <~ ((foo²) -> ⋅f¹) +//│ flow:bar⁰ ~> ((foo¹) -> app⁹) +//│ = 43 + + +:todo +:expect 44 +:sf +fun bar(foo) = + foo.g +bar(foo) +//│ Flowed: +//│ fun bar¹(foo³) ‹flow:bar¹› = foo³.g‹?›{ ~> Foo⁰.g⁰(foo³)‹app¹⁰› }, +//│ bar¹{ ~> bar¹ }(foo¹)‹app¹¹› +//│ where +//│ foo¹ <~ Foo +//│ foo¹ ~> {g: ⋅g¹} +//│ foo¹ -> foo² foo³ +//│ foo³ <~ Foo +//│ foo³ ~> {g: ⋅g²} +//│ flow:bar¹ <~ ((foo³) -> ⋅g²) +//│ flow:bar¹ ~> ((foo¹) -> app¹¹) +//│ ═══[RUNTIME ERROR] Expected: '44', got: 'fun' +//│ = fun