Skip to content

Conversation

@abs0luty
Copy link
Contributor

@abs0luty abs0luty commented Oct 24, 2025

💡 Explanation
pub type Test(a) {
  Test(a)
}

pub fn it(value: Test(a)) {
  it2(value)
}

pub fn it2(value: Test(a)) -> Test(a) {
  it(value)
}

pub fn main() {
  it(Test(1))
}

Before the patch the compiler registered a generic "sketch" for each function, then tried to compare the sketch directly to the inferred body types. That made the generic variables act rigid in the check and produced a false mismatch in mutually recursive code.

startup
  |
  v
preregister function sketches in env
  it  : fn(Test(a)) -> Test(a)
  it2 : fn(Test(a)) -> Test(a)

main()
  |
  v
Test(1) : Test(Int)
  |
  v
call it(Test(Int))
  |
  v
type of it looked up from env  -----------------------------> fn(Test(a)) -> Test(a)
  |                                                           (generic, rigid at check)
  v
inside it body, call it2(value)
  |
  v
finish it2
  |
  v
last expr type from body ----------------------------------> Test(Int)
  |
  v
direct compare without instantiation ----------------------> expected: Test(a)
                                                             found:    Test(Int)
                                                             result:   a behaves rigid
                                                                       unification fails
Environment remains with preregistered sketches
Error bubbles as false mismatch

Now the compiler instantiates annotations and preregistered types before unification, and it records the inferred result in the environment so later checks see the concrete shape.

startup
  |
  v
preregister sketches in env
  it  : fn(Test(a)) -> Test(a)
  it2 : fn(Test(a)) -> Test(a)

main()
  |
  v
Test(1) : Test(Int)
  |
  v
need type of it(Test(Int))  ------------------------------+
  |                                                       |
  v                                                       |
infer it body                                             |
  |                                                       |
  v                                                       |
call it2(value)                                           |
  |                                                       |
  v                                                       |
flow back provisional result from use of value -----------+--> Test(Int)
  |                                                       |
  v                                                       |
inferred type for it becomes -----------------------------+--> fn(Test(Int)) -> Test(Int)
  |                                                       |
  v                                                       |
store inferred type in env (overwrite sketch) ------------+--> it : fn(Test(Int)) -> Test(Int)

infer it2
  |
  v
last expr is it(value)
  |
  v
from env, type of it(value) -------------------------------> Test(Int)
  |
  v
check it2 return annotation
  |
  v
instantiate annotation Test(a) ----------------------------> Test(a₁)
  |
  v
unify Test(a₁) with Test(Int)  ============================= a₁ := Int, success
  |
  v
store inferred type in env --------------------------------> it2 : fn(Test(Int)) -> Test(Int)

finish main
  |
  v
it(Test(1)) : Test(Int)

no mismatch, because:
- comparisons use instantiated annotations
- recursion observes concrete env entries rather than generic sketches
- typed AST keeps the inferred return type

@abs0luty abs0luty changed the title Preserve generic type parameters when constructors or functions are used without explicit annotations Preserve generic type parameters when constructors, functions are used without explicit annotations Oct 24, 2025
@abs0luty abs0luty changed the title Preserve generic type parameters when constructors, functions are used without explicit annotations Preserve generic parameters when constructors, functions are used without explicit annotations Oct 24, 2025
@abs0luty abs0luty force-pushed the fix-type-inference branch 2 times, most recently from 96d4132 to 980e01e Compare October 24, 2025 17:37
@abs0luty abs0luty changed the title Preserve generic parameters when constructors, functions are used without explicit annotations Fix type inference for functions, constructors without explicit annotations. Oct 24, 2025
@abs0luty abs0luty changed the title Fix type inference for functions, constructors without explicit annotations. Fix type inference for functions, constructors without explicit annotations Oct 24, 2025
@abs0luty abs0luty force-pushed the fix-type-inference branch 3 times, most recently from 0761e59 to d1a7ec5 Compare October 31, 2025 06:19
@abs0luty abs0luty changed the title Fix type inference for functions, constructors without explicit annotations Fix generic inference by instantiating annotations and storing inferred types Nov 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Type inference issue expected Test(a) but found Test(a) Unbound type variable in top level definition after type inference

2 participants