summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/reflect/internal/Scopes.scala4
-rw-r--r--src/compiler/scala/reflect/internal/Types.scala37
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala11
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala32
-rw-r--r--test/files/pos/infersingle.flags1
-rw-r--r--test/files/pos/infersingle.scala5
6 files changed, 59 insertions, 31 deletions
diff --git a/src/compiler/scala/reflect/internal/Scopes.scala b/src/compiler/scala/reflect/internal/Scopes.scala
index 4db2cbf0d6..3ea43c22f2 100644
--- a/src/compiler/scala/reflect/internal/Scopes.scala
+++ b/src/compiler/scala/reflect/internal/Scopes.scala
@@ -33,6 +33,10 @@ trait Scopes extends api.Scopes { self: SymbolTable =>
e
}
+ object Scope {
+ def unapplySeq(decls: Scope): Some[Seq[Symbol]] = Some(decls.toList)
+ }
+
class Scope(initElems: ScopeEntry) extends Iterable[Symbol] {
var elems: ScopeEntry = initElems
diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala
index 24b641ddb4..a0bd994a97 100644
--- a/src/compiler/scala/reflect/internal/Types.scala
+++ b/src/compiler/scala/reflect/internal/Types.scala
@@ -2338,6 +2338,20 @@ A type's typeSymbol should never be inspected directly.
//private var tidCount = 0 //DEBUG
+ object HasTypeMember {
+ def apply(name: TypeName, tp: Type): Type = {
+ val bound = refinedType(List(WildcardType), NoSymbol)
+ val bsym = bound.typeSymbol.newAliasType(NoPosition, name)
+ bsym setInfo tp
+ bound.decls enter bsym
+ bound
+ }
+ def unapply(tp: Type): Option[(TypeName, Type)] = tp match {
+ case RefinedType(List(WildcardType), Scope(sym)) => Some((sym.name.toTypeName, sym.info))
+ case _ => None
+ }
+ }
+
//@M
// a TypeVar used to be a case class with only an origin and a constr
// then, constr became mutable (to support UndoLog, I guess),
@@ -2558,6 +2572,8 @@ A type's typeSymbol should never be inspected directly.
// So the strategy used here is to test first the type, then the direct parents, and finally
// to fall back on the individual base types. This warrants eventual re-examination.
+ // AM: I think we could use the `suspended` flag to avoid side-effecting during unification
+
if (suspended) // constraint accumulation is disabled
checkSubtype(tp, origin)
else if (constr.instValid) // type var is already set
@@ -2603,11 +2619,7 @@ A type's typeSymbol should never be inspected directly.
* (`T` corresponds to @param sym)
*/
def registerTypeSelection(sym: Symbol, tp: Type): Boolean = {
- val bound = refinedType(List(WildcardType), NoSymbol)
- val bsym = bound.typeSymbol.newAliasType(NoPosition, sym.name.toTypeName)
- bsym setInfo tp
- bound.decls enter bsym
- registerBound(bound, false)
+ registerBound(HasTypeMember(sym.name.toTypeName, tp), false)
}
/** Can this variable be related in a constraint to type `tp`?
@@ -3206,7 +3218,7 @@ A type's typeSymbol should never be inspected directly.
/** A class expressing upper and lower bounds constraints of type variables,
* as well as their instantiations.
*/
- class TypeConstraint(lo0: List[Type], hi0: List[Type], numlo0: Type, numhi0: Type) {
+ class TypeConstraint(lo0: List[Type], hi0: List[Type], numlo0: Type, numhi0: Type, avoidWidening0: Boolean = false) {
def this(lo0: List[Type], hi0: List[Type]) = this(lo0, hi0, NoType, NoType)
def this() = this(List(), List())
@@ -3214,9 +3226,11 @@ A type's typeSymbol should never be inspected directly.
private var hibounds = hi0
private var numlo = numlo0
private var numhi = numhi0
+ private var avoidWidening = avoidWidening0
def loBounds: List[Type] = if (numlo == NoType) lobounds else numlo :: lobounds
def hiBounds: List[Type] = if (numhi == NoType) hibounds else numhi :: hibounds
+ def avoidWiden: Boolean = avoidWidening
def addLoBound(tp: Type, isNumericBound: Boolean = false) {
if (isNumericBound && isNumericValueType(tp)) {
@@ -3228,7 +3242,16 @@ A type's typeSymbol should never be inspected directly.
else lobounds ::= tp
}
+ def checkWidening(tp: Type) {
+ if(tp.isStable) avoidWidening = true
+ else tp match {
+ case HasTypeMember(_, _) => avoidWidening = true
+ case _ =>
+ }
+ }
+
def addHiBound(tp: Type, isNumericBound: Boolean = false) {
+ checkWidening(tp)
if (isNumericBound && isNumericValueType(tp)) {
if (numhi == NoType || isNumericSubType(tp, numhi))
numhi = tp
@@ -3249,7 +3272,7 @@ A type's typeSymbol should never be inspected directly.
def instValid = (inst ne null) && (inst ne NoType)
def cloneInternal = {
- val tc = new TypeConstraint(lobounds, hibounds, numlo, numhi)
+ val tc = new TypeConstraint(lobounds, hibounds, numlo, numhi, avoidWidening)
tc.inst = inst
tc
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 660999eb64..fedf0a9104 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -178,13 +178,8 @@ trait Implicits {
private val hasMemberCache = perRunCaches.newMap[Name, Type]()
def apply(name: Name): Type = hasMemberCache.getOrElseUpdate(name, memberWildcardType(name, WildcardType))
def unapply(pt: Type): Option[Name] = pt match {
- case RefinedType(List(WildcardType), decls) =>
- decls.toList match {
- case List(sym) if sym.tpe == WildcardType => Some(sym.name)
- case _ => None
- }
- case _ =>
- None
+ case RefinedType(List(WildcardType), Scope(sym)) if sym.tpe == WildcardType => Some(sym.name)
+ case _ => None
}
}
@@ -570,7 +565,7 @@ trait Implicits {
// filter out failures from type inference, don't want to remove them from undetParams!
// we must be conservative in leaving type params in undetparams
// prototype == WildcardType: want to remove all inferred Nothings
- val AdjustedTypeArgs(okParams, okArgs) = adjustTypeArgs(undetParams, targs)
+ val AdjustedTypeArgs(okParams, okArgs) = adjustTypeArgs(undetParams, tvars, targs)
val subst: TreeTypeSubstituter =
if (okParams.isEmpty) EmptyTreeTypeSubstituter
else {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index f2a0091ee1..f6fc6cedd2 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -523,7 +523,7 @@ trait Infer {
* @param pt ...
* @return ...
*/
- private def exprTypeArgs(tparams: List[Symbol], restpe: Type, pt: Type, useWeaklyCompatible: Boolean = false): List[Type] = {
+ private def exprTypeArgs(tparams: List[Symbol], restpe: Type, pt: Type, useWeaklyCompatible: Boolean = false): (List[Type], List[TypeVar]) = {
val tvars = tparams map freshVar
val instResTp = restpe.instantiateTypeParams(tparams, tvars)
if ( if (useWeaklyCompatible) isWeaklyCompatible(instResTp, pt) else isCompatible(instResTp, pt) ) {
@@ -538,12 +538,12 @@ trait Infer {
restpe
}
//println("try to solve "+tvars+" "+tparams)
- solvedTypes(tvars, tparams, tparams map varianceInType(varianceType),
- false, lubDepth(List(restpe, pt)))
+ (solvedTypes(tvars, tparams, tparams map varianceInType(varianceType),
+ false, lubDepth(List(restpe, pt))), tvars)
} catch {
- case ex: NoInstance => null
+ case ex: NoInstance => (null, null)
}
- } else null
+ } else (null, null)
}
/** Return inferred proto-type arguments of function, given
@@ -639,11 +639,11 @@ trait Infer {
* @return map from tparams to inferred arg, if inference was successful, tparams that map to None are considered left undetermined
* type parameters that are inferred as `scala.Nothing` and that are not covariant in <code>restpe</code> are taken to be undetermined
*/
- def adjustTypeArgs(tparams: List[Symbol], targs: List[Type], restpe: Type = WildcardType): AdjustedTypeArgs.Result = {
+ def adjustTypeArgs(tparams: List[Symbol], tvars: List[TypeVar], targs: List[Type], restpe: Type = WildcardType): AdjustedTypeArgs.Result = {
@inline def notCovariantIn(tparam: Symbol, restpe: Type) =
(varianceInType(restpe)(tparam) & COVARIANT) == 0 // tparam occurred non-covariantly (in invariant or contravariant position)
- (tparams, targs).zipped.map{ (tparam, targ) =>
+ (tparams, tvars, targs).zipped.map{ (tparam, tvar, targ) =>
if (targ.typeSymbol == NothingClass &&
(restpe.isWildcard || notCovariantIn(tparam, restpe))) {
tparam -> None
@@ -651,7 +651,7 @@ trait Infer {
tparam -> Some(
if (targ.typeSymbol == RepeatedParamClass) targ.baseType(SeqClass)
else if (targ.typeSymbol == JavaRepeatedParamClass) targ.baseType(ArrayClass)
- else if (targ.typeSymbol.isModuleClass) targ // this infers Foo.type instead of "object Foo" (see also widenIfNecessary)
+ else if (targ.typeSymbol.isModuleClass || (opt.experimental && tvar.constr.avoidWiden)) targ // this infers Foo.type instead of "object Foo" (see also widenIfNecessary)
else targ.widen
)
}
@@ -729,7 +729,7 @@ trait Infer {
tvars, tparams, tparams map varianceInTypes(formals),
false, lubDepth(formals) max lubDepth(argtpes)
)
- val result = adjustTypeArgs(tparams, targs, restpe)
+ val result = adjustTypeArgs(tparams, tvars, targs, restpe)
printInference(
ptBlock("methTypeArgs result",
@@ -861,7 +861,7 @@ trait Infer {
try {
val AdjustedTypeArgs.Undets(okparams, okargs, leftUndet) = methTypeArgs(undetparams, formals, restpe, argtpes, pt)
// #2665: must use weak conformance, not regular one (follow the monomorphic case above)
- (exprTypeArgs(leftUndet, restpe.instantiateTypeParams(okparams, okargs), pt, useWeaklyCompatible = true) ne null) &&
+ (exprTypeArgs(leftUndet, restpe.instantiateTypeParams(okparams, okargs), pt, useWeaklyCompatible = true)._1 ne null) &&
isWithinBounds(NoPrefix, NoSymbol, okparams, okargs)
} catch {
case ex: NoInstance => false
@@ -1176,10 +1176,10 @@ trait Infer {
"lenientPt" -> lenientPt
)
)
- var targs = exprTypeArgs(undetparams, tree.tpe, strictPt)
- if ((targs eq null) || !(tree.tpe.subst(undetparams, targs) <:< strictPt)) {
- targs = exprTypeArgs(undetparams, tree.tpe, lenientPt)
- }
+ var targs = exprTypeArgs(undetparams, tree.tpe, strictPt)._1
+ if ((targs eq null) || !(tree.tpe.subst(undetparams, targs) <:< strictPt))
+ targs = exprTypeArgs(undetparams, tree.tpe, lenientPt)._1
+
substExpr(tree, undetparams, targs, lenientPt)
printInference("[inferArgumentInstance] finished, targs = " + targs)
}
@@ -1199,13 +1199,13 @@ trait Infer {
"pt" -> pt
)
)
- val targs = exprTypeArgs(tparams, treeTp, pt, useWeaklyCompatible)
+ val (targs, tvars) = exprTypeArgs(tparams, treeTp, pt, useWeaklyCompatible)
if (keepNothings || (targs eq null)) { //@M: adjustTypeArgs fails if targs==null, neg/t0226
substExpr(tree, tparams, targs, pt)
List()
} else {
- val AdjustedTypeArgs.Undets(okParams, okArgs, leftUndet) = adjustTypeArgs(tparams, targs)
+ val AdjustedTypeArgs.Undets(okParams, okArgs, leftUndet) = adjustTypeArgs(tparams, tvars, targs)
printInference(
ptBlock("inferExprInstance/AdjustedTypeArgs",
"okParams" -> okParams,
diff --git a/test/files/pos/infersingle.flags b/test/files/pos/infersingle.flags
new file mode 100644
index 0000000000..e1b37447c9
--- /dev/null
+++ b/test/files/pos/infersingle.flags
@@ -0,0 +1 @@
+-Xexperimental \ No newline at end of file
diff --git a/test/files/pos/infersingle.scala b/test/files/pos/infersingle.scala
new file mode 100644
index 0000000000..6830fcd799
--- /dev/null
+++ b/test/files/pos/infersingle.scala
@@ -0,0 +1,5 @@
+object Test {
+ def one[T](x: T): Option[T] = Some(x)
+ val x = "one"
+ val y: Option[x.type] = one(x)
+} \ No newline at end of file