Skip to content

Type parameters are instantiated too early in some cases #738

Closed
@smarter

Description

@smarter

When a type parameter is used in the second parameter list of a method, the inference is not precise enough, unless it also part of the return type:

object Test {
  def one[T](ev: T): Nothing = ???
  def two1[T](ev: T)(): Nothing = ???
  def two2a[T]()(ev: T): Nothing = ???
  def two2b[T]()(ev: T): T = ???
  def two3a[T](ev: T)(ev2: T): Nothing = ???
  def two3b[T](ev: T)(ev2: T): T = ???

  def test = {
    one(42) // Correct: Infer T=Int
    two1(42)() // Correct: Infer T=Int
    two2a()(42) // Wrong: Infer T=Any
    two2b()(42) // Correct: Infer T=Int
    two3a(42)(42) // Wrong: Infer T=Any
    two3b(42)(42) // Correct: Infer T=Int
  }
}

Here's how one(42) is typed:

[log frontend]         ==> typing one(42)?
[log frontend]           ==> typing one?
typed ident one in method test
[log frontend]             ==> adapting Test.one of type ([T](ev: T)Nothing)(Test.one) to FunProto(42):??
[log frontend]             <== adapting Test.one of type ([T](ev: T)Nothing)(Test.one) to FunProto(42):? = Test.one[(T?)]
[log frontend]           <== typing Test.one = Test.one[(T?)]
[log frontend]           ==> adapting 42 of type Int(42) to (T?)?
adding constraint T >: Int(42) to
Constraint(
 uninstVars = (T?);
 constrained types = [T](ev: T)Nothing
 bounds = 
     T
 ordering = 
)
added constraint T >: Int(42) to
Constraint(
 uninstVars = (T? >: Int(42));
 constrained types = [T](ev: T)Nothing
 bounds = 
     T >: Int(42)
 ordering = 
) = true
[log frontend]           <== adapting 42 of type Int(42) to (T? >: Int(42)) = 42
[log frontend]           ==> adapting Test.one[(T? >: Int(42))](42) of type Nothing to ??
interpolate undet vars in Nothing, pos = [263..266..270], mode = Mode(ImplicitsEnabled,InferringReturnType), undets = ArrayBuffer((T? >: Int(42))@[263..266])
qualifying undet vars: ArrayBuffer(TypeVar(PolyParam(T)) / (T? >: Int(42))), constraint: Constraint(
 uninstVars = (T? >: Int(42));
 constrained types = [T](ev: T)Nothing
 bounds = 
     T >: Int(42)
 ordering = 
)
instantiating non-occurring (T? >: Int(42)) in Nothing
approx T, from below = true, bound = Int(42), inst = Int(42)
instantiating (T? >: Int(42)) with Int
[log frontend]           <== adapting Test.one[Int'](42) of type Nothing to ? = Test.one[Int'](42)
[log frontend]         <== typing Test.one(42) = Test.one[Int'](42)

Here's how two2a()(42) is typed:

[log frontend]         ==> typing two2a()(42)?
[log frontend]           ==> typing two2a()?
[log frontend]             ==> typing two2a?
typed ident two2a in method test
[log frontend]               ==> adapting Test.two2a of type ([T]()(ev: T)Nothing)(Test.two2a) to FunProto():??
[log frontend]               <== adapting Test.two2a of type ([T]()(ev: T)Nothing)(Test.two2a) to FunProto():? = Test.two2a[(T?)]
[log frontend]             <== typing Test.two2a = Test.two2a[(T?)]
[log frontend]             ==> adapting Test.two2a[(T?)]() of type (ev: (T?))Nothing to FunProto(42):??
interpolate undet vars in (ev: (T?))Nothing, pos = [334..339..341], mode = Mode(ImplicitsEnabled,InferringReturnType), undets = ArrayBuffer((T?)@[334..339])
qualifying undet vars: ArrayBuffer(TypeVar(PolyParam(T)) / (T?)), constraint: Constraint(
 uninstVars = (T?);
 constrained types = [T]()(ev: T)Nothing
 bounds = 
     T
 ordering = 
)
interpolate contravariant (T?) in (ev: (T?))Nothing
approx T, from below = false, bound = Any, inst = Any
instantiating (T?) with Any
[log frontend]             <== adapting Test.two2a[Any']() of type (ev: Any)Nothing to FunProto(42):? = Test.two2a[Any']()
[log frontend]           <== typing Test.two2a() = Test.two2a[Any']()
[log frontend]           ==> adapting 42 of type Int(42) to Any?
[log frontend]           <== adapting 42 of type Int(42) to Any = 42
[log frontend]           ==> adapting Test.two2a[Any']()(42) of type Nothing to ??
[log frontend]           <== adapting Test.two2a[Any']()(42) of type Nothing to ? = Test.two2a[Any']()(42)
[log frontend]         <== typing Test.two2a()(42) = Test.two2a[Any']()(42)

And here's how two2b()(42) is typed:

[error] [log frontend]         ==> typing two2b()(42)?
[error] [log frontend]           ==> typing two2b()?
[error] [log frontend]             ==> typing two2b?
[info] typed ident two2b in method test
[error] [log frontend]               ==> adapting Test.two2b of type ([T]()(ev: T)T)(Test.two2b) to FunProto():??
[error] [log frontend]               <== adapting Test.two2b of type ([T]()(ev: T)T)(Test.two2b) to FunProto():? = Test.two2b[(T?)]
[error] [log frontend]             <== typing Test.two2b = Test.two2b[(T?)]
[error] [log frontend]             ==> adapting Test.two2b[(T?)]() of type (ev: (T?))(T?) to FunProto(42):??
[info] interpolate undet vars in (ev: (T?))(T?), pos = [370..375..377], mode = Mode(ImplicitsEnabled,InferringReturnType), undets = ArrayBuffer((T?)@[370..375])
[info] qualifying undet vars: ArrayBuffer(TypeVar(PolyParam(T)) / (T?)), constraint: Constraint(
[info]  uninstVars = (T?);
[info]  constrained types = [T]()(ev: T)T
[info]  bounds = 
[info]      T
[info]  ordering = 
[info] )
[error] [log frontend]             <== adapting Test.two2b[(T?)]() of type (ev: (T?))(T?) to FunProto(42):? = Test.two2b[(T?)]()
[error] [log frontend]           <== typing Test.two2b() = Test.two2b[(T?)]()
[error] [log frontend]           ==> adapting 42 of type Int(42) to (T?)?
[info] adding constraint T >: Int(42) to
[info] Constraint(
[info]  uninstVars = (T?);
[info]  constrained types = [T]()(ev: T)T
[info]  bounds = 
[info]      T
[info]  ordering = 
[info] )
[info] added constraint T >: Int(42) to
[info] Constraint(
[info]  uninstVars = (T? >: Int(42));
[info]  constrained types = [T]()(ev: T)T
[info]  bounds = 
[info]      T >: Int(42)
[info]  ordering = 
[info] ) = true
[error] [log frontend]           <== adapting 42 of type Int(42) to (T? >: Int(42)) = 42
[error] [log frontend]           ==> adapting Test.two2b[(T? >: Int(42))]()(42) of type (T? >: Int(42)) to ??
[info] interpolate undet vars in (T? >: Int(42)), pos = [370..377..381], mode = Mode(ImplicitsEnabled,InferringReturnType), undets = ArrayBuffer((T? >: Int(42))@[370..375])
[info] qualifying undet vars: ArrayBuffer(TypeVar(PolyParam(T)) / (T? >: Int(42))), constraint: Constraint(
[info]  uninstVars = (T? >: Int(42));
[info]  constrained types = [T]()(ev: T)T
[info]  bounds = 
[info]      T >: Int(42)
[info]  ordering = 
[info] )
[info] interpolate covariant (T? >: Int(42)) in (T? >: Int(42))
[info] approx T, from below = true, bound = Int(42), inst = Int(42)
[info] instantiating (T? >: Int(42)) with Int
[error] [log frontend]           <== adapting Test.two2b[Int']()(42) of type Int to ? = Test.two2b[Int']()(42)
[error] [log frontend]         <== typing Test.two2b()(42) = Test.two2b[Int']()(42)

When we type one(42):

  • First we adapt Test.one to FunProto(42), this does not add any additional constraint or instantiate any type variable
  • Then we adapt 42 to T, this adds a constraint >: Int on T
  • Then we adapt Test.one[T](42) to ??, this instantiates T to Int, as expected

But when we type two2a()(42):

  • First we adapt Test.two2a to FunProto(), this does not add any additional constraint or instantiate any type variable
  • Then we adapt Test.two2a[T]() to FunProto(42), this interpolates T to Any because T appears contravariantly in the method type, which is not what we wanted!
  • Then we adapt 42 to Any, this does not add any additional constraint or instantiate any type variable
  • Then we adapt Test.two2a[Any]()(42) to ??, this does not add any additional constraint or instantiate any type variable

And when we type two2b()(42):

  • First we adapt Test.two2b to FunProto(), this does not add any additional constraint or instantiate any type variable
  • Then we adapt Test.two2b[T]() to FunProto(42), this does not add any additional constraint or instantiate any type variable because T appears both contravariantly and covariantly in the method type
  • Then we adapt 42 to T, this adds a constraint >: Int on T
  • Then we adapt Test.two2b[T]()(42) to ??, this instantiates T to Int, as expected

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions