Skip to content

Harden Type Inference #12560

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

Merged
merged 8 commits into from
May 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/Inferencing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ object Inferencing {
*/
def isFullyDefined(tp: Type, force: ForceDegree.Value)(using Context): Boolean = {
val nestedCtx = ctx.fresh.setNewTyperState()
val result = new IsFullyDefinedAccumulator(force)(using nestedCtx).process(tp)
val result =
try new IsFullyDefinedAccumulator(force)(using nestedCtx).process(tp)
catch case ex: StackOverflowError =>
false // can happen for programs with illegal recusions, e.g. neg/recursive-lower-constraint.scala
if (result) nestedCtx.typerState.commit()
result
}
Expand All @@ -43,7 +46,7 @@ object Inferencing {
*/
def canDefineFurther(tp: Type)(using Context): Boolean =
val prevConstraint = ctx.typerState.constraint
isFullyDefined(tp, force = ForceDegree.all)
isFullyDefined(tp, force = ForceDegree.failBottom)
&& (ctx.typerState.constraint ne prevConstraint)

/** The fully defined type, where all type variables are forced.
Expand Down Expand Up @@ -687,7 +690,7 @@ trait Inferencing { this: Typer =>

val arg = findArg(call)
if !arg.isEmpty then
var argType = arg.tpe
var argType = arg.tpe.widenExpr.widenTermRefExpr
if !argType.isSingleton then argType = SkolemType(argType)
argType <:< tvar
case _ =>
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/typer/ReTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,5 @@ class ReTyper extends Typer with ReChecking {
override protected def addAccessorDefs(cls: Symbol, body: List[Tree])(using Context): List[Tree] = body
override protected def checkEqualityEvidence(tree: tpd.Tree, pt: Type)(using Context): Unit = ()
override protected def matchingApply(methType: MethodOrPoly, pt: FunProto)(using Context): Boolean = true
override protected def typedScala2MacroBody(call: untpd.Tree)(using Context): Tree = promote(call)
}
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3678,7 +3678,7 @@ class Typer extends Namer
return readapt(tree.cast(target))

def recover(failure: SearchFailureType) =
if canDefineFurther(wtp) then readapt(tree)
if canDefineFurther(wtp) || canDefineFurther(pt) then readapt(tree)
else err.typeMismatch(tree, pt, failure)

pt match
Expand Down Expand Up @@ -3900,7 +3900,7 @@ class Typer extends Namer
report.warning(PureExpressionInStatementPosition(original, exprOwner), original.srcPos)

/** Types the body Scala 2 macro declaration `def f = macro <body>` */
private def typedScala2MacroBody(call: untpd.Tree)(using Context): Tree =
protected def typedScala2MacroBody(call: untpd.Tree)(using Context): Tree =
// TODO check that call is to a method with valid signature
def typedPrefix(tree: untpd.RefTree)(splice: Context ?=> Tree => Tree)(using Context): Tree = {
tryAlternatively {
Expand Down
6 changes: 3 additions & 3 deletions tests/neg-scalajs/jsconstructortag-error-in-typer.check
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
|no implicit argument of type scala.scalajs.js.ConstructorTag[ScalaClass] was found for parameter tag of method constructorTag in package scala.scalajs.js.
|I found:
|
| scala.scalajs.js.ConstructorTag.materialize[Nothing]
| scala.scalajs.js.ConstructorTag.materialize[T]
|
|But method materialize in object ConstructorTag does not match type scala.scalajs.js.ConstructorTag[ScalaClass].
-- Error: tests/neg-scalajs/jsconstructortag-error-in-typer.scala:10:39 ------------------------------------------------
Expand All @@ -13,7 +13,7 @@
|no implicit argument of type scala.scalajs.js.ConstructorTag[ScalaTrait] was found for parameter tag of method constructorTag in package scala.scalajs.js.
|I found:
|
| scala.scalajs.js.ConstructorTag.materialize[Nothing]
| scala.scalajs.js.ConstructorTag.materialize[T]
|
|But method materialize in object ConstructorTag does not match type scala.scalajs.js.ConstructorTag[ScalaTrait].
-- Error: tests/neg-scalajs/jsconstructortag-error-in-typer.scala:11:45 ------------------------------------------------
Expand All @@ -22,6 +22,6 @@
|no implicit argument of type scala.scalajs.js.ConstructorTag[ScalaObject.type] was found for parameter tag of method constructorTag in package scala.scalajs.js.
|I found:
|
| scala.scalajs.js.ConstructorTag.materialize[Nothing]
| scala.scalajs.js.ConstructorTag.materialize[T]
|
|But method materialize in object ConstructorTag does not match type scala.scalajs.js.ConstructorTag[ScalaObject.type].
2 changes: 1 addition & 1 deletion tests/neg/i4986a.check
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
|Cannot construct a collection of type List[String] with elements of type Int based on a collection of type List[Int]..
|I found:
|
| collection.BuildFrom.buildFromIterableOps[Nothing, Nothing, Nothing]
| collection.BuildFrom.buildFromIterableOps[CC, A0, A]
|
|But method buildFromIterableOps in trait BuildFromLowPriority2 does not match type collection.BuildFrom[List[Int], Int, List[String]].
7 changes: 0 additions & 7 deletions tests/neg/i7056.check

This file was deleted.

2 changes: 1 addition & 1 deletion tests/pos/i864.scala → tests/neg/i864.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ object C {
trait X[T]
implicit def u[A, B]: X[A | B] = new X[A | B] {}
def y[T](implicit x: X[T]): T = ???
val x: a.type & b.type | b.type & c.type = y
val x: a.type & b.type | b.type & c.type = y // error
}
Original file line number Diff line number Diff line change
Expand Up @@ -344,5 +344,6 @@ object functors {
??? // $this.flatMap[A](identity) disabled since it does not typecheck
}

MonadFlatten.flattened(List(List(1, 2, 3), List(4, 5)))
MonadFlatten.flattened(List(List(1, 2, 3), List(4, 5))) // ok, synthesizes (using ListMonad)
MonadFlatten.flattened(List(List(1, 2, 3), List(4, 5)))(using ListMonad) // error
Comment on lines +347 to +348
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear: passing ListMonad explicitly already failed on master and this PR doesn't improve the situation but just documents it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, correct.

}
17 changes: 17 additions & 0 deletions tests/pos/depfun.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// The following test is derived from scala/reflect/TypeTest.scala, but using
// a dependent function instead of a dependent SAM. It shows that the special treatment
// using a DependentTypeTree is not needed for plain function types.
// But for SAM types, the treatment is needed, otherwise TypeTest.scala does
// not typecheck. Todo: Figure out the reason for this difference.
object Test:

type F[S, T] = (x: S) => Option[x.type & T]

/** Trivial type test that always succeeds */
def identity[T]: F[T, T] = Some(_)

val x: 1 = 1
val y = identity(x)
val z: Option[1] = y


3 changes: 2 additions & 1 deletion tests/neg/i7056.scala → tests/pos/i7056.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ given [T <: A](using PartialId[T]): T1[T] = new T1[T] {
given PartialId[B] = ???

val x: B = ???
val z = x.idnt1 // error
val z = x.idnt1 // used to be an error, now ok