Permalink
Browse files

SI-6921 SI-7239 Tread lightly during exploratory typing

When deciding whether an Assign is a named argument or
and assignment expression, or when looking at arguments
that the current selection is applied to in order to
evaluate candidate implicit views, we risk polluting
the tree by setting error types. This happens even
if we are in 'silent' mode; that mode does silence the
error report, but not the side effect on the tree.

This commit adds strategic `duplicate` calls to
address the problem symptomatically.

Duplicating trees and retyping in general reach into
the domain of bugs umbrella-ed under SI-5464, but in
these places we should be safe because the tree is in
the argument position, not somewhere where, for example,
a case class-es synthetic companion object might be
twice entered into the same scope.

Longer term, we'd like to make type checking side effect
free, so we wouldn't need to play whack-a-mole like this.
That idea is tracked under SI-7176.
  • Loading branch information...
1 parent b7b4f87 commit 174334b9095be2be79c164bbdea1749dab9e0cbe @retronym retronym committed Mar 13, 2013
@@ -502,7 +502,7 @@ trait NamesDefaults { self: Analyzer =>
// disable conforms as a view...
val errsBefore = reporter.ERROR.count
try typer.silent { tpr =>
- val res = tpr.typed(arg, subst(paramtpe))
+ val res = tpr.typed(arg.duplicate, subst(paramtpe))
// better warning for SI-5044: if `silent` was not actually silent give a hint to the user
// [H]: the reason why `silent` is not silent is because the cyclic reference exception is
// thrown in a context completely different from `context` here. The exception happens while
@@ -1375,9 +1375,8 @@ trait Typers extends Modes with Adaptations with Tags {
def onError(reportError: => Tree): Tree = {
context.tree match {
case Apply(tree1, args) if (tree1 eq tree) && args.nonEmpty =>
- silent(_.typedArgs(args, mode)) match {
- case SilentResultValue(xs) =>
- val args = xs.asInstanceOf[List[Tree]]
+ silent(_.typedArgs(args.map(_.duplicate), mode)) match {
+ case SilentResultValue(args) =>
if (args exists (_.isErrorTyped))
reportError
else
View
@@ -0,0 +1,11 @@
+class Message(messageType: String, reason: Option[String])
+
+class ReproForSI6921 {
+
+ private[this] var reason = ""
+
+ def decideElection = {
+ val explanation = None
+ new Message("", reason = explanation)
+ }
+}
View
@@ -0,0 +1,38 @@
+object Test {
+ def BrokenMethod(): HasFilter[(Int, String)] = ???
+
+ trait HasFilter[B] {
+ def filter(p: B => Boolean) = ???
+ }
+
+ trait HasWithFilter {
+ def withFilter = ???
+ }
+
+ object addWithFilter {
+ trait NoImplicit
+ implicit def enrich(v: Any)
+ (implicit F0: NoImplicit): HasWithFilter = ???
+ }
+
+ BrokenMethod().withFilter(_ => true) // okay
+ BrokenMethod().filter(_ => true) // okay
+
+ locally {
+ import addWithFilter._
+ BrokenMethod().withFilter((_: (Int, String)) => true) // okay
+ }
+
+ locally {
+ import addWithFilter._
+ // adaptToMemberWithArgs sets the type of the tree `x`
+ // to ErrorType (while in silent mode, so the error is not
+ // reported. Later, when the fallback from `withFilter`
+ // to `filter` is attempted, the closure is taken to have
+ // have the type `<error> => Boolean`, which conforms to
+ // `(B => Boolean)`. Only later during pickling does the
+ // defensive check for erroneous types in the tree pick up
+ // the problem.
+ BrokenMethod().withFilter(x => true) // erroneous or inaccessible type.
+ }
+}

0 comments on commit 174334b

Please sign in to comment.