From e28c3edda4dd405ed382227d2a688b799bf33c72 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 11 May 2013 16:27:03 -0700 Subject: SI-1786 incorporate defined bounds in inference Also fixes SI-5459. Look, you don't have to redeclare the bounds, isn't it exciting? For instance, there are eight places in JavaMirrors with this: jTypeVariable[_ <: GenericDeclaration] After this code is in starr, those can look like this: jTypeVariable[_] Since TypeVariable's definition looks like this: interface TypeVariable We already know that! --- .../scala/tools/nsc/typechecker/Typers.scala | 54 ++++++++++++++++------ test/files/neg/t5687.check | 7 +-- test/files/pos/t1786.scala | 19 ++++++++ test/files/pos/t5459.scala | 48 +++++++++++++++++++ test/pending/pos/t1786.scala | 20 -------- test/pending/pos/t5459.scala | 48 ------------------- 6 files changed, 108 insertions(+), 88 deletions(-) create mode 100644 test/files/pos/t1786.scala create mode 100644 test/files/pos/t5459.scala delete mode 100644 test/pending/pos/t1786.scala delete mode 100644 test/pending/pos/t5459.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 001808e6bc..b41df05c3d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4867,19 +4867,23 @@ trait Typers extends Adaptations with Tags { } def typedAppliedTypeTree(tree: AppliedTypeTree) = { - val tpt = tree.tpt - val args = tree.args - val tpt1 = typed1(tpt, mode | FUNmode | TAPPmode, WildcardType) + val tpt = tree.tpt + val args = tree.args + val tpt1 = typed1(tpt, mode | FUNmode | TAPPmode, WildcardType) + def isPoly = tpt1.tpe.isInstanceOf[PolyType] + def isComplete = tpt1.symbol.rawInfo.isComplete + if (tpt1.isErrorTyped) { tpt1 } else if (!tpt1.hasSymbolField) { AppliedTypeNoParametersError(tree, tpt1.tpe) } else { val tparams = tpt1.symbol.typeParams + if (sameLength(tparams, args)) { // @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer) val args1 = - if (!tpt1.symbol.rawInfo.isComplete) + if (!isComplete) args mapConserve (typedHigherKindedType(_, mode)) // if symbol hasn't been fully loaded, can't check kind-arity else map2Conserve(args, tparams) { (arg, tparam) => @@ -4888,19 +4892,39 @@ trait Typers extends Adaptations with Tags { } val argtypes = args1 map (_.tpe) - foreach2(args, tparams)((arg, tparam) => arg match { - // note: can't use args1 in selector, because Bind's got replaced - case Bind(_, _) => - if (arg.symbol.isAbstractType) - arg.symbol setInfo // XXX, feedback. don't trackSymInfo here! - TypeBounds( - lub(List(arg.symbol.info.bounds.lo, tparam.info.bounds.lo.subst(tparams, argtypes))), - glb(List(arg.symbol.info.bounds.hi, tparam.info.bounds.hi.subst(tparams, argtypes)))) - case _ => - }) + foreach2(args, tparams) { (arg, tparam) => + // note: can't use args1 in selector, because Binds got replaced + val asym = arg.symbol + def abounds = asym.info.bounds + def tbounds = tparam.info.bounds + def enhanceBounds(): Unit = { + val TypeBounds(lo0, hi0) = abounds + val TypeBounds(lo1, hi1) = tbounds.subst(tparams, argtypes) + val lo = lub(List(lo0, lo1)) + val hi = glb(List(hi0, hi1)) + if (!(lo =:= lo0 && hi =:= hi0)) + asym setInfo logResult(s"Updating bounds of ${asym.fullLocationString} in $tree from '$abounds' to")(TypeBounds(lo, hi)) + } + if (asym != null && asym.isAbstractType) { + // See pos/t1786 to follow what's happening here. + def canEnhanceIdent = ( + asym.hasCompleteInfo + && tparam.exists /* sometimes it is NoSymbol */ + && tparam.hasCompleteInfo /* SI-2940 */ + && !tparam.isFBounded /* SI-2251 */ + && !tparam.isHigherOrderTypeParameter + && !(abounds.hi <:< tbounds.hi) + ) + arg match { + case Bind(_, _) => enhanceBounds() + case Ident(name) if canEnhanceIdent => enhanceBounds() + case _ => + } + } + } val original = treeCopy.AppliedTypeTree(tree, tpt1, args1) val result = TypeTree(appliedType(tpt1.tpe, argtypes)) setOriginal original - if(tpt1.tpe.isInstanceOf[PolyType]) // did the type application (performed by appliedType) involve an unchecked beta-reduction? + if (isPoly) // did the type application (performed by appliedType) involve an unchecked beta-reduction? TypeTreeWithDeferredRefCheck(){ () => // wrap the tree and include the bounds check -- refchecks will perform this check (that the beta reduction was indeed allowed) and unwrap // we can't simply use original in refchecks because it does not contains types diff --git a/test/files/neg/t5687.check b/test/files/neg/t5687.check index 5096077ee5..f8d02fdcc3 100644 --- a/test/files/neg/t5687.check +++ b/test/files/neg/t5687.check @@ -1,8 +1,5 @@ -t5687.scala:4: error: type arguments [T] do not conform to class Template's type parameter bounds [T <: AnyRef] - type Repr[T]<:Template[T] - ^ -t5687.scala:20: error: overriding type Repr in class Template with bounds[T] <: Template[T]; +t5687.scala:20: error: overriding type Repr in class Template with bounds[T <: AnyRef] <: Template[T]; type Repr has incompatible type type Repr = CurveTemplate[T] ^ -two errors found +one error found diff --git a/test/files/pos/t1786.scala b/test/files/pos/t1786.scala new file mode 100644 index 0000000000..22bd659609 --- /dev/null +++ b/test/files/pos/t1786.scala @@ -0,0 +1,19 @@ +class SomeClass(val intValue:Int) +class MyClass[T <: SomeClass](val myValue:T) +class Flooz[A >: Null <: SomeClass, T >: Null <: A](var value: T) + +class A { + def f1(i:MyClass[_]) = i.myValue.intValue + def f2(i:MyClass[_ <: SomeClass]) = i.myValue.intValue + def f3[T](i: MyClass[T]) = i.myValue.intValue + def f4[T <: SomeClass](i: MyClass[T]) = i.myValue.intValue + def f5[T >: Null](i: MyClass[T]) = i.myValue.intValue + def f6[T >: Null <: String](i: MyClass[T]) = i.myValue.intValue + i.myValue.charAt(0) + + def g1[A, T](x: Flooz[A, T]) = { x.value = null ; x.value.intValue } + def g2(x: Flooz[_, _]) = { x.value = null ; x.value.intValue } + + class MyClass2(x: MyClass[_]) { val p = x.myValue.intValue } + class MyClass3[T <: String](x: MyClass[T]) { val p = x.myValue.intValue + x.myValue.length } + class MyClass4[T >: Null](x: MyClass[T]) { val p = x.myValue.intValue } +} diff --git a/test/files/pos/t5459.scala b/test/files/pos/t5459.scala new file mode 100644 index 0000000000..971e6f896d --- /dev/null +++ b/test/files/pos/t5459.scala @@ -0,0 +1,48 @@ +trait A1 +trait A2 +trait A3 +trait L1 extends A1 with A2 with A3 + +object Test { + trait T1[-A <: A1] + trait T2[-A >: L1] + trait T3[ A <: A1] + trait T4[ A >: L1] + trait T5[+A <: A1] + trait T6[+A >: L1] + + def f1(x: T1[_]) = x + def f2(x: T2[_]) = x + def f3(x: T3[_]) = x + def f4(x: T4[_]) = x + def f5(x: T5[_]) = x + def f6(x: T6[_]) = x + // a.scala:22: error: type arguments [Any] do not conform to trait T5's type parameter bounds [+A <: A1] + // def f5(x: T5[_]) = x + // ^ + + def g1(x: T1[_ <: A1]) = x + def g2(x: T2[_ >: L1]) = x + def g3(x: T3[_ <: A1]) = x + def g4(x: T4[_ >: L1]) = x + def g5(x: T5[_ <: A1]) = x + def g6(x: T6[_ >: L1]) = x + + def q1(x: T1[_ >: L1]) = x + def q2(x: T2[_ <: A1]) = x + def q3(x: T3[_ >: L1]) = x + def q4(x: T4[_ <: A1]) = x + def q5(x: T5[_ >: L1]) = x + def q6(x: T6[_ <: A1]) = x + // a.scala:41: error: type arguments [Any] do not conform to trait T5's type parameter bounds [+A <: A1] + // def q5(x: T5[_ >: L1]) = x + // ^ + // two errors found + + def h1(x: T1[_ >: L1 <: A1]) = x + def h2(x: T2[_ >: L1 <: A1]) = x + def h3(x: T3[_ >: L1 <: A1]) = x + def h4(x: T4[_ >: L1 <: A1]) = x + def h5(x: T5[_ >: L1 <: A1]) = x + def h6(x: T6[_ >: L1 <: A1]) = x +} diff --git a/test/pending/pos/t1786.scala b/test/pending/pos/t1786.scala deleted file mode 100644 index dca2edaab4..0000000000 --- a/test/pending/pos/t1786.scala +++ /dev/null @@ -1,20 +0,0 @@ -/** This a consequence of the current type checking algorithm, where bounds - * are checked only after variables are instantiated. I believe this will change once we go to contraint-based type inference. Assigning low priority until then. - * - * - */ -class SomeClass(val intValue:Int) -class MyClass[T <: SomeClass](val myValue:T) - -object Test extends Application { - def myMethod(i:MyClass[_]) { - i.myValue.intValue/2 // << error i is of type Any - } - - def myMethod(i:MyClass[_ <: SomeClass]) { - i.myValue.intValue/2 // << works - } -} -/* -The below code shows a compiler flaw in that the wildcard "_" as value for a bounded type parameter either breaks the boundry - as it result in Any - or doesnt (as id hoped it to be) evaluates to the boundy. -*/ diff --git a/test/pending/pos/t5459.scala b/test/pending/pos/t5459.scala deleted file mode 100644 index 971e6f896d..0000000000 --- a/test/pending/pos/t5459.scala +++ /dev/null @@ -1,48 +0,0 @@ -trait A1 -trait A2 -trait A3 -trait L1 extends A1 with A2 with A3 - -object Test { - trait T1[-A <: A1] - trait T2[-A >: L1] - trait T3[ A <: A1] - trait T4[ A >: L1] - trait T5[+A <: A1] - trait T6[+A >: L1] - - def f1(x: T1[_]) = x - def f2(x: T2[_]) = x - def f3(x: T3[_]) = x - def f4(x: T4[_]) = x - def f5(x: T5[_]) = x - def f6(x: T6[_]) = x - // a.scala:22: error: type arguments [Any] do not conform to trait T5's type parameter bounds [+A <: A1] - // def f5(x: T5[_]) = x - // ^ - - def g1(x: T1[_ <: A1]) = x - def g2(x: T2[_ >: L1]) = x - def g3(x: T3[_ <: A1]) = x - def g4(x: T4[_ >: L1]) = x - def g5(x: T5[_ <: A1]) = x - def g6(x: T6[_ >: L1]) = x - - def q1(x: T1[_ >: L1]) = x - def q2(x: T2[_ <: A1]) = x - def q3(x: T3[_ >: L1]) = x - def q4(x: T4[_ <: A1]) = x - def q5(x: T5[_ >: L1]) = x - def q6(x: T6[_ <: A1]) = x - // a.scala:41: error: type arguments [Any] do not conform to trait T5's type parameter bounds [+A <: A1] - // def q5(x: T5[_ >: L1]) = x - // ^ - // two errors found - - def h1(x: T1[_ >: L1 <: A1]) = x - def h2(x: T2[_ >: L1 <: A1]) = x - def h3(x: T3[_ >: L1 <: A1]) = x - def h4(x: T4[_ >: L1 <: A1]) = x - def h5(x: T5[_ >: L1 <: A1]) = x - def h6(x: T6[_ >: L1 <: A1]) = x -} -- cgit v1.2.3