diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/Erasure.scala | 22 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala | 1 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Namers.scala | 3 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 19 | ||||
-rw-r--r-- | src/library/scala/Symbol.scala | 5 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/StdAttachments.scala | 17 | ||||
-rw-r--r-- | test/files/neg/t6357.check | 4 | ||||
-rw-r--r-- | test/files/neg/t6357.scala | 6 | ||||
-rw-r--r-- | test/files/run/t6023.check | 12 | ||||
-rw-r--r-- | test/files/run/t6023.scala | 17 | ||||
-rw-r--r-- | test/files/run/t6673.check | 1 | ||||
-rw-r--r-- | test/files/run/t6673.scala | 5 | ||||
-rw-r--r-- | test/files/run/t6677.scala | 28 | ||||
-rw-r--r-- | test/files/run/t6677b.scala | 33 | ||||
-rw-r--r-- | test/files/run/t6695.scala | 18 | ||||
-rw-r--r-- | test/files/run/t6706.scala | 14 |
16 files changed, 192 insertions, 13 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 7b45b3efe5..bba5fd4e5e 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -693,7 +693,7 @@ abstract class Erasure extends AddInterfaces adaptToType(unbox(tree, pt), pt) else if (isPrimitiveValueType(tree.tpe) && !isPrimitiveValueType(pt)) { adaptToType(box(tree, pt.toString), pt) - } else if (tree.tpe.isInstanceOf[MethodType] && tree.tpe.params.isEmpty) { + } else if (isMethodTypeWithEmptyParams(tree.tpe)) { // [H] this assert fails when trying to typecheck tree !(SomeClass.this.bitmap) for single lazy val //assert(tree.symbol.isStable, "adapt "+tree+":"+tree.tpe+" to "+pt) adaptToType(Apply(tree, List()) setPos tree.pos setType tree.tpe.resultType, pt) @@ -783,16 +783,21 @@ abstract class Erasure extends AddInterfaces else if (!isPrimitiveValueType(qual1.tpe) && isPrimitiveValueMember(tree.symbol)) qual1 = unbox(qual1, tree.symbol.owner.tpe) - if (isPrimitiveValueMember(tree.symbol) && !isPrimitiveValueType(qual1.tpe)) + def selectFrom(qual: Tree) = treeCopy.Select(tree, qual, name) + + if (isPrimitiveValueMember(tree.symbol) && !isPrimitiveValueType(qual1.tpe)) { tree.symbol = NoSymbol - else if (qual1.tpe.isInstanceOf[MethodType] && qual1.tpe.params.isEmpty) { + selectFrom(qual1) + } else if (isMethodTypeWithEmptyParams(qual1.tpe)) { assert(qual1.symbol.isStable, qual1.symbol); - qual1 = Apply(qual1, List()) setPos qual1.pos setType qual1.tpe.resultType + val applied = Apply(qual1, List()) setPos qual1.pos setType qual1.tpe.resultType + adaptMember(selectFrom(applied)) } else if (!(qual1.isInstanceOf[Super] || (qual1.tpe.typeSymbol isSubClass tree.symbol.owner))) { assert(tree.symbol.owner != ArrayClass) - qual1 = cast(qual1, tree.symbol.owner.tpe) + selectFrom(cast(qual1, tree.symbol.owner.tpe)) + } else { + selectFrom(qual1) } - treeCopy.Select(tree, qual1, name) } case SelectFromArray(qual, name, erasure) => var qual1 = typedQualifier(qual) @@ -870,6 +875,11 @@ abstract class Erasure extends AddInterfaces tree1 } } + + private def isMethodTypeWithEmptyParams(tpe: Type) = tpe match { + case MethodType(Nil, _) => true + case _ => false + } } /** The erasure transformer */ diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index e8a2c9f43c..acc4f7ff67 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -426,6 +426,7 @@ trait MethodSynthesis { // spot that brand of them. In other words it's an artifact of the implementation. val tpt = derivedSym.tpe.finalResultType match { case ExistentialType(_, _) => TypeTree() + case _ if mods.isDeferred => TypeTree() case tp => TypeTree(tp) } tpt setPos derivedSym.pos.focus diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index f5d4df14fe..36edd46f25 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1289,7 +1289,8 @@ trait Namers extends MethodSynthesis { if (clazz.isDerivedValueClass) { log("Ensuring companion for derived value class " + name + " at " + cdef.pos.show) clazz setFlag FINAL - enclosingNamerWithScope(clazz.owner.info.decls).ensureCompanionObject(cdef) + // Don't force the owner's info lest we create cycles as in SI-6357. + enclosingNamerWithScope(clazz.owner.rawInfo.decls).ensureCompanionObject(cdef) } result diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index f2d7adc628..a2aca45e8f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1117,7 +1117,8 @@ trait Typers extends Modes with Adaptations with Tags { adaptType() else if ( inExprModeButNot(mode, FUNmode) && !tree.isDef && // typechecking application - tree.symbol != null && tree.symbol.isTermMacro) // of a macro + tree.symbol != null && tree.symbol.isTermMacro && // of a macro + !tree.attachments.get[SuppressMacroExpansionAttachment.type].isDefined) macroExpand(this, tree, mode, pt) else if ((mode & (PATTERNmode | FUNmode)) == (PATTERNmode | FUNmode)) adaptConstrPattern() @@ -5221,9 +5222,9 @@ trait Typers extends Modes with Adaptations with Tags { // find out whether the programmer is trying to eta-expand a macro def // to do that we need to typecheck the tree first (we need a symbol of the eta-expandee) // that typecheck must not trigger macro expansions, so we explicitly prohibit them - // Q: "but, " - you may ask - ", `typed1` doesn't call adapt, which does macro expansion, so why explicit check?" - // A: solely for robustness reasons. this mechanism might change in the future, which might break unprotected code - val exprTyped = context.withMacrosDisabled(typed1(expr, mode, pt)) + // however we cannot do `context.withMacrosDisabled` + // because `expr` might contain nested macro calls (see SI-6673) + val exprTyped = typed1(expr updateAttachment SuppressMacroExpansionAttachment, mode, pt) exprTyped match { case macroDef if macroDef.symbol != null && macroDef.symbol.isTermMacro && !macroDef.symbol.isErroneous => MacroEtaError(exprTyped) @@ -5367,8 +5368,14 @@ trait Typers extends Modes with Adaptations with Tags { } def typedTypeTree(tree: TypeTree) = { - if (tree.original != null) - tree setType typedType(tree.original, mode).tpe + if (tree.original != null) { + val newTpt = typedType(tree.original, mode) + tree setType newTpt.tpe + newTpt match { + case tt @ TypeTree() => tree setOriginal tt.original + case _ => tree + } + } else // we should get here only when something before failed // and we try again (@see tryTypedApply). In that case we can assign diff --git a/src/library/scala/Symbol.scala b/src/library/scala/Symbol.scala index 723c05a30c..4fead7a50c 100644 --- a/src/library/scala/Symbol.scala +++ b/src/library/scala/Symbol.scala @@ -69,6 +69,11 @@ private[scala] abstract class UniquenessCache[K, V >: Null] val res = cached() if (res != null) res else { + // If we don't remove the old String key from the map, we can + // wind up with one String as the key and a different String as + // as the name field in the Symbol, which can lead to surprising + // GC behavior and duplicate Symbols. See SI-6706. + map remove name val sym = valueFromKey(name) map.put(name, new WeakReference(sym)) sym diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index 9fe443bf50..1df91a67b0 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -19,9 +19,26 @@ trait StdAttachments { def setPos(newpos: Position): this.type = { pos = newpos; this } } + /** When present, indicates that the host `Ident` has been created from a backquoted identifier. + */ case object BackquotedIdentifierAttachment + /** Stores the trees that give rise to a refined type to be used in reification. + * Unfortunately typed `CompoundTypeTree` is lacking essential info, and the reifier cannot use `CompoundTypeTree.tpe`. + * Therefore we need this hack (see `Reshape.toPreTyperTypeTree` for a detailed explanation). + */ case class CompoundTypeTreeOriginalAttachment(parents: List[Tree], stats: List[Tree]) + /** Is added by the macro engine to the results of macro expansions. + * Stores the original expandee as it entered the `macroExpand` function. + */ case class MacroExpansionAttachment(original: Tree) + + /** When present, suppresses macro expansion for the host. + * This is occasionally necessary, e.g. to prohibit eta-expansion of macros. + * + * Does not affect expandability of child nodes, there's context.withMacrosDisabled for that + * (but think thrice before using that API - see the discussion at https://github.com/scala/scala/pull/1639). + */ + case object SuppressMacroExpansionAttachment } diff --git a/test/files/neg/t6357.check b/test/files/neg/t6357.check new file mode 100644 index 0000000000..a534d1439a --- /dev/null +++ b/test/files/neg/t6357.check @@ -0,0 +1,4 @@ +t6357.scala:3: error: value class may not be a local class + final class Y(val j: Int) extends AnyVal + ^ +one error found diff --git a/test/files/neg/t6357.scala b/test/files/neg/t6357.scala new file mode 100644 index 0000000000..47f5629638 --- /dev/null +++ b/test/files/neg/t6357.scala @@ -0,0 +1,6 @@ +object K { + def q = { + final class Y(val j: Int) extends AnyVal + 3 + } +} diff --git a/test/files/run/t6023.check b/test/files/run/t6023.check new file mode 100644 index 0000000000..ee93565234 --- /dev/null +++ b/test/files/run/t6023.check @@ -0,0 +1,12 @@ +{ + abstract trait Foo extends AnyRef { + <stable> <accessor> def a: Int + }; + () +} +{ + abstract trait Foo extends AnyRef { + <stable> <accessor> def a: Int + }; + () +} diff --git a/test/files/run/t6023.scala b/test/files/run/t6023.scala new file mode 100644 index 0000000000..07af3685a5 --- /dev/null +++ b/test/files/run/t6023.scala @@ -0,0 +1,17 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} +import scala.tools.reflect.ToolBox + +object Test extends App { + // test 1: reify + val tree = reify{ trait Foo { val a: Int } }.tree + println(tree.toString) + + // test 2: import and typecheck + val toolbox = cm.mkToolBox() + val ttree = toolbox.typeCheck(tree) + println(ttree.toString) + + // test 3: import and compile + toolbox.eval(tree) +} diff --git a/test/files/run/t6673.check b/test/files/run/t6673.check new file mode 100644 index 0000000000..ef2aa551dc --- /dev/null +++ b/test/files/run/t6673.check @@ -0,0 +1 @@ +List(x) diff --git a/test/files/run/t6673.scala b/test/files/run/t6673.scala new file mode 100644 index 0000000000..115bbdf234 --- /dev/null +++ b/test/files/run/t6673.scala @@ -0,0 +1,5 @@ +object Test extends App { + def foo(f: String => Array[String])(s: String) = f(s) + val test = foo(Array(_)) _ + println(test("x").toList) +}
\ No newline at end of file diff --git a/test/files/run/t6677.scala b/test/files/run/t6677.scala new file mode 100644 index 0000000000..e6eaf6a498 --- /dev/null +++ b/test/files/run/t6677.scala @@ -0,0 +1,28 @@ + +class Test { + val cm: reflect.runtime.universe.Mirror = reflect.runtime.currentMirror + def error { + new cm.universe.Traverser // java.lang.VerifyError: (class: Test, method: error signature: ()V) Incompatible object argument for function call + + } + + def okay1 { + val cm: reflect.runtime.universe.Mirror = reflect.runtime.currentMirror + + new cm.universe.Traverser + } + + def okay2 { + val cm: reflect.runtime.universe.Mirror = reflect.runtime.currentMirror + val u: reflect.runtime.universe.type = cm.universe + new u.Traverser + } +} + +object Test { + def main(args: Array[String]) { + new Test().error + new Test().okay1 + new Test().okay2 + } +} diff --git a/test/files/run/t6677b.scala b/test/files/run/t6677b.scala new file mode 100644 index 0000000000..e4fe5e3722 --- /dev/null +++ b/test/files/run/t6677b.scala @@ -0,0 +1,33 @@ +trait U { + trait U1 { + class X + } + type U11 <: U1 + val u : U11 = null.asInstanceOf[U11] +} +trait A extends U + +trait B extends U { + def foo = "" + class U11 extends U1 { class X extends super.X { foo } } // refer to foo to add $outer pointer + override val u = new U11 +} +class C { + val ab: A with B = new A with B // `B with A` works. + + def foo { + // fails + new ab.u.X + + // works: + val u = ab.u + new u.X + } +} +object Test { + def main(args: Array[String]) { + // java.lang.NoSuchMethodError: A.u()LB$U11; + // at C.foo(t6677b.scala:23) + new C().foo + } +} diff --git a/test/files/run/t6695.scala b/test/files/run/t6695.scala new file mode 100644 index 0000000000..b527238a51 --- /dev/null +++ b/test/files/run/t6695.scala @@ -0,0 +1,18 @@ +object Test extends App { + try { + Array("a", "b", "c") match { + case Array("a", "x", "c") => println("x") + case Array("a", "b", "x") => println("a"); + case Array("a", "d", _*) => println("wrongly positive") + } + assert(false, "match succeeded") + } catch { + case _: MatchError => // okay + } + + Array("a", "b", "c") match { + case Array("a", "x", "c") => println("x") + case Array("a", "b", "x") => println("a"); + case Array("a", "b", _*) => // okay + } +} diff --git a/test/files/run/t6706.scala b/test/files/run/t6706.scala new file mode 100644 index 0000000000..905494ca8d --- /dev/null +++ b/test/files/run/t6706.scala @@ -0,0 +1,14 @@ +object Test { + var name = "foo" + 1 + var s1 = Symbol(name) + s1 = null + System.gc + val s2 = Symbol("foo1") + name = null + System.gc + val s3 = Symbol("foo1") + + def main(args: Array[String]): Unit = { + assert(s2 eq s3, ((s2, System.identityHashCode(s2), s3, System.identityHashCode(s3)))) + } +} |