From 2aa68419c09ba16e4e7fdc71e4a3ad9e8b261e87 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sat, 17 Nov 2012 22:04:19 +0100 Subject: SI-6677 Insert required cast in `new qual.foo.T` Short version: we sometimes need to rewrite this as new T(qual.asInstanceOf[OwnerOfFoo].foo) Long version: `adaptMember` in Erasure performs a few tasks, among them: 1. adding an empty argument list to qualifiers in `new qual.T` for which `qual` is a val template member that has (post uncurry) a MethodType with an empty parameter list. The same rewriting was already applied in uncurry for such qualifiers appearing in other contexts, e.g. `qual.foo` was already rewritten to `qual().foo`. 2. casting, if necessary, the qualifier in `Select(qual, name)` to the type of owner of the symbol that this selection references. This can be neccesary with compound types: - some val class member has type `A with B`; - we instantiate `new ab.valMemberOfB.T` - we must pass `ab.valMemberOfB` to the constructor of `T` as the `$outer` pointer - we must cast `ab` to `B` before calling `valMemberOfB`. Failure to insert this cast can lead to a LinkageError or a VerifyError. However, if 1) was performed, 2) was not. The error is pretty easy to trigger with the new reflection API: class Test { val cm: reflect.runtime.universe.Mirror = reflect.runtime.currentMirror def error { new cm.universe.Traverser // java.lang.VerifyError } def okay1 { val cm: reflect.runtime.universe.Mirror = reflect.runtime.currentMirror new cm.universe.Traverser } } The fix applied here to `adaptMember` mirrors the existing implementation of `adaptType`. --- .../scala/tools/nsc/transform/Erasure.scala | 1 + test/files/run/t6677.scala | 28 ++++++++++++++++++ test/files/run/t6677b.scala | 33 ++++++++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 test/files/run/t6677.scala create mode 100644 test/files/run/t6677b.scala diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 7b45b3efe5..d66a0dd3f6 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -788,6 +788,7 @@ abstract class Erasure extends AddInterfaces else if (qual1.tpe.isInstanceOf[MethodType] && qual1.tpe.params.isEmpty) { assert(qual1.symbol.isStable, qual1.symbol); qual1 = Apply(qual1, List()) setPos qual1.pos setType qual1.tpe.resultType + return adaptMember(treeCopy.Select(tree, qual1, name)) } else if (!(qual1.isInstanceOf[Super] || (qual1.tpe.typeSymbol isSubClass tree.symbol.owner))) { assert(tree.symbol.owner != ArrayClass) qual1 = cast(qual1, tree.symbol.owner.tpe) 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 + } +} -- cgit v1.2.3 From cd1bf7890c742a899e31e3c66487a633170e41ca Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 18 Nov 2012 12:05:32 +0100 Subject: Refactoring of adaptMethod Avoid the early return, and address micro-duplication between adaptType and adaptMember. --- .../scala/tools/nsc/transform/Erasure.scala | 23 +++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index d66a0dd3f6..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,17 +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 - return adaptMember(treeCopy.Select(tree, qual1, name)) + 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) @@ -871,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 */ -- cgit v1.2.3 From 8b54ec9f4ee8c5a0ab00f4b6ffd35c5c9913afd6 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 14 Nov 2012 12:22:22 -0800 Subject: Fix for SI-6357, cycle with value classes. Don't force the owner info. --- src/compiler/scala/tools/nsc/typechecker/Namers.scala | 3 ++- test/files/neg/t6357.check | 4 ++++ test/files/neg/t6357.scala | 6 ++++++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 test/files/neg/t6357.check create mode 100644 test/files/neg/t6357.scala 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/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 + } +} -- cgit v1.2.3 From 555a9bae72107f18a79cff16aa4e0b448b6cd915 Mon Sep 17 00:00:00 2001 From: Jean-Remi Desjardins Date: Mon, 19 Nov 2012 21:41:17 -0500 Subject: findEntry implementation code more concise and DRYer. --- src/library/scala/collection/mutable/FlatHashTable.scala | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/library/scala/collection/mutable/FlatHashTable.scala b/src/library/scala/collection/mutable/FlatHashTable.scala index c0299479f3..91e95e039b 100644 --- a/src/library/scala/collection/mutable/FlatHashTable.scala +++ b/src/library/scala/collection/mutable/FlatHashTable.scala @@ -110,24 +110,23 @@ trait FlatHashTable[A] extends FlatHashTable.HashUtils[A] { /** Finds an entry in the hash table if such an element exists. */ protected def findEntry(elem: A): Option[A] = { - var h = index(elemHashCode(elem)) - var entry = table(h) - while (null != entry && entry != elem) { - h = (h + 1) % table.length - entry = table(h) - } + val entry = findEntryImpl(elem) if (null == entry) None else Some(entry.asInstanceOf[A]) } /** Checks whether an element is contained in the hash table. */ protected def containsEntry(elem: A): Boolean = { + null != findEntryImpl(elem) + } + + private def findEntryImpl(elem: A): AnyRef = { var h = index(elemHashCode(elem)) var entry = table(h) while (null != entry && entry != elem) { h = (h + 1) % table.length entry = table(h) } - null != entry + entry } /** Add entry if not yet in table. -- cgit v1.2.3 From d0de367e72ba695777cfec31d796f5972ae18601 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 23 Nov 2012 19:40:44 -0800 Subject: Fix for SI-6706, Symbol breakage under GC. Ensure the map key and the String in the Symbol are the same reference by removing the old key before updating the map with the new key -> symbol relation. --- src/library/scala/Symbol.scala | 5 +++++ test/files/run/t6706.scala | 14 ++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 test/files/run/t6706.scala 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/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)))) + } +} -- cgit v1.2.3 From 7f1ba06f1313383fe60ce9bc6566afc78b3dae75 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Mon, 19 Nov 2012 14:50:29 -0800 Subject: Fix for SI-6687, wrong isVar logic. Fields which back lazy vals need to be excluded via !isLazy lest isVar return true. --- src/reflect/scala/reflect/internal/Symbols.scala | 2 +- test/files/run/t6687.scala | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 test/files/run/t6687.scala diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 9254cc9ac9..2a52178c4c 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -79,7 +79,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isImplementationArtifact: Boolean = (this hasFlag BRIDGE) || (this hasFlag VBRIDGE) || (this hasFlag ARTIFACT) def isJava: Boolean = isJavaDefined def isVal: Boolean = isTerm && !isModule && !isMethod && !isMutable - def isVar: Boolean = isTerm && !isModule && !isMethod && isMutable + def isVar: Boolean = isTerm && !isModule && !isMethod && !isLazy && isMutable def newNestedSymbol(name: Name, pos: Position, newFlags: Long, isClass: Boolean): Symbol = name match { case n: TermName => newTermSymbol(n, pos, newFlags) diff --git a/test/files/run/t6687.scala b/test/files/run/t6687.scala new file mode 100644 index 0000000000..ee44e5f0d2 --- /dev/null +++ b/test/files/run/t6687.scala @@ -0,0 +1,10 @@ +import scala.reflect.runtime.universe._ + +class A { lazy val x = 1 } + +object Test { + def main(args: Array[String]): Unit = { + val vars = typeOf[A].members.toList filter (x => x.isTerm && x.asTerm.isVar) + assert(vars.isEmpty, vars) + } +} -- cgit v1.2.3 From b02e95288b35486eab1b1dd67934e3097fa2719f Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 24 Nov 2012 01:41:15 -0800 Subject: Set symbol flags at creation. All should prefer passing flags at symbol creation to mutating the flags field after creation. --- src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 31db042942..d73460f7e3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -1143,7 +1143,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` // if there's an outer accessor, otherwise the condition becomes `true` -- TODO: can we improve needsOuterTest so there's always an outerAccessor? - val outer = expectedTp.typeSymbol.newMethod(vpmName.outer) setInfo expectedTp.prefix setFlag SYNTHETIC | ARTIFACT + val outer = expectedTp.typeSymbol.newMethod(vpmName.outer, newFlags = SYNTHETIC | ARTIFACT) setInfo expectedTp.prefix (Select(codegen._asInstanceOf(testedBinder, expectedTp), outer)) OBJ_EQ expectedOuter } @@ -3601,7 +3601,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL */ def matcher(scrut: Tree, scrutSym: Symbol, restpe: Type)(cases: List[Casegen => Tree], matchFailGen: Option[Tree => Tree]): Tree = { val matchEnd = newSynthCaseLabel("matchEnd") - val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, SYNTHETIC) setInfo restpe.withoutAnnotations + val matchRes = NoSymbol.newValueParameter(newTermName("x"), NoPosition, newFlags = SYNTHETIC) setInfo restpe.withoutAnnotations matchEnd setInfo MethodType(List(matchRes), restpe) def newCaseSym = newSynthCaseLabel("case") setInfo MethodType(Nil, restpe) -- cgit v1.2.3 From 3177934fc157bb5e37797c87d20a25e2ff0311d1 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 24 Nov 2012 01:40:12 -0800 Subject: Mark pattern matcher synthetics as SYNTHETIC. Flagging synthetics accurately is important to allow for useful static analysis. --- src/compiler/scala/tools/nsc/transform/UnCurry.scala | 2 +- src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index f6b55b6606..529009a058 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -369,7 +369,7 @@ abstract class UnCurry extends InfoTransform } val isDefinedAtMethodDef = { - val methSym = anonClass.newMethod(nme.isDefinedAt, fun.pos, FINAL) + val methSym = anonClass.newMethod(nme.isDefinedAt, fun.pos, FINAL | SYNTHETIC) val params = methSym newSyntheticValueParams formals methSym setInfoAndEnter MethodType(params, BooleanClass.tpe) diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index d73460f7e3..a8f471a396 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -1405,7 +1405,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // assert(owner ne null); assert(owner ne NoSymbol) def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x") = - NoSymbol.newTermSymbol(freshName(prefix), pos) setInfo tp + NoSymbol.newTermSymbol(freshName(prefix), pos, newFlags = SYNTHETIC) setInfo tp def newSynthCaseLabel(name: String) = NoSymbol.newLabel(freshName(name), NoPosition) setFlag treeInfo.SYNTH_CASE_FLAGS -- cgit v1.2.3 From 20c2a50d7ce29a8593e92a2806031d5c08dc31b7 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Mon, 26 Nov 2012 12:55:32 +0100 Subject: SI-6718 fixes a volatile test --- test/files/run/showraw_aliases.check | 4 ++-- test/files/run/showraw_aliases.scala | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/test/files/run/showraw_aliases.check b/test/files/run/showraw_aliases.check index 1838bf9bec..aebd354031 100644 --- a/test/files/run/showraw_aliases.check +++ b/test/files/run/showraw_aliases.check @@ -1,2 +1,2 @@ -Block(List(Import(Select(Select(Ident(scala), scala.reflect), scala.reflect.runtime), List(ImportSelector(newTermName("universe"), 52, newTermName("ru"), 64)))), Select(Select(Select(Select(Ident(scala), scala.reflect), scala.reflect.runtime), scala.reflect.runtime.package), [newTermName("universe") aka newTermName("ru")])) -Block(List(Import(Select(Select(Ident(scala#), scala.reflect#), scala.reflect.runtime#), List(ImportSelector(newTermName("universe"), 52, newTermName("ru"), 64)))), Select(Select(Select(Select(Ident(scala#), scala.reflect#), scala.reflect.runtime#), scala.reflect.runtime.package#), [newTermName("universe")# aka newTermName("ru")])) +Block(List(Import(Select(Select(Ident(scala), scala.reflect), scala.reflect.runtime), List(ImportSelector(newTermName("universe"), , newTermName("ru"), )))), Select(Select(Select(Select(Ident(scala), scala.reflect), scala.reflect.runtime), scala.reflect.runtime.package), [newTermName("universe") aka newTermName("ru")])) +Block(List(Import(Select(Select(Ident(scala#), scala.reflect#), scala.reflect.runtime#), List(ImportSelector(newTermName("universe"), , newTermName("ru"), )))), Select(Select(Select(Select(Ident(scala#), scala.reflect#), scala.reflect.runtime#), scala.reflect.runtime.package#), [newTermName("universe")# aka newTermName("ru")])) diff --git a/test/files/run/showraw_aliases.scala b/test/files/run/showraw_aliases.scala index 3a68ca37b2..65b4fcb1cd 100644 --- a/test/files/run/showraw_aliases.scala +++ b/test/files/run/showraw_aliases.scala @@ -9,7 +9,9 @@ object Test extends App { """) val ttree = tb.typeCheck(tree) - def stabilize(s: String) = """#\d+""".r.replaceAllIn(s, "#") - println(showRaw(ttree)) + def stabilizeIds(s: String) = """#\d+""".r.replaceAllIn(s, "#") + def stabilizePositions(s: String) = """\d+""".r.replaceAllIn(s, "") + def stabilize(s: String) = stabilizePositions(stabilizeIds(s)) + println(stabilize(showRaw(ttree))) println(stabilize(showRaw(ttree, printIds = true))) } \ No newline at end of file -- cgit v1.2.3 From a0001fcfd06fc2eca9ee965aeb766c832797aa9e Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 21 Nov 2012 14:01:04 +0100 Subject: Adds a margin stripping string interpolator. Currently only for compiler internal use. Designed to avoid surprises if the interpolated values themselves contain the margin delimiter. Before: val bip = "\n |.." s"""fooo |bar $bip |baz""".stripMargin "fooo bar .. baz" After: sm"""fooo |bar $bip |baz""" "fooo bar |.. baz" --- .../scala/reflect/internal/SymbolTable.scala | 6 ++++ .../internal/util/StripMarginInterpolator.scala | 40 +++++++++++++++++++++ .../scala/reflect/internal/util/package.scala | 9 +++++ test/files/run/sm-interpolator.scala | 41 ++++++++++++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 src/reflect/scala/reflect/internal/util/StripMarginInterpolator.scala create mode 100644 src/reflect/scala/reflect/internal/util/package.scala create mode 100644 test/files/run/sm-interpolator.scala diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index 554acf9c0b..02ac59a461 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -345,6 +345,12 @@ abstract class SymbolTable extends macros.Universe /** Is this symbol table a part of a compiler universe? */ def isCompilerUniverse = false + + /** + * Adds the `sm` String interpolator to a [[scala.StringContext]]. + */ + implicit val StringContextStripMarginOps: StringContext => StringContextStripMarginOps = util.StringContextStripMarginOps + } object SymbolTableStats { diff --git a/src/reflect/scala/reflect/internal/util/StripMarginInterpolator.scala b/src/reflect/scala/reflect/internal/util/StripMarginInterpolator.scala new file mode 100644 index 0000000000..e7579229b2 --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/StripMarginInterpolator.scala @@ -0,0 +1,40 @@ +package scala.reflect +package internal +package util + +trait StripMarginInterpolator { + def stringContext: StringContext + + /** + * A safe combination of `[[scala.collection.immutable.StringLike#stripMargin]] + * and [[scala.StringContext#raw]]. + * + * The margin of each line is defined by whitespace leading up to a '|' character. + * This margin is stripped '''before''' the arguments are interpolated into to string. + * + * String escape sequences are '''not''' processed; this interpolater is designed to + * be used with triple quoted Strings. + * + * {{{ + * scala> val foo = "f|o|o" + * foo: String = f|o|o + * scala> sm"""|${foo} + * |""" + * res0: String = + * "f|o|o + * " + * }}} + */ + final def sm(args: Any*): String = { + def isLineBreak(c: Char) = c == '\n' || c == '\f' // compatible with StringLike#isLineBreak + def stripTrailingPart(s: String) = { + val (pre, post) = s.span(c => !isLineBreak(c)) + pre + post.stripMargin + } + val stripped: List[String] = stringContext.parts.toList match { + case head :: tail => head.stripMargin :: (tail map stripTrailingPart) + case Nil => Nil + } + new StringContext(stripped: _*).raw(args: _*) + } +} diff --git a/src/reflect/scala/reflect/internal/util/package.scala b/src/reflect/scala/reflect/internal/util/package.scala new file mode 100644 index 0000000000..6d77235db6 --- /dev/null +++ b/src/reflect/scala/reflect/internal/util/package.scala @@ -0,0 +1,9 @@ +package scala.reflect +package internal + +package object util { + /** + * Adds the `sm` String interpolator to a [[scala.StringContext]]. + */ + implicit class StringContextStripMarginOps(val stringContext: StringContext) extends StripMarginInterpolator +} diff --git a/test/files/run/sm-interpolator.scala b/test/files/run/sm-interpolator.scala new file mode 100644 index 0000000000..7f7b9f061a --- /dev/null +++ b/test/files/run/sm-interpolator.scala @@ -0,0 +1,41 @@ +object Test extends App { + import scala.reflect.internal.util.StringContextStripMarginOps + def check(actual: Any, expected: Any) = if (actual != expected) sys.error(s"expected: [$expected], actual: [$actual])") + + val bar = "|\n ||" + + check( + sm"""|ab + |de + |${bar} | ${1}""", + "ab \nde\n|\n || | 1") + + check( + sm"|", + "") + + check( + sm"${0}", + "0") + + check( + sm"${0}", + "0") + + check( + sm"""${0}|${1} + |""", + "0|1\n") + + check( + sm""" ||""", + "|") + + check( + sm""" ${" "} ||""", + " ||") + + check( + sm"\n", + raw"\n".stripMargin) +} -- cgit v1.2.3 From 8fcbee5e2d0fcff2f42c94b9ecf6599e586c8166 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 21 Nov 2012 14:21:46 +0100 Subject: Take advantage of the margin stripping interpolator. Safer and shorter. --- src/compiler/scala/reflect/reify/Errors.scala | 8 +++---- .../scala/tools/nsc/transform/Erasure.scala | 18 +++++++------- .../tools/nsc/typechecker/ContextErrors.scala | 28 +++++++++++----------- .../scala/tools/nsc/typechecker/Implicits.scala | 16 ++++++------- .../scala/tools/nsc/typechecker/RefChecks.scala | 4 ++-- .../tools/nsc/typechecker/SuperAccessors.scala | 4 ++-- .../scala/tools/nsc/typechecker/TreeCheckers.scala | 8 +++---- .../scala/tools/nsc/typechecker/Typers.scala | 4 ++-- .../scala/reflect/runtime/JavaMirrors.scala | 17 +++++++------ 9 files changed, 52 insertions(+), 55 deletions(-) diff --git a/src/compiler/scala/reflect/reify/Errors.scala b/src/compiler/scala/reflect/reify/Errors.scala index 3d7cb95792..7c66d5b9eb 100644 --- a/src/compiler/scala/reflect/reify/Errors.scala +++ b/src/compiler/scala/reflect/reify/Errors.scala @@ -33,10 +33,10 @@ trait Errors { } def CannotConvertManifestToTagWithoutScalaReflect(tpe: Type, manifestInScope: Tree) = { - val msg = s""" - |to create a type tag here, it is necessary to interoperate with the manifest `$manifestInScope` in scope. - |however manifest -> typetag conversion requires Scala reflection, which is not present on the classpath. - |to proceed put scala-reflect.jar on your compilation classpath and recompile.""".trim.stripMargin + val msg = + sm"""to create a type tag here, it is necessary to interoperate with the manifest `$manifestInScope` in scope. + |however manifest -> typetag conversion requires Scala reflection, which is not present on the classpath. + |to proceed put scala-reflect.jar on your compilation classpath and recompile.""" throw new ReificationException(defaultErrorPosition, msg) } diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 7b45b3efe5..92743919ec 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -437,19 +437,19 @@ abstract class Erasure extends AddInterfaces noclash = false unit.error( if (member.owner == root) member.pos else root.pos, - s"""bridge generated for member ${fulldef(member)} - |which overrides ${fulldef(other)} - |clashes with definition of $what; - |both have erased type ${afterPostErasure(bridge.tpe)}""".stripMargin) + sm"""bridge generated for member ${fulldef(member)} + |which overrides ${fulldef(other)} + |clashes with definition of $what; + |both have erased type ${afterPostErasure(bridge.tpe)}""") } for (bc <- root.baseClasses) { if (settings.debug.value) afterPostErasure(println( - s"""check bridge overrides in $bc - ${bc.info.nonPrivateDecl(bridge.name)} - ${site.memberType(bridge)} - ${site.memberType(bc.info.nonPrivateDecl(bridge.name) orElse IntClass)} - ${(bridge.matchingSymbol(bc, site))}""".stripMargin)) + sm"""check bridge overrides in $bc + |${bc.info.nonPrivateDecl(bridge.name)} + |${site.memberType(bridge)} + |${site.memberType(bc.info.nonPrivateDecl(bridge.name) orElse IntClass)} + |${(bridge.matchingSymbol(bc, site))}""")) def overriddenBy(sym: Symbol) = sym.matchingSymbol(bc, site).alternatives filter (sym => !sym.isBridge) diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 9e9b8b995b..faacb60d75 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -107,9 +107,9 @@ trait ContextErrors { s"$name extends Any, not AnyRef" ) if (isPrimitiveValueType(found) || isTrivialTopType(tp)) "" else "\n" + - s"""|Note that $what. - |Such types can participate in value classes, but instances - |cannot appear in singleton types or in reference comparisons.""".stripMargin + sm"""|Note that $what. + |Such types can participate in value classes, but instances + |cannot appear in singleton types or in reference comparisons.""" } import ErrorUtils._ @@ -1128,9 +1128,9 @@ trait ContextErrors { (isView: Boolean, pt: Type, tree: Tree)(implicit context0: Context) = { if (!info1.tpe.isErroneous && !info2.tpe.isErroneous) { def coreMsg = - s"""| $pre1 ${info1.sym.fullLocationString} of type ${info1.tpe} - | $pre2 ${info2.sym.fullLocationString} of type ${info2.tpe} - | $trailer""".stripMargin + sm"""| $pre1 ${info1.sym.fullLocationString} of type ${info1.tpe} + | $pre2 ${info2.sym.fullLocationString} of type ${info2.tpe} + | $trailer""" def viewMsg = { val found :: req :: _ = pt.typeArgs def explanation = { @@ -1141,19 +1141,19 @@ trait ContextErrors { // involving Any, are further explained from foundReqMsg. if (AnyRefClass.tpe <:< req) ( if (sym == AnyClass || sym == UnitClass) ( - s"""|Note: ${sym.name} is not implicitly converted to AnyRef. You can safely - |pattern match `x: AnyRef` or cast `x.asInstanceOf[AnyRef]` to do so.""".stripMargin + sm"""|Note: ${sym.name} is not implicitly converted to AnyRef. You can safely + |pattern match `x: AnyRef` or cast `x.asInstanceOf[AnyRef]` to do so.""" ) else boxedClass get sym map (boxed => - s"""|Note: an implicit exists from ${sym.fullName} => ${boxed.fullName}, but - |methods inherited from Object are rendered ambiguous. This is to avoid - |a blanket implicit which would convert any ${sym.fullName} to any AnyRef. - |You may wish to use a type ascription: `x: ${boxed.fullName}`.""".stripMargin + sm"""|Note: an implicit exists from ${sym.fullName} => ${boxed.fullName}, but + |methods inherited from Object are rendered ambiguous. This is to avoid + |a blanket implicit which would convert any ${sym.fullName} to any AnyRef. + |You may wish to use a type ascription: `x: ${boxed.fullName}`.""" ) getOrElse "" ) else - s"""|Note that implicit conversions are not applicable because they are ambiguous: - |${coreMsg}are possible conversion functions from $found to $req""".stripMargin + sm"""|Note that implicit conversions are not applicable because they are ambiguous: + |${coreMsg}are possible conversion functions from $found to $req""" } typeErrorMsg(found, req, infer.isPossiblyMissingArgs(found, req)) + ( if (explanation == "") "" else "\n" + explanation diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 547d756888..fc10f68454 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -1306,17 +1306,17 @@ trait Implicits { else { if (ReflectRuntimeUniverse == NoSymbol) { // todo. write a test for this - context.error(pos, s""" - |to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope. - |however typetag -> manifest conversion requires Scala reflection, which is not present on the classpath. - |to proceed put scala-reflect.jar on your compilation classpath and recompile.""".trim.stripMargin) + context.error(pos, + sm"""to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope. + |however typetag -> manifest conversion requires Scala reflection, which is not present on the classpath. + |to proceed put scala-reflect.jar on your compilation classpath and recompile.""") return SearchFailure } if (resolveClassTag(pos, tp, allowMaterialization = true) == EmptyTree) { - context.error(pos, s""" - |to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope. - |however typetag -> manifest conversion requires a class tag for the corresponding type to be present. - |to proceed add a class tag to the type `$tp` (e.g. by introducing a context bound) and recompile.""".trim.stripMargin) + context.error(pos, + sm"""to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope. + |however typetag -> manifest conversion requires a class tag for the corresponding type to be present. + |to proceed add a class tag to the type `$tp` (e.g. by introducing a context bound) and recompile.""") return SearchFailure } val cm = typed(Ident(ReflectRuntimeCurrentMirror)) diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index ee7805cb3d..78ec6508ed 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1379,8 +1379,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans private def checkCompileTimeOnly(sym: Symbol, pos: Position) = { if (sym.isCompileTimeOnly) { def defaultMsg = - s"""|Reference to ${sym.fullLocationString} should not have survived past type checking, - |it should have been processed and eliminated during expansion of an enclosing macro.""".stripMargin + sm"""Reference to ${sym.fullLocationString} should not have survived past type checking, + |it should have been processed and eliminated during expansion of an enclosing macro.""" // The getOrElse part should never happen, it's just here as a backstop. unit.error(pos, sym.compileTimeOnlyMessage getOrElse defaultMsg) } diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index c9e45b6348..fb8a111da1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -527,8 +527,8 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } def isJavaProtected = host.isTrait && sym.isJavaDefined && { restrictionError(pos, unit, - s"""|$clazz accesses protected $sym inside a concrete trait method. - |Add an accessor in a class extending ${sym.enclClass} as a workaround.""".stripMargin + sm"""$clazz accesses protected $sym inside a concrete trait method. + |Add an accessor in a class extending ${sym.enclClass} as a workaround.""" ) true } diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index b4cdad70e2..48a5a36b00 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -280,11 +280,9 @@ abstract class TreeCheckers extends Analyzer { if (sym.owner != currentOwner) { val expected = currentOwner.ownerChain find (x => cond(x)) getOrElse fail("DefTree can't find owner: ") if (sym.owner != expected) - fail("""| - | currentOwner chain: %s - | symbol chain: %s""".stripMargin.format( - currentOwner.ownerChain take 3 mkString " -> ", - sym.ownerChain mkString " -> ") + fail(sm"""| + | currentOwner chain: ${currentOwner.ownerChain take 3 mkString " -> "} + | symbol chain: ${sym.ownerChain mkString " -> "}""" ) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index d3847de894..034de203c9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4863,12 +4863,12 @@ trait Typers extends Modes with Adaptations with Tags { defSym = pre.member(defEntry.sym.name) if (defSym ne defEntry.sym) { qual = gen.mkAttributedQualifier(pre) - log(s""" + log(sm""" | !!! Overloaded package object member resolved incorrectly. | prefix: $pre | Discarded: ${defEntry.sym.defString} | Using: ${defSym.defString} - """.stripMargin) + """) } } else diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 2d08cd887b..ab93d7033a 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -129,11 +129,10 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def ErrorStaticModule(sym: Symbol) = throw new ScalaReflectionException(s"$sym is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror") private def ErrorNotMember(sym: Symbol, owner: Symbol) = throw new ScalaReflectionException(s"expected a member of $owner, you provided ${sym.kindString} ${sym.fullName}") private def ErrorNotField(sym: Symbol) = throw new ScalaReflectionException(s"expected a field or an accessor method symbol, you provided $sym") - private def ErrorNonExistentField(sym: Symbol) = throw new ScalaReflectionException(s""" - |Scala field ${sym.name} isn't represented as a Java field, neither it has a Java accessor method - |note that private parameters of class constructors don't get mapped onto fields and/or accessors, - |unless they are used outside of their declaring constructors. - """.trim.stripMargin) + private def ErrorNonExistentField(sym: Symbol) = throw new ScalaReflectionException( + sm"""Scala field ${sym.name} isn't represented as a Java field, neither it has a Java accessor method + |note that private parameters of class constructors don't get mapped onto fields and/or accessors, + |unless they are used outside of their declaring constructors.""") private def ErrorSetImmutableField(sym: Symbol) = throw new ScalaReflectionException(s"cannot set an immutable field ${sym.name}") private def ErrorNotConstructor(sym: Symbol, owner: Symbol) = throw new ScalaReflectionException(s"expected a constructor of $owner, you provided $sym") private def ErrorFree(member: Symbol, freeType: Symbol) = throw new ScalaReflectionException(s"cannot reflect ${member.kindString} ${member.name}, because it's a member of a weak type ${freeType.name}") @@ -541,8 +540,8 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni val result = anns find (_.annotationType == annotClass) if (result.isEmpty && (anns exists (_.annotationType.getName == name))) throw new ClassNotFoundException( - s"""Mirror classloader mismatch: $jclazz (loaded by ${ReflectionUtils.show(jclazz.getClassLoader)}) - |is unrelated to the mirror's classloader: (${ReflectionUtils.show(classLoader)})""".stripMargin) + sm"""Mirror classloader mismatch: $jclazz (loaded by ${ReflectionUtils.show(jclazz.getClassLoader)}) + |is unrelated to the mirror's classloader: (${ReflectionUtils.show(classLoader)})""") result } def loadBytes[T: ClassTag](name: String): Option[T] = @@ -955,8 +954,8 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni javaTypeToValueClass(jclazz) orElse lookupClass assert (cls.isType, - s"""${if (cls == NoSymbol) "not a type: symbol" else "no symbol could be"} - | loaded from $jclazz in $owner with name $simpleName and classloader $classLoader""".stripMargin) + sm"""${if (cls == NoSymbol) "not a type: symbol" else "no symbol could be"} + | loaded from $jclazz in $owner with name $simpleName and classloader $classLoader""") cls.asClass } -- cgit v1.2.3 From 597a949e8700d585dc89979b66375367ac73acc2 Mon Sep 17 00:00:00 2001 From: martende Date: Tue, 27 Nov 2012 14:08:53 +0100 Subject: SI-5753 macros cannot be loaded when inherited from a class or a trait enclClass should be taken from Tree otherwise we can jump to declaration class/trait. --- src/compiler/scala/tools/nsc/typechecker/Macros.scala | 13 +++---------- test/files/run/t5753_1.check | 1 + test/files/run/t5753_1.flags | 1 + test/files/run/t5753_1/Impls_Macros_1.scala | 10 ++++++++++ test/files/run/t5753_1/Test_2.scala | 4 ++++ test/files/run/t5753_2.check | 1 + test/files/run/t5753_2.flags | 1 + test/files/run/t5753_2/Impls_Macros_1.scala | 10 ++++++++++ test/files/run/t5753_2/Test_2.scala | 4 ++++ 9 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 test/files/run/t5753_1.check create mode 100644 test/files/run/t5753_1.flags create mode 100644 test/files/run/t5753_1/Impls_Macros_1.scala create mode 100644 test/files/run/t5753_1/Test_2.scala create mode 100644 test/files/run/t5753_2.check create mode 100644 test/files/run/t5753_2.flags create mode 100644 test/files/run/t5753_2/Impls_Macros_1.scala create mode 100644 test/files/run/t5753_2/Test_2.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index bcc37e8b37..b20a9ea626 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -122,16 +122,9 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { } def pickle(macroImplRef: Tree): Tree = { - val macroImpl = macroImplRef.symbol + val MacroImplReference(owner, macroImpl, targs) = macroImplRef val paramss = macroImpl.paramss - // this logic relies on the assumptions that were valid for the old macro prototype - // namely that macro implementations can only be defined in top-level classes and modules - // with the new prototype that materialized in a SIP, macros need to be statically accessible, which is different - // for example, a macro def could be defined in a trait that is implemented by an object - // there are some more clever cases when seemingly non-static method ends up being statically accessible - // however, the code below doesn't account for these guys, because it'd take a look of time to get it right - // for now I leave it as a todo and move along to more the important stuff // todo. refactor when fixing SI-5498 def className: String = { def loop(sym: Symbol): String = sym match { @@ -143,7 +136,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { loop(sym.owner) + separator + sym.javaSimpleName.toString } - loop(macroImpl.owner.enclClass) + loop(owner) } def signature: List[Int] = { @@ -164,7 +157,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // I just named it "macro", because it's macro-related, but I could as well name it "foobar" val nucleus = Ident(newTermName("macro")) val wrapped = Apply(nucleus, payload map { case (k, v) => Assign(pickleAtom(k), pickleAtom(v)) }) - val pickle = gen.mkTypeApply(wrapped, treeInfo.typeArguments(macroImplRef.duplicate)) + val pickle = gen.mkTypeApply(wrapped, targs map (_.duplicate)) // assign NoType to all freshly created AST nodes // otherwise pickler will choke on tree.tpe being null diff --git a/test/files/run/t5753_1.check b/test/files/run/t5753_1.check new file mode 100644 index 0000000000..f70d7bba4a --- /dev/null +++ b/test/files/run/t5753_1.check @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/test/files/run/t5753_1.flags b/test/files/run/t5753_1.flags new file mode 100644 index 0000000000..cd66464f2f --- /dev/null +++ b/test/files/run/t5753_1.flags @@ -0,0 +1 @@ +-language:experimental.macros \ No newline at end of file diff --git a/test/files/run/t5753_1/Impls_Macros_1.scala b/test/files/run/t5753_1/Impls_Macros_1.scala new file mode 100644 index 0000000000..1664301f5f --- /dev/null +++ b/test/files/run/t5753_1/Impls_Macros_1.scala @@ -0,0 +1,10 @@ +import scala.reflect.macros.Context +import language.experimental.macros + +trait Impls { + def impl(c: Context)(x: c.Expr[Any]) = x +} + +object Macros extends Impls { + def foo(x: Any) = macro impl +} \ No newline at end of file diff --git a/test/files/run/t5753_1/Test_2.scala b/test/files/run/t5753_1/Test_2.scala new file mode 100644 index 0000000000..a2777638bc --- /dev/null +++ b/test/files/run/t5753_1/Test_2.scala @@ -0,0 +1,4 @@ +object Test extends App { + import Macros._ + println(foo(42)) +} \ No newline at end of file diff --git a/test/files/run/t5753_2.check b/test/files/run/t5753_2.check new file mode 100644 index 0000000000..f70d7bba4a --- /dev/null +++ b/test/files/run/t5753_2.check @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/test/files/run/t5753_2.flags b/test/files/run/t5753_2.flags new file mode 100644 index 0000000000..cd66464f2f --- /dev/null +++ b/test/files/run/t5753_2.flags @@ -0,0 +1 @@ +-language:experimental.macros \ No newline at end of file diff --git a/test/files/run/t5753_2/Impls_Macros_1.scala b/test/files/run/t5753_2/Impls_Macros_1.scala new file mode 100644 index 0000000000..e23c0b938b --- /dev/null +++ b/test/files/run/t5753_2/Impls_Macros_1.scala @@ -0,0 +1,10 @@ +import scala.reflect.macros.{Context => Ctx} + +trait Macro_T { + def foo[T](c: Ctx)(s: c.Expr[T]) = s +} + +object Macros { + def foo[T](s: T) = macro Impls.foo[T] + object Impls extends Macro_T +} diff --git a/test/files/run/t5753_2/Test_2.scala b/test/files/run/t5753_2/Test_2.scala new file mode 100644 index 0000000000..a2777638bc --- /dev/null +++ b/test/files/run/t5753_2/Test_2.scala @@ -0,0 +1,4 @@ +object Test extends App { + import Macros._ + println(foo(42)) +} \ No newline at end of file -- cgit v1.2.3 From 1be02447823f309c7450299ba7128da4b86573ad Mon Sep 17 00:00:00 2001 From: martende Date: Tue, 27 Nov 2012 17:30:20 +0100 Subject: neg test added --- test/files/neg/t5753.check | 4 ++++ test/files/neg/t5753.flags | 1 + test/files/neg/t5753/Impls$class.class | Bin 0 -> 626 bytes test/files/neg/t5753/Impls.class | Bin 0 -> 866 bytes test/files/neg/t5753/Impls_Macros_1.scala | 6 ++++++ test/files/neg/t5753/Test_2.scala | 11 +++++++++++ 6 files changed, 22 insertions(+) create mode 100644 test/files/neg/t5753.check create mode 100644 test/files/neg/t5753.flags create mode 100644 test/files/neg/t5753/Impls$class.class create mode 100644 test/files/neg/t5753/Impls.class create mode 100644 test/files/neg/t5753/Impls_Macros_1.scala create mode 100644 test/files/neg/t5753/Test_2.scala diff --git a/test/files/neg/t5753.check b/test/files/neg/t5753.check new file mode 100644 index 0000000000..76602de17d --- /dev/null +++ b/test/files/neg/t5753.check @@ -0,0 +1,4 @@ +Test_2.scala:9: error: macro implementation not found: foo (the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them) + println(foo(42)) + ^ +one error found diff --git a/test/files/neg/t5753.flags b/test/files/neg/t5753.flags new file mode 100644 index 0000000000..cd66464f2f --- /dev/null +++ b/test/files/neg/t5753.flags @@ -0,0 +1 @@ +-language:experimental.macros \ No newline at end of file diff --git a/test/files/neg/t5753/Impls$class.class b/test/files/neg/t5753/Impls$class.class new file mode 100644 index 0000000000..476329174e Binary files /dev/null and b/test/files/neg/t5753/Impls$class.class differ diff --git a/test/files/neg/t5753/Impls.class b/test/files/neg/t5753/Impls.class new file mode 100644 index 0000000000..dfcf89ed44 Binary files /dev/null and b/test/files/neg/t5753/Impls.class differ diff --git a/test/files/neg/t5753/Impls_Macros_1.scala b/test/files/neg/t5753/Impls_Macros_1.scala new file mode 100644 index 0000000000..1d9c26458c --- /dev/null +++ b/test/files/neg/t5753/Impls_Macros_1.scala @@ -0,0 +1,6 @@ +import scala.reflect.macros.{Context => Ctx} + +trait Impls { +def impl(c: Ctx)(x: c.Expr[Any]) = x +} + diff --git a/test/files/neg/t5753/Test_2.scala b/test/files/neg/t5753/Test_2.scala new file mode 100644 index 0000000000..2369b18e76 --- /dev/null +++ b/test/files/neg/t5753/Test_2.scala @@ -0,0 +1,11 @@ +import scala.reflect.macros.{Context => Ctx} + +object Macros extends Impls { + def foo(x: Any) = macro impl +} + +object Test extends App { + import Macros._ + println(foo(42)) +} + -- cgit v1.2.3 From dac1488a889ff5952ff85e87ec4acd7d72a74891 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 28 Nov 2012 12:03:33 +0100 Subject: Fix for SI-6731, dropped trees in selectDynamic. I rewrote mkInvoke entirely, and boosted the test coverage. --- .../scala/tools/nsc/typechecker/Typers.scala | 85 ++++++------ test/files/run/t6731.check | 40 ++++++ test/files/run/t6731.scala | 143 +++++++++++++++++++++ 3 files changed, 221 insertions(+), 47 deletions(-) create mode 100644 test/files/run/t6731.check create mode 100644 test/files/run/t6731.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index a2aca45e8f..757107a9b6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3926,54 +3926,45 @@ trait Typers extends Modes with Adaptations with Tags { * */ def mkInvoke(cxTree: Tree, tree: Tree, qual: Tree, name: Name): Option[Tree] = { - debuglog(s"mkInvoke($cxTree, $tree, $qual, $name)") + log(s"dyna.mkInvoke($cxTree, $tree, $qual, $name)") + val treeSelection = treeInfo.methPart(tree) + def isDesugaredApply = treeSelection match { + case Select(`qual`, nme.apply) => true + case _ => false + } acceptsApplyDynamicWithType(qual, name) map { tp => - // tp eq NoType => can call xxxDynamic, but not passing any type args (unless specified explicitly by the user) - // in scala-virtualized, when not NoType, tp is passed as type argument (for selection on a staged Struct) - - // strip off type application -- we're not doing much with outer, - // so don't bother preserving cxTree's attributes etc - val cxTree1 = cxTree match { - case t: ValOrDefDef => t.rhs - case t => t - } - val cxTree2 = cxTree1 match { - case Typed(t, tpe) => t // ignore outer type annotation - case t => t - } - val (outer, explicitTargs) = cxTree2 match { - case TypeApply(fun, targs) => (fun, targs) - case Apply(TypeApply(fun, targs), args) => (Apply(fun, args), targs) - case Select(TypeApply(fun, targs), nme) => (Select(fun, nme), targs) - case t => (t, Nil) - } - def hasNamedArg(as: List[Tree]) = as.collectFirst{case AssignOrNamedArg(lhs, rhs) =>}.nonEmpty - - def desugaredApply = tree match { - case Select(`qual`, nme.apply) => true - case _ => false + // If tp == NoType, pass only explicit type arguments to applyXXX. Not used at all + // here - it is for scala-virtualized, where tp will be passed as an argument (for + // selection on a staged Struct) + def hasNamed(args: List[Tree]): Boolean = args exists (_.isInstanceOf[AssignOrNamedArg]) + // not supported: foo.bar(a1,..., an: _*) + def hasStar(args: List[Tree]) = treeInfo.isWildcardStarArgList(args) + def applyOp(args: List[Tree]) = if (hasNamed(args)) nme.applyDynamicNamed else nme.applyDynamic + def matches(t: Tree) = isDesugaredApply || treeInfo.methPart(t) == treeSelection + + /** Note that the trees which arrive here are potentially some distance from + * the trees of direct interest. `cxTree` is some enclosing expression which + * may apparently be arbitrarily larger than `tree`; and `tree` itself is + * too small, having at least in some cases lost its explicit type parameters. + * This logic is designed to use `tree` to pinpoint the immediately surrounding + * Apply/TypeApply/Select node, and only then creates the dynamic call. + * See SI-6731 among others. + */ + def findSelection(t: Tree): Option[(TermName, Tree)] = t match { + case Apply(fn, args) if hasStar(args) => DynamicVarArgUnsupported(tree, applyOp(args)) ; None + case Apply(fn, args) if matches(fn) => Some((applyOp(args), fn)) + case Assign(lhs, _) if matches(lhs) => Some((nme.updateDynamic, lhs)) + case _ if matches(t) => Some((nme.selectDynamic, t)) + case _ => t.children flatMap findSelection headOption } - // note: context.tree includes at most one Apply node - // thus, we can't use it to detect we're going to receive named args in expressions such as: - // qual.sel(a)(a2, arg2 = "a2") - val oper = outer match { - case Apply(q, as) if q == tree || desugaredApply => - val oper = - if (hasNamedArg(as)) nme.applyDynamicNamed - else nme.applyDynamic - // not supported: foo.bar(a1,..., an: _*) - if (treeInfo.isWildcardStarArgList(as)) { - DynamicVarArgUnsupported(tree, oper) - return Some(setError(tree)) - } else oper - case Assign(`tree`, _) => nme.updateDynamic - case _ => nme.selectDynamic + findSelection(cxTree) match { + case Some((opName, tapply)) => + val targs = treeInfo.typeArguments(tapply) + val fun = gen.mkTypeApply(Select(qual, opName), targs) + atPos(qual.pos)(Apply(fun, Literal(Constant(name.decode)) :: Nil)) + case _ => + setError(tree) } - - val dynSel = Select(qual, oper) - val tappSel = if (explicitTargs.nonEmpty) TypeApply(dynSel, explicitTargs) else dynSel - - atPos(qual.pos)(Apply(tappSel, List(Literal(Constant(name.decode))))) } } @@ -3981,7 +3972,7 @@ trait Typers extends Modes with Adaptations with Tags { silent(typeTree) match { case SilentResultValue(r) => r case SilentTypeError(err) => DynamicRewriteError(tree, err) - } + } } } @@ -5375,7 +5366,7 @@ trait Typers extends Modes with Adaptations with Tags { 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/test/files/run/t6731.check b/test/files/run/t6731.check new file mode 100644 index 0000000000..a5d59bd378 --- /dev/null +++ b/test/files/run/t6731.check @@ -0,0 +1,40 @@ +Mono$.bar() +Mono$.bar +Mono$.bar() +Mono$.bar +Mono$.bar +Mono$.baz +Mono$.bar(bippy=1, boppy=2) +Mono$.baz +Poly.bar[Nothing] +Poly.bar[Int] +Poly.bar[Nothing]() +Poly.bar[Int]() +Poly.bar[Int](1, 2, 3) +Poly.bar[Nothing] +Poly.bar[Int] +Poly.bar[Nothing]() +Poly.bar[Int]() +Poly.bar[Int](1, 2, 3) +Updating.bar +Updating.bar = b +Nest1$Nest2$Nest3$.bippy(1, 2, 3) +Nest1$Nest2$Nest3$.bippy +Named.bippy(a=1, b=2) +Named.boppy(c=3, d=4) +Named.apply() +Named.apply() +Named.apply(e=5, f=6) +Named2.bippy(1)(q0, c) +Named2.bippy(1)(q0, c) +Named2.bippy(1)(b, q0) +Named2.bippy(1)(q0, c) +Named2.bippy(1)(c, b) +Named2.bippy(1)(b, c) +Named2.bippy(1)(q0, c) +Named2.bippy(2)(b, c) +Named2.bippy(1)(q0, c) +Named2.bippy(5)(b, c) +Named2.dingus(100)(b, dong) +Named2.bippy(1)(q0, q1) +Named2.hello(100)(!!, !!) diff --git a/test/files/run/t6731.scala b/test/files/run/t6731.scala new file mode 100644 index 0000000000..89d212e91e --- /dev/null +++ b/test/files/run/t6731.scala @@ -0,0 +1,143 @@ +import scala.language.dynamics +import scala.reflect.{ ClassTag, classTag } + +object Util { + def show[T](x: T): T = { println(x) ; x } + def mkArgs(xs: Any*) = xs map { case ((k, v)) => k + "=" + v ; case x => "" + x } mkString ("(", ", ", ")") +} +import Util._ + +abstract class MonoDynamic extends Dynamic { + def selectDynamic(name: String): String = show(this + "." + name) + def applyDynamic(name: String)(args: Any*): String = show(this + "." + name + mkArgs(args: _*)) + def applyDynamicNamed(name: String)(args: (String, Any)*): String = show(this + "." + name + mkArgs(args: _*)) + + override def toString = this.getClass.getName split '.' last +} + +object Mono extends MonoDynamic { + def f(s: String): String = s + + def f1 = this.bar() + def f2 = this.bar + def f3 = f(this.bar()) + def f4 = f(this.bar) + def f5 = f(f(f(f(f(f(this.bar)))))) + f(f(f(f(f(f(this.baz)))))) + def f6 = f(f(f(f(f(f(this.bar(bippy = 1, boppy = 2))))))) + f(f(f(f(f(f(this.baz)))))) +} + +object Poly extends Dynamic { + def selectDynamic[T: ClassTag](name: String): String = show(s"$this.$name[${classTag[T]}]") + def applyDynamic[T: ClassTag](name: String)(args: Any*): String = show(args.mkString(s"$this.$name[${classTag[T]}](", ", ", ")")) + + def f(s: String): String = s + + def f1 = this.bar + def f2 = this.bar[Int] + def f3 = this.bar() + def f4 = this.bar[Int]() + def f5 = this.bar[Int](1, 2, 3) + + def f6 = f(f(this.bar)) + def f7 = f(f(this.bar[Int])) + def f8 = f(f(this.bar())) + def f9 = f(f(this.bar[Int]())) + def f10 = f(f(this.bar[Int](1, 2, 3))) + + override def toString = "Poly" +} + +object Updating extends Dynamic { + def selectDynamic(name: String): String = show(s"$this.$name") + def updateDynamic(name: String)(value: Any): String = show(s"$this.$name = $value") + + def f1 = this.bar + def f2 = this.bar = "b" + + override def toString = "Updating" +} + +object Nest1 extends Dynamic { + def applyDynamic(name: String)(args: Any*): Nest2.type = Nest2 + + object Nest2 extends Dynamic { + def applyDynamicNamed(name: String)(args: (String, Any)*): Nest3.type = Nest3 + + object Nest3 extends MonoDynamic { + + } + } + + def f1 = Nest1.bip().bop(foo = "bar").bippy(1, 2, 3) + def f2 = Nest1.bip("abc").bop(foo = 5).bippy +} + +object Named extends Dynamic { + def applyDynamic(name: String)(args: Any*): Named.type = { + show(this + "." + name + mkArgs(args: _*)) + this + } + def applyDynamicNamed(name: String)(args: (String, Any)*): Named.type = { + show(this + "." + name + mkArgs(args: _*)) + this + } + + def f1 = this.bippy(a = 1, b = 2).boppy(c = 3, d = 4)()()(e = 5, f = 6) + override def toString = "Named" +} + +object Named2 extends Dynamic { + def applyDynamic(name: String)(a: Any)(b: Any = "b", c: Any = "c"): Named2.type = { + show(this + "." + name + mkArgs(a) + mkArgs(b, c)) + this + } + def applyDynamicNamed(name: String)(a: (String, Any))(b: (String, Any), c: (String, Any)): Named2.type = { + show(this + "." + name + mkArgs(a) + mkArgs(b, c)) + this + } + + def f1 = this.bippy(1)(b = "q0") + def f2 = this.bippy(1)("q0") + def f3 = this.bippy(1)(c = "q0") + def f4 = this.bippy(1)("q0") + def f5 = this.bippy(1)(c = "b", b = "c") + def f6 = this.bippy(1)("b", "c") + def f7 = this.bippy(1)(b = "q0").bippy(2)() + def f8 = this.bippy(1)("q0").bippy(5)(c = "c").dingus(100)(c = "dong") + def f9 = this.bippy(1)(b = "q0", c = "q1").hello(100)("!!", "!!") + + override def toString = "Named2" +} + + +object Test { + def main(args: Array[String]): Unit = { + { + import Mono._ + f1 ; f2 ; f3 ; f4 ; f5 + f6 + } + { + import Poly._ + f1 ; f2 ; f3 ; f4 ; f5 + f6 ; f7 ; f8 ; f9 ; f10 + } + { + import Updating._ + f1 ; f2 + } + { + import Nest1._ + f1 ; f2 + } + { + import Named._ + f1 + } + { + import Named2._ + f1 ; f2 ; f3 ; f4 ; f5 + f6 ; f7 ; f8 ; f9 + } + } +} -- cgit v1.2.3 From d55840e07586ceb11c18ad4c75f4bc726d03aeb6 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 29 Nov 2012 05:17:48 +0100 Subject: Asserts about Tree qualifiers. Encoding recent revelations about certain tree invariants in the form of asserts. --- src/compiler/scala/tools/nsc/ast/Trees.scala | 2 +- src/compiler/scala/tools/nsc/ast/parser/Parsers.scala | 2 +- src/reflect/scala/reflect/internal/Trees.scala | 19 ++++++++++++++++--- test/files/neg/gadts1.check | 4 ++-- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index ed73464c93..296d55fec5 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -56,7 +56,7 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => * The class `C` is stored as a tree attachment. */ case class InjectDerivedValue(arg: Tree) - extends SymTree + extends SymTree with TermTree class PostfixSelect(qual: Tree, name: Name) extends Select(qual, name) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index a2a45d0684..074fcabec8 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1933,7 +1933,7 @@ self => case _ => } val typeAppliedTree = in.token match { - case LBRACKET => atPos(start, in.offset)(TypeApply(convertToTypeId(t), typeArgs())) + case LBRACKET => atPos(start, in.offset)(AppliedTypeTree(convertToTypeId(t), typeArgs())) case _ => t } in.token match { diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index b2158de9ec..6df4b75a88 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -391,6 +391,9 @@ trait Trees extends api.Trees { self: SymbolTable => case class TypeApply(fun: Tree, args: List[Tree]) extends GenericApply with TypeApplyApi { + + assert(fun.isTerm, fun) + override def symbol: Symbol = fun.symbol override def symbol_=(sym: Symbol) { fun.symbol = sym } } @@ -426,7 +429,11 @@ trait Trees extends api.Trees { self: SymbolTable => object This extends ThisExtractor case class Select(qualifier: Tree, name: Name) - extends RefTree with SelectApi + extends RefTree with SelectApi { + + // !!! assert disabled due to test case pos/annotDepMethType.scala triggering it. + // assert(qualifier.isTerm, qualifier) + } object Select extends SelectExtractor case class Ident(name: Name) extends RefTree with IdentContextApi { @@ -458,7 +465,10 @@ trait Trees extends api.Trees { self: SymbolTable => object SingletonTypeTree extends SingletonTypeTreeExtractor case class SelectFromTypeTree(qualifier: Tree, name: TypeName) - extends RefTree with TypTree with SelectFromTypeTreeApi + extends RefTree with TypTree with SelectFromTypeTreeApi { + + assert(qualifier.isType, qualifier) + } object SelectFromTypeTree extends SelectFromTypeTreeExtractor case class CompoundTypeTree(templ: Template) @@ -467,6 +477,9 @@ trait Trees extends api.Trees { self: SymbolTable => case class AppliedTypeTree(tpt: Tree, args: List[Tree]) extends TypTree with AppliedTypeTreeApi { + + assert(tpt.isType, tpt) + override def symbol: Symbol = tpt.symbol override def symbol_=(sym: Symbol) { tpt.symbol = sym } } @@ -1314,7 +1327,7 @@ trait Trees extends api.Trees { self: SymbolTable => class ChangeOwnerTraverser(val oldowner: Symbol, val newowner: Symbol) extends Traverser { final def change(sym: Symbol) = { - if (sym != NoSymbol && sym.owner == oldowner) + if (sym != NoSymbol && sym.owner == oldowner) sym.owner = newowner } override def traverse(tree: Tree) { diff --git a/test/files/neg/gadts1.check b/test/files/neg/gadts1.check index 44d2b114d6..a5e3e0de10 100644 --- a/test/files/neg/gadts1.check +++ b/test/files/neg/gadts1.check @@ -3,9 +3,9 @@ gadts1.scala:15: error: type mismatch; required: a case NumTerm(n) => c.x = Double(1.0) ^ -gadts1.scala:20: error: class Cell of type Test.Cell does not take type parameters. +gadts1.scala:20: error: Test.Cell[a] does not take parameters case Cell[a](x: Int) => c.x = 5 - ^ + ^ gadts1.scala:20: error: type mismatch; found : Int(5) required: a -- cgit v1.2.3 From a6941944bf80f660722e9151801776715c3e4ab5 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 28 Nov 2012 18:36:47 +0100 Subject: Test cases for SI-5726, SI-5733, SI-6320, SI-6551, SI-6722. All tickets involving selectDynamic fixed by the prior commit. It also fixes SI-6663, but that already has a test case. --- test/files/pos/t5726.scala | 17 +++++++++++++++ test/files/pos/t6551.scala | 2 +- test/files/pos/t6722.scala | 11 ++++++++++ test/files/run/t5733.check | 2 ++ test/files/run/t5733.scala | 53 ++++++++++++++++++++++++++++++++++++++++++++++ test/files/run/t6320.check | 17 +++++++++++++++ test/files/run/t6320.scala | 9 ++++++++ 7 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 test/files/pos/t5726.scala create mode 100644 test/files/pos/t6722.scala create mode 100644 test/files/run/t5733.check create mode 100644 test/files/run/t5733.scala create mode 100644 test/files/run/t6320.check create mode 100644 test/files/run/t6320.scala diff --git a/test/files/pos/t5726.scala b/test/files/pos/t5726.scala new file mode 100644 index 0000000000..b28ebd8674 --- /dev/null +++ b/test/files/pos/t5726.scala @@ -0,0 +1,17 @@ +import scala.language.dynamics + +class DynamicTest extends Dynamic { + def selectDynamic(name: String) = s"value of $name" + def updateDynamic(name: String)(value: Any) { + println(s"You have just updated property '$name' with value: $value") + } +} + +object MyApp extends App { + def testing() { + val test = new DynamicTest + test.firstName = "John" + } + + testing() +} diff --git a/test/files/pos/t6551.scala b/test/files/pos/t6551.scala index fb68663809..8bb396a19f 100644 --- a/test/files/pos/t6551.scala +++ b/test/files/pos/t6551.scala @@ -1,4 +1,4 @@ -import language.dynamics +import scala.language.dynamics object Test { def main(args: Array[String]) { diff --git a/test/files/pos/t6722.scala b/test/files/pos/t6722.scala new file mode 100644 index 0000000000..576746c915 --- /dev/null +++ b/test/files/pos/t6722.scala @@ -0,0 +1,11 @@ +import scala.language.dynamics + +class Dyn extends Dynamic { + def selectDynamic(s: String): Dyn = new Dyn + def get[T]: T = null.asInstanceOf[T] +} + +object Foo { + val dyn = new Dyn + dyn.foo.bar.baz.get[String] +} diff --git a/test/files/run/t5733.check b/test/files/run/t5733.check new file mode 100644 index 0000000000..e697046a94 --- /dev/null +++ b/test/files/run/t5733.check @@ -0,0 +1,2 @@ +Running ABTest asserts +Done diff --git a/test/files/run/t5733.scala b/test/files/run/t5733.scala new file mode 100644 index 0000000000..360264e4ed --- /dev/null +++ b/test/files/run/t5733.scala @@ -0,0 +1,53 @@ +import scala.language.dynamics + +object A extends Dynamic { + var a = "a" + + def selectDynamic(method:String): String = a + + def updateDynamic(method:String)(v:String) { a = v } +} + +class B extends Dynamic { + var b = "b" + + def selectDynamic(method:String): String = b + + def updateDynamic(method:String)(v:String) { b = v } +} + +object Test extends App { + assert( A.foo == "a" ) + assert( A.bar == "a" ) + A.aaa = "aaa" + assert( A.bar == "aaa" ) + + val b = new B + assert( b.foo == "b" ) + assert( b.bar == "b" ) + b.bbb = "bbb" + assert( b.bar == "bbb" ) + + { + println("Running ABTest asserts") + A.a = "a" + (new ABTest).test() + } + + println("Done") +} + +class ABTest { + def test() { + assert( A.foo == "a" ) + assert( A.bar == "a" ) + A.aaa = "aaa" + assert( A.bar == "aaa" ) + + val b = new B + assert( b.foo == "b" ) + assert( b.bar == "b" ) + b.bbb = "bbb" + assert( b.bar == "bbb" ) + } +} diff --git a/test/files/run/t6320.check b/test/files/run/t6320.check new file mode 100644 index 0000000000..e56bacd223 --- /dev/null +++ b/test/files/run/t6320.check @@ -0,0 +1,17 @@ +Type in expressions to have them evaluated. +Type :help for more information. + +scala> + +scala> import scala.language.dynamics +import scala.language.dynamics + +scala> class Dyn(m: Map[String, Any]) extends Dynamic { def selectDynamic[T](s: String): T = m(s).asInstanceOf[T] } +defined class Dyn + +scala> new Dyn(Map("foo" -> 10)).foo[Int] +res0: Int = 10 + +scala> + +scala> diff --git a/test/files/run/t6320.scala b/test/files/run/t6320.scala new file mode 100644 index 0000000000..26085a3d7d --- /dev/null +++ b/test/files/run/t6320.scala @@ -0,0 +1,9 @@ +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + def code = """ +import scala.language.dynamics +class Dyn(m: Map[String, Any]) extends Dynamic { def selectDynamic[T](s: String): T = m(s).asInstanceOf[T] } +new Dyn(Map("foo" -> 10)).foo[Int] + """ +} -- cgit v1.2.3 From e6441f16e8c85b43537f227e8e1a5d27a867a0ea Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 30 Nov 2012 09:00:53 +0100 Subject: SI-6685 fixes error handling in typedApply When MissingClassTagError doesn't lead to an exception, but rather silently sets an error, we need to bubble the resulting erroneous tree up the responsibility chain instead of mindlessly typechecking this again. This wasn't an issue before, because as far as I can guess the aforementioned error setter was always throwing exceptions in the most common usage scenarios (therefore the typecheck-again-fail-again vicious loop wasn't triggered). --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index d51ebc7d08..934ba91ac8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4516,12 +4516,11 @@ trait Typers extends Modes with Adaptations with Tags { // [Eugene] no more MaxArrayDims. ClassTags are flexible enough to allow creation of arrays of arbitrary dimensionality (w.r.t JVM restrictions) val Some((level, componentType)) = erasure.GenericArray.unapply(tpt.tpe) val tagType = List.iterate(componentType, level)(tpe => appliedType(ArrayClass.toTypeConstructor, List(tpe))).last - val newArrayApp = atPos(tree.pos) { + atPos(tree.pos) { val tag = resolveClassTag(tree.pos, tagType) if (tag.isEmpty) MissingClassTagError(tree, tagType) - else new ApplyToImplicitArgs(Select(tag, nme.newArray), args) + else typed(new ApplyToImplicitArgs(Select(tag, nme.newArray), args)) } - typed(newArrayApp, mode, pt) case Apply(Select(fun, nme.apply), _) if treeInfo.isSuperConstrCall(fun) => //SI-5696 TooManyArgumentListsForConstructor(tree) case tree1 => -- cgit v1.2.3 From eccdc2fefcf0839e09bb9c63c89baed8cf09e60f Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 2 Oct 2012 11:21:16 -0700 Subject: Fix for rangepos crasher. wrapClassTagUnapply was generating an unpositioned tree which would crash under -Yrangepos. See SI-6338. --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 5 +++-- test/files/pos/classtag-pos.flags | 1 + test/files/pos/classtag-pos.scala | 5 +++++ 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 test/files/pos/classtag-pos.flags create mode 100644 test/files/pos/classtag-pos.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 934ba91ac8..b823e1ad0f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3362,12 +3362,13 @@ trait Typers extends Modes with Adaptations with Tags { // println(util.Position.formatMessage(uncheckedPattern.pos, "made unchecked type test into a checked one", true)) val args = List(uncheckedPattern) + val app = atPos(uncheckedPattern.pos)(Apply(classTagExtractor, args)) // must call doTypedUnapply directly, as otherwise we get undesirable rewrites // and re-typechecks of the target of the unapply call in PATTERNmode, // this breaks down when the classTagExtractor (which defineds the unapply member) is not a simple reference to an object, // but an arbitrary tree as is the case here - doTypedUnapply(Apply(classTagExtractor, args), classTagExtractor, classTagExtractor, args, PATTERNmode, pt) - } + doTypedUnapply(app, classTagExtractor, classTagExtractor, args, PATTERNmode, pt) + } // if there's a ClassTag that allows us to turn the unchecked type test for `pt` into a checked type test // return the corresponding extractor (an instance of ClassTag[`pt`]) diff --git a/test/files/pos/classtag-pos.flags b/test/files/pos/classtag-pos.flags new file mode 100644 index 0000000000..281f0a10cd --- /dev/null +++ b/test/files/pos/classtag-pos.flags @@ -0,0 +1 @@ +-Yrangepos diff --git a/test/files/pos/classtag-pos.scala b/test/files/pos/classtag-pos.scala new file mode 100644 index 0000000000..768d2e27f4 --- /dev/null +++ b/test/files/pos/classtag-pos.scala @@ -0,0 +1,5 @@ +import scala.reflect.runtime.universe._ + +class A { + def f[T: TypeTag] = typeOf[T] match { case TypeRef(_, _, args) => args } +} -- cgit v1.2.3 From e791a92667653071e34c53071dc7ebc74532f1b8 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Wed, 5 Dec 2012 08:52:35 -0500 Subject: Fixing OSGi distribution. There was some kind of wierd filesystem issue where ANT would overwrite jars or not, depending on timestamps. It was a non-repeatable failure. Rather than overwrite JARs and rely on ANT, let's just spell out the non-OSGI bundles. While I had hoped to avoid hard-coding these, it's probably best we've done so. --- build.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/build.xml b/build.xml index 3cfbd454e2..a828d71a44 100644 --- a/build.xml +++ b/build.xml @@ -2672,7 +2672,12 @@ DISTRIBUTION - + + + + + + -- cgit v1.2.3