summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala25
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala27
-rw-r--r--test/files/run/virtpatmat_stringinterp.check1
-rw-r--r--test/files/run/virtpatmat_stringinterp.flags1
-rw-r--r--test/files/run/virtpatmat_stringinterp.scala13
5 files changed, 62 insertions, 5 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index fe1c90fe67..e2d4efab83 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -67,6 +67,7 @@ trait Contexts { self: Analyzer =>
val c = sc.make(unit, tree, sc.owner, sc.scope, sc.imports)
if (erasedTypes) c.setThrowErrors() else c.setReportErrors()
c.implicitsEnabled = !erasedTypes
+ c.enrichmentEnabled = c.implicitsEnabled
c
}
@@ -106,7 +107,7 @@ trait Contexts { self: Analyzer =>
var depth: Int = 0
var imports: List[ImportInfo] = List() // currently visible imports
var openImplicits: List[(Type,Tree)] = List() // types for which implicit arguments
- // are currently searched
+ // are currently searched
// for a named application block (Tree) the corresponding NamedApplyInfo
var namedApplyBlockInfo: Option[(Tree, NamedApplyInfo)] = None
var prefix: Type = NoPrefix
@@ -120,6 +121,7 @@ trait Contexts { self: Analyzer =>
var diagnostic: List[String] = Nil // these messages are printed when issuing an error
var implicitsEnabled = false
var macrosEnabled = true
+ var enrichmentEnabled = false // to selectively allow enrichment in patterns, where other kinds of implicit conversions are not allowed
var checking = false
var retyping = false
@@ -192,8 +194,25 @@ trait Contexts { self: Analyzer =>
def withImplicitsDisabled[T](op: => T): T = {
val saved = implicitsEnabled
implicitsEnabled = false
+ val savedP = enrichmentEnabled
+ enrichmentEnabled = false
try op
- finally implicitsEnabled = saved
+ finally {
+ implicitsEnabled = saved
+ enrichmentEnabled = savedP
+ }
+ }
+
+ def withImplicitsDisabledAllowEnrichment[T](op: => T): T = {
+ val saved = implicitsEnabled
+ implicitsEnabled = false
+ val savedP = enrichmentEnabled
+ enrichmentEnabled = true
+ try op
+ finally {
+ implicitsEnabled = saved
+ enrichmentEnabled = savedP
+ }
}
def withMacrosEnabled[T](op: => T): T = {
@@ -246,6 +265,7 @@ trait Contexts { self: Analyzer =>
c.typingIndentLevel = typingIndentLevel
c.implicitsEnabled = this.implicitsEnabled
c.macrosEnabled = this.macrosEnabled
+ c.enrichmentEnabled = this.enrichmentEnabled
c.checking = this.checking
c.retyping = this.retyping
c.openImplicits = this.openImplicits
@@ -298,6 +318,7 @@ trait Contexts { self: Analyzer =>
def makeImplicit(reportAmbiguousErrors: Boolean) = {
val c = makeSilent(reportAmbiguousErrors)
c.implicitsEnabled = false
+ c.enrichmentEnabled = false
c
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 2b7c8e8304..a6a670f163 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1171,7 +1171,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
&& !qtpe.typeSymbol.isBottomClass
&& qtpe != WildcardType
&& !qual.isInstanceOf[ApplyImplicitView] // don't chain views
- && context.implicitsEnabled
+ && (context.implicitsEnabled || context.enrichmentEnabled)
// Elaborating `context.implicitsEnabled`:
// don't try to adapt a top-level type that's the subject of an implicit search
// this happens because, if isView, typedImplicit tries to apply the "current" implicit value to
@@ -4063,7 +4063,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
} else {
member(qual, name)
}
- if (sym == NoSymbol && name != nme.CONSTRUCTOR && (mode & EXPRmode) != 0) {
+
+ // symbol not found? --> try to convert implicitly to a type that does have the required member
+ // added `| PATTERNmode` to allow enrichment in patterns (so we can add e.g., an xml member to StringContext, which in turn has an unapply[Seq] method)
+ if (sym == NoSymbol && name != nme.CONSTRUCTOR && (mode & (EXPRmode | PATTERNmode)) != 0) {
val qual1 =
if (member(qual, name) != NoSymbol) qual
else adaptToMemberWithArgs(tree, qual, name, mode, true, true)
@@ -4071,6 +4074,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
if (qual1 ne qual)
return typed(treeCopy.Select(tree, qual1, name), mode, pt)
}
+
if (!reallyExists(sym)) {
if (context.owner.enclosingTopLevelClass.isJavaDefined && name.isTypeName) {
val tree1 = atPos(tree.pos) { gen.convertToSelectFromType(qual, name) }
@@ -4806,6 +4810,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
ptLine("typing %s: pt = %s".format(ptTree(tree), pt),
"undetparams" -> context.undetparams,
"implicitsEnabled" -> context.implicitsEnabled,
+ "enrichmentEnabled" -> context.enrichmentEnabled,
+ "mode" -> modeString(mode),
"silent" -> context.bufferErrors,
"context.owner" -> context.owner
)
@@ -4909,7 +4915,22 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
// We disable implicits because otherwise some constructs will
// type check which should not. The pattern matcher does not
// perform implicit conversions in an attempt to consummate a match.
- context.withImplicitsDisabled(typed(tree, PATTERNmode, pt))
+
+ // on the one hand,
+ // "abc" match { case Seq('a', 'b', 'c') => true }
+ // should be ruled out statically, otherwise this is a runtime
+ // error both because there is an implicit from String to Seq
+ // (even though such implicits are not used by the matcher) and
+ // because the typer is fine with concluding that "abc" might
+ // be of type "String with Seq[T]" and thus eligible for a call
+ // to unapplySeq.
+
+ // on the other hand, we want to be able to use implicits to add members retro-actively (e.g., add xml to StringContext)
+
+ // as a compromise, context.enrichmentEnabled tells adaptToMember to go ahead and enrich,
+ // but arbitrary conversions (in adapt) are disabled
+ // TODO: can we achieve the pattern matching bit of the string interpolation SIP without this?
+ context.withImplicitsDisabledAllowEnrichment(typed(tree, PATTERNmode, pt))
}
/** Types a (fully parameterized) type tree */
diff --git a/test/files/run/virtpatmat_stringinterp.check b/test/files/run/virtpatmat_stringinterp.check
new file mode 100644
index 0000000000..7927f4f2d9
--- /dev/null
+++ b/test/files/run/virtpatmat_stringinterp.check
@@ -0,0 +1 @@
+Node(1)
diff --git a/test/files/run/virtpatmat_stringinterp.flags b/test/files/run/virtpatmat_stringinterp.flags
new file mode 100644
index 0000000000..0612c4f8ff
--- /dev/null
+++ b/test/files/run/virtpatmat_stringinterp.flags
@@ -0,0 +1 @@
+-Xexperimental -Yvirtpatmat \ No newline at end of file
diff --git a/test/files/run/virtpatmat_stringinterp.scala b/test/files/run/virtpatmat_stringinterp.scala
new file mode 100644
index 0000000000..213712f17a
--- /dev/null
+++ b/test/files/run/virtpatmat_stringinterp.scala
@@ -0,0 +1,13 @@
+object Test extends App {
+ case class Node(x: Int)
+
+ implicit def sc2xml(sc: StringContext): XMLContext = new XMLContext(sc)
+ class XMLContext(sc: StringContext) {
+ object xml {
+ def unapplySeq(xml: Node): Option[Seq[Node]] = Some(List(Node(1)))
+ }
+ }
+
+ val x: Node = Node(0)
+ x match { case xml"""<foo arg=$a/>""" => println(a) }
+} \ No newline at end of file