Skip to content

Improve type inference of function calls by propagating expectations#1317

Open
jiribenes wants to merge 7 commits intomainfrom
typer/fn-call-hint
Open

Improve type inference of function calls by propagating expectations#1317
jiribenes wants to merge 7 commits intomainfrom
typer/fn-call-hint

Conversation

@jiribenes
Copy link
Contributor

@jiribenes jiribenes commented Mar 6, 2026

Improves the error message of #1224 by propagating more information for boxed functions whenever possible, specifically when we know the expected type of the call, but were previously too shy to try and apply it:

type Status[B] {
  Done(value: Int)
  More(state: B)
}

def ex1(): Unit = {
  val res: Int = Done(123) match {
  //     ~~~~~ 
  // this is enough to help resolve the issue
    case Done(value) => value
    case More(task) => task()
  }
  println(res)
}

def ex2() =
  println(Done(123) match {
  // the println is enough to force it to be a number
    case Done(value) => value
    case More(task) => task()
  })

def original_issue1224(): Unit = {
  // still fails with "Unbox requires a boxed type, but got B." on the `task()` call
  Done(123) match {
    case Done(value) => value
    case More(task) => task()
  }
  ()
}

def another_fails(): Unit = {
  val result: Int = Done(123) match {
    case Done(v) => v
    // f() checked with expected = None
    case More(f) => f() match { // still fails with "Unbox requires a boxed type, but got B."
      case n => n
    }
  }
  result
}

@jiribenes jiribenes requested a review from phischu March 6, 2026 14:55
@jiribenes
Copy link
Contributor Author

@phischu if you still remember the original problem that inspired the issue, does this fix solve it?

@jiribenes jiribenes changed the title Improve type inference of function calls by propagating more info Improve type inference of function calls by propagating expectations Mar 6, 2026
@jiribenes jiribenes requested a review from b-studios March 6, 2026 14:59
@jiribenes jiribenes marked this pull request as draft March 6, 2026 15:04
expectedCallHint match {
case Some((arity, retTpe)) =>
val freshVps = List.fill(arity) {
ValueTypeRef(Context.freshTypeVar(TypeParam(Name.local("_")), u))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still a bit worried about this _ leaking (again), I'm trying to fake "just enough" block unification to get by here...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that seems to have broken #1315 :(
Screenshot 2026-03-06 at 17 18 18

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a way, the unexpected error message is better, but I don't want to have ad-hoc "anything fits here" type variables without them having some reasonable semantics a la #276.

@jiribenes jiribenes marked this pull request as ready for review March 6, 2026 15:32
@jiribenes jiribenes force-pushed the typer/fn-call-hint branch from 4081307 to 71e5cf7 Compare March 6, 2026 15:57
@jiribenes
Copy link
Contributor Author

Rebased on top of 117f720 (from merging #1315).

@phischu
Copy link
Collaborator

phischu commented Mar 7, 2026

No, this does not fix #1224. The following type checks:

def main(): Unit = {
  Done[() => Int at {}](123) match {
    case Done(value) => value
    case More(task) => task()
  }
  ()
}

But in the following:

def main(): Unit = {
  Done[String](123) match {
    case Done(value) => value
    case More(task) => task()
  }
  ()
}

I get:

Expected task to be a function, but got a value of type B instead, which cannot be called as a function.

What I would want is

Expected task to be a function, but got a value of type String instead, which cannot be called as a function.

So we are forgetting to do index unification in matches or something.

@jiribenes
Copy link
Contributor Author

jiribenes commented Mar 7, 2026

Yeah, I know it doesn't fix #1224, I was just trying to improve the inference when you annotate at least something. But good point, I can take a look at the trace again and see if I can come up with some other trick — the issue here is that we never unify block types... (so we would derive the B ~ String fact too late, only after the err)

@phischu
Copy link
Collaborator

phischu commented Mar 7, 2026

No need to fix #1224, I misunderstood. I am the wrong person to review this, but I'll just approve anyway.

Copy link
Collaborator

@phischu phischu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing controversial

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants