Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lost type information after two type projections #3443

Closed
scabug opened this issue May 17, 2010 · 4 comments
Closed

lost type information after two type projections #3443

scabug opened this issue May 17, 2010 · 4 comments

Comments

@scabug
Copy link

scabug commented May 17, 2010

I have a trait for a type-level function:

trait TFn1[I, O] {
  type app[i <: I] <: O
}

and I'm trying to write bind/flatMap for it. The following is a simplified example that gives me the error I'm getting:

trait TFn1B[T] {
  type x[a, b, f <: TFn1[a, TFn1[T, b]] ] =
    TFn1[T, b] {
      type app[s <: T] = f#app[a]#app[s]
    }
}

The error is:

type arguments [s] do not conform to type app's type parameter bounds [i <: I]
           TFn1[T, b] {
           ^

I think the error message is for the app[s] part despite where ^ points. It looks like scalac doesn't know that:

  f#app[a] <: TFn1[T, b]

which should mean that s <: I since I is T and s <: T.

It seems to me that the code should be accepted but maybe some type information gets lost with the second projection.

This is in 2.8.0.RC2. As Eric Willigers pointed out on the mailing list, this code is accepted in 2.7.x

@scabug
Copy link
Author

scabug commented May 17, 2010

Imported From: https://issues.scala-lang.org/browse/SI-3443?orig=1
Reporter: @harrah

@scabug
Copy link
Author

scabug commented May 18, 2010

@odersky said:
There was some tightening of the rules between 2.7 and 2.8. I am pretty sure the new rules are a better approximation to safety than the old ones. To re-open the ticket, I'd need a one-by-one argument why this code should be accepted. Note that

A # B is now equivalent to (x.B forSome x: A)

@scabug
Copy link
Author

scabug commented May 18, 2010

@harrah said:
Ok, but are they supposed to be equivalent all of the time?

Using TFn1 from above:

trait TFn1[I, O] {
  type app[i <: I] <: O
}

B and B2 below should be equivalent, right?

trait Test {
  type a
  type b
  type f = TFn1[a, b]

   // compiles,
   //  browsing the tree shows TypeRefs only
  type B = f#app[a]

   // should be equivalent to B?
   //  does not compile though,
   //  tree shows Existential as expected
  type B2 = x.app[a] forSome { val x: f }
}

The compile error related to B2 is the same as in the original report:

type arguments [Test.this.a] do not conform to type app's type parameter bounds [i <: I]
        type B = x.app[a] forSome { val x: f }
                          ^

@scabug
Copy link
Author

scabug commented Jan 20, 2011

@adriaanm said:
(In r24029) introduce NullaryMethodType to disambiguate PolyType

motivation:
given def foo[T]: (T, T) and type Foo[T] = (T, T),

foo.info and TypeRef(_, Foo, Nil).normalize are both PolyType(List(T), Pair[T, T])

uncurry has been relying on an ugly hack to distinguish these cases based on ad-hoc kind inference
without this distinction, the type alias's info (a type function) would be transformed to PolyType(List(T), MethodType(Nil, Pair[T, T]))

anonymous type functions are being used more often (see #2741, #4017, #4079, #3443, #3106), which makes a proper treatment of PolyTypes more pressing

change to type representation:
PolyType(Nil, tp) -> NullaryMethodType(tp)
PolyType(tps, tp) -> PolyType(tps, NullaryMethodType(tp)) (if the polytype denoted a polymorphic nullary method)

PolyType(Nil, tp) is now invalid

the kind of a PolyType is * iff its resulttype is a NullaryMethodType or a MethodType (i.e., it's a polymorphic value)
in all other cases a PolyType now denotes a type constructor

NullaryMethodType is eliminated during uncurry

pickling:
for backwards compatibility, a NullaryMethodType(tp) is still pickled as a PolyType(Nil, tp),
unpickling rewrites pre-2.9-pickled PolyTypes according to the expected kind of the unpickled type (similar to what we used to do in uncurry)
a pickled PolyType(Nil, restpe) is unpickled to NullaryMethodType(restpe)
a pickled PolyType(tps, restpe) is unpickled to PolyType(tps, NullaryMethodType(restpe)) when the type is expected to have kind *

the rewrite probably isn't complete, but was validated by compiling against the old scalacheck jar (which has plenty of polymorphic nullary methods)
nevertheless, this commit includes a new scalacheck jar

summary of the refactoring:

  • PolyType(List(), tp) or PolyType(Nil, tp) or PolyType(parms, tp) if params.isEmpty ==> NullaryMethodType(tp)

  • whenever there was a case PolyType(tps, tp) (irrespective of tps isEmpty), now need to consider the
    case PolyType(tps, NullaryMethodType(tp)); just add a case NullaryMethodType(tp), since usually:

    • there already is a PolyType case that recurses on the result type,
    • the polytype case applied to empty and non-empty type parameter lists alike
  • tp.resultType, where tp was assumed to be a PolyType that represents a polymorphic nullary method type
    before, tp == PolyType(tps, res), now tp == PolyType(tps, NullaryMethodType(res))

  • got bitten again (last time was dependent-method types refactoring) by a TypeMap not being the identity when dropNonConstraintAnnotations is true (despite having an identity apply method). Since asSeenFrom is skipped when isTrivial, the annotations aren't dropped. The cps plugin relies on asSeenFrom dropping these annotations for trivial types though. Therefore, NullaryMethodType pretends to never be trivial. Better fix(?) in AsSeenFromMap: if(tp.isTrivial) dropNonContraintAnnotations(tp) else ...

TODO: scalap and eclipse

review by odersky, rytz

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

No branches or pull requests

1 participant