diff options
Diffstat (limited to 'src/compiler')
11 files changed, 234 insertions, 68 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index ac2780f53e..0d804e6e9f 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -2191,9 +2191,15 @@ abstract class GenASM extends SubComponent with BytecodeWriters { val st = pending.getOrElseUpdate(lv, mutable.Stack.empty[Label])
st.push(start)
}
- def popScope(lv: Local, end: Label) {
- val start = pending(lv).pop()
- seen ::= LocVarEntry(lv, start, end)
+ def popScope(lv: Local, end: Label, iPos: Position) {
+ pending.get(lv) match {
+ case Some(st) if st.nonEmpty =>
+ val start = st.pop()
+ seen ::= LocVarEntry(lv, start, end)
+ case _ =>
+ // TODO SI-6049
+ getCurrentCUnit().warning(iPos, "Visited SCOPE_EXIT before visiting corresponding SCOPE_ENTER. SI-6049")
+ }
}
def getMerged(): collection.Map[Local, List[Interval]] = {
@@ -2406,7 +2412,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { // similarly, these labels aren't tracked in the `labels` map.
val end = new asm.Label
jmethod.visitLabel(end)
- scoping.popScope(lv, end)
+ scoping.popScope(lv, end, instr.pos)
}
}
diff --git a/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala b/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala index f622f11ffd..afb8985700 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTest.scala @@ -82,13 +82,17 @@ abstract class InteractiveTest /** Test's entry point */ def main(args: Array[String]) { + try execute() + finally shutdown() + } + + protected def execute(): Unit = { loadSources() - runTests() - shutdown() + runDefaultTests() } /** Load all sources before executing the test. */ - private def loadSources() { + protected def loadSources() { // ask the presentation compiler to track all sources. We do // not wait for the file to be entirely typed because we do want // to exercise the presentation compiler on scoped type requests. @@ -100,7 +104,7 @@ abstract class InteractiveTest } /** Run all defined `PresentationCompilerTestDef` */ - protected def runTests() { + protected def runDefaultTests() { //TODO: integrate random tests!, i.e.: if (runRandomTests) randomTests(20, sourceFiles) testActions.foreach(_.runTest()) } @@ -109,7 +113,7 @@ abstract class InteractiveTest private def randomTests(n: Int, files: Array[SourceFile]) { val tester = new Tester(n, files, settings) { override val compiler = self.compiler - override val reporter = compilerReporter + override val reporter = new reporters.StoreReporter } tester.run() } diff --git a/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTestSettings.scala b/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTestSettings.scala index 36671555d1..4d85ab9d88 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTestSettings.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/InteractiveTestSettings.scala @@ -4,10 +4,8 @@ package tests import java.io.File.pathSeparatorChar import java.io.File.separatorChar - import scala.tools.nsc.interactive.tests.core.PresentationCompilerInstance -import scala.tools.nsc.io.File - +import scala.tools.nsc.io.{File,Path} import core.Reporter import core.TestSettings @@ -46,6 +44,11 @@ trait InteractiveTestSettings extends TestSettings with PresentationCompilerInst println("error processing arguments (unprocessed: %s)".format(rest)) case _ => () } + + // Make the --sourcepath path provided in the .flags file (if any) relative to the test's base directory + if(settings.sourcepath.isSetByUser) + settings.sourcepath.value = (baseDir / Path(settings.sourcepath.value)).path + adjustPaths(settings.bootclasspath, settings.classpath, settings.javabootclasspath, settings.sourcepath) } diff --git a/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala b/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala index 8ccb5aa075..5c1837b3bf 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala @@ -2,12 +2,15 @@ package scala.tools.nsc package interactive package tests.core -import reporters.StoreReporter +import reporters.{Reporter => CompilerReporter} +import scala.reflect.internal.util.Position /** Trait encapsulating the creation of a presentation compiler's instance.*/ -private[tests] trait PresentationCompilerInstance { +private[tests] trait PresentationCompilerInstance extends TestSettings { protected val settings = new Settings - protected val compilerReporter = new StoreReporter + protected val compilerReporter: CompilerReporter = new InteractiveReporter { + override def compiler = PresentationCompilerInstance.this.compiler + } protected lazy val compiler: Global = { prepareSettings(settings) diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 49f5fca19d..ba6c43f9d3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -562,7 +562,9 @@ trait ContextErrors { // SelectFromTypeTree def TypeSelectionFromVolatileTypeError(tree: Tree, qual: Tree) = { - issueNormalTypeError(tree, "illegal type selection from volatile type "+qual.tpe) + val hiBound = qual.tpe.bounds.hi + val addendum = if (hiBound =:= qual.tpe) "" else s" (with upper bound ${hiBound})" + issueNormalTypeError(tree, s"illegal type selection from volatile type ${qual.tpe}${addendum}") setError(tree) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 6428173577..9580cd5676 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -52,27 +52,6 @@ trait Namers extends MethodSynthesis { def newNamerFor(context: Context, tree: Tree): Namer = newNamer(context.makeNewScope(tree, tree.symbol)) - // In the typeCompleter (templateSig) of a case class (resp it's module), - // synthetic `copy` (reps `apply`, `unapply`) methods are added. To compute - // their signatures, the corresponding ClassDef is needed. - // During naming, for each case class module symbol, the corresponding ClassDef - // is stored in this map. The map is cleared lazily, i.e. when the new symbol - // is created with the same name, the old one (if present) is wiped out, or the - // entry is deleted when it is used and no longer needed. - private val classOfModuleClass = perRunCaches.newWeakMap[Symbol, WeakReference[ClassDef]]() - - // Default getters of constructors are added to the companion object in the - // typeCompleter of the constructor (methodSig). To compute the signature, - // we need the ClassDef. To create and enter the symbols into the companion - // object, we need the templateNamer of that module class. - // This map is extended during naming of classes, the Namer is added in when - // it's available, i.e. in the type completer (templateSig) of the module class. - private[typechecker] val classAndNamerOfModule = perRunCaches.newMap[Symbol, (ClassDef, Namer)]() - - def resetNamer() { - classAndNamerOfModule.clear() - } - abstract class Namer(val context: Context) extends MethodSynth with NamerContextErrors { thisNamer => import NamerErrorGen._ @@ -618,7 +597,7 @@ trait Namers extends MethodSynthesis { MaxParametersCaseClassError(tree) val m = ensureCompanionObject(tree, caseModuleDef) - classOfModuleClass(m.moduleClass) = new WeakReference(tree) + m.moduleClass.addAttachment(new ClassForCaseCompanionAttachment(tree)) } val hasDefault = impl.body exists { case DefDef(_, nme.CONSTRUCTOR, _, vparamss, _, _) => mexists(vparamss)(_.mods.hasDefault) @@ -626,7 +605,7 @@ trait Namers extends MethodSynthesis { } if (hasDefault) { val m = ensureCompanionObject(tree) - classAndNamerOfModule(m) = (tree, null) + m.addAttachment(new ConstructorDefaultsAttachment(tree, null)) } val owner = tree.symbol.owner if (settings.lint.value && owner.isPackageObjectClass && !mods.isImplicit) { @@ -657,7 +636,8 @@ trait Namers extends MethodSynthesis { if (sym.isLazy) sym.lazyAccessor andAlso enterIfNotThere - defaultParametersOfMethod(sym) foreach { symRef => enterIfNotThere(symRef()) } + for (defAtt <- sym.attachments.get[DefaultsOfLocalMethodAttachment]) + defAtt.defaultGetters foreach enterIfNotThere } this.context } @@ -846,23 +826,20 @@ trait Namers extends MethodSynthesis { // add apply and unapply methods to companion objects of case classes, // unless they exist already; here, "clazz" is the module class if (clazz.isModuleClass) { - Namers.this.classOfModuleClass get clazz foreach { cdefRef => - val cdef = cdefRef() - if (cdef.mods.isCase) addApplyUnapply(cdef, templateNamer) - classOfModuleClass -= clazz + clazz.attachments.get[ClassForCaseCompanionAttachment] foreach { cma => + val cdef = cma.caseClass + assert(cdef.mods.isCase, "expected case class: "+ cdef) + addApplyUnapply(cdef, templateNamer) } } // add the copy method to case classes; this needs to be done here, not in SyntheticMethods, because // the namer phase must traverse this copy method to create default getters for its parameters. // here, clazz is the ClassSymbol of the case class (not the module). - // @check: this seems to work only if the type completer of the class runs before the one of the - // module class: the one from the module class removes the entry from classOfModuleClass (see above). if (clazz.isClass && !clazz.hasModuleFlag) { val modClass = companionSymbolOf(clazz, context).moduleClass - Namers.this.classOfModuleClass get modClass map { cdefRef => - val cdef = cdefRef() - + modClass.attachments.get[ClassForCaseCompanionAttachment] foreach { cma => + val cdef = cma.caseClass def hasCopy(decls: Scope) = (decls lookup nme.copy) != NoSymbol if (cdef.mods.isCase && !hasCopy(decls) && !parents.exists(p => hasCopy(p.typeSymbol.info.decls)) && @@ -874,9 +851,8 @@ trait Namers extends MethodSynthesis { // if default getters (for constructor defaults) need to be added to that module, here's the namer // to use. clazz is the ModuleClass. sourceModule works also for classes defined in methods. val module = clazz.sourceModule - classAndNamerOfModule get module foreach { - case (cdef, _) => - classAndNamerOfModule(module) = (cdef, templateNamer) + for (cda <- module.attachments.get[ConstructorDefaultsAttachment]) { + cda.companionModuleClassNamer = templateNamer } ClassInfoType(parents, decls, clazz) } @@ -1097,13 +1073,15 @@ trait Namers extends MethodSynthesis { val module = companionSymbolOf(clazz, context) module.initialize // call type completer (typedTemplate), adds the // module's templateNamer to classAndNamerOfModule - classAndNamerOfModule get module match { - case s @ Some((cdef, nmr)) if nmr != null => - moduleNamer = s - (cdef, nmr) + module.attachments.get[ConstructorDefaultsAttachment] match { + // by martin: the null case can happen in IDE; this is really an ugly hack on top of an ugly hack but it seems to work + // later by lukas: disabled when fixing SI-5975, i think it cannot happen anymore + case Some(cda) /*if cma.companionModuleClassNamer == null*/ => + val p = (cda.classWithDefault, cda.companionModuleClassNamer) + moduleNamer = Some(p) + p case _ => return // fix #3649 (prevent crash in erroneous source code) - // nmr == null can happen in IDE; this is really an ugly hack on top[ of an ugly hack but it seems to work } } deftParams = cdef.tparams map copyUntypedInvariant @@ -1141,11 +1119,14 @@ trait Namers extends MethodSynthesis { clazz.resetFlag(INTERFACE) // there's a concrete member now val default = parentNamer.enterSyntheticSym(defaultTree) if (forInteractive && default.owner.isTerm) { - // enter into map from method symbols to default arguments. - // if compiling the same local block several times (which can happen in interactive mode) - // we might otherwise not find the default symbol, because the second time it the - // method symbol will be re-entered in the scope but the default parameter will not. - defaultParametersOfMethod(meth) += new WeakReference(default) + // save the default getters as attachments in the method symbol. if compiling the + // same local block several times (which can happen in interactive mode) we might + // otherwise not find the default symbol, because the second time it the method + // symbol will be re-entered in the scope but the default parameter will not. + val att = meth.attachments.get[DefaultsOfLocalMethodAttachment] match { + case Some(att) => att.defaultGetters += default + case None => meth.addAttachment(new DefaultsOfLocalMethodAttachment(default)) + } } } else if (baseHasDefault) { // the parameter does not have a default itself, but the diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 61443faba0..a0c1342026 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -21,8 +21,19 @@ trait NamesDefaults { self: Analyzer => import definitions._ import NamesDefaultsErrorsGen._ - val defaultParametersOfMethod = - perRunCaches.newWeakMap[Symbol, Set[WeakReference[Symbol]]]() withDefaultValue Set() + // Default getters of constructors are added to the companion object in the + // typeCompleter of the constructor (methodSig). To compute the signature, + // we need the ClassDef. To create and enter the symbols into the companion + // object, we need the templateNamer of that module class. These two are stored + // as an attachment in the companion module symbol + class ConstructorDefaultsAttachment(val classWithDefault: ClassDef, var companionModuleClassNamer: Namer) + + // To attach the default getters of local (term-owned) methods to the method symbol. + // Used in Namer.enterExistingSym: it needs to re-enter the method symbol and also + // default getters, which could not be found otherwise. + class DefaultsOfLocalMethodAttachment(val defaultGetters: mutable.Set[Symbol]) { + def this(default: Symbol) = this(mutable.Set(default)) + } case class NamedApplyInfo( qual: Option[Tree], diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 6aa93f9cec..b9fe269e43 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -47,7 +47,6 @@ trait Typers extends Modes with Adaptations with Tags { def resetTyper() { //println("resetTyper called") resetContexts() - resetNamer() resetImplicits() transformed.clear() } @@ -5114,7 +5113,7 @@ trait Typers extends Modes with Adaptations with Tags { case SelectFromTypeTree(qual, selector) => val qual1 = typedType(qual, mode) - if (qual1.tpe.isVolatile) TypeSelectionFromVolatileTypeError(tree, qual) + if (qual1.tpe.isVolatile) TypeSelectionFromVolatileTypeError(tree, qual1) else typedSelect(qual1, selector) case CompoundTypeTree(templ) => diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 1b89f3db44..ad936ac39d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -23,6 +23,14 @@ trait Unapplies extends ast.TreeDSL private val unapplyParamName = nme.x_0 + + // In the typeCompleter (templateSig) of a case class (resp it's module), + // synthetic `copy` (reps `apply`, `unapply`) methods are added. To compute + // their signatures, the corresponding ClassDef is needed. During naming (in + // `enterClassDef`), the case class ClassDef is added as an attachment to the + // moduleClass symbol of the companion module. + class ClassForCaseCompanionAttachment(val caseClass: ClassDef) + /** returns type list for return type of the extraction */ def unapplyTypeList(ufn: Symbol, ufntpe: Type) = { assert(ufn.isMethod, ufn) diff --git a/src/compiler/scala/tools/reflect/FastTrack.scala b/src/compiler/scala/tools/reflect/FastTrack.scala index 59160f87d0..8ea66979bc 100644 --- a/src/compiler/scala/tools/reflect/FastTrack.scala +++ b/src/compiler/scala/tools/reflect/FastTrack.scala @@ -15,8 +15,9 @@ trait FastTrack { import definitions._ import language.implicitConversions - private implicit def context2taggers(c0: MacroContext) : Taggers { val c: c0.type } = new { val c: c0.type = c0 } with Taggers - private implicit def context2contextreifiers(c0: MacroContext) : ContextReifiers { val c: c0.type } = new { val c: c0.type = c0 } with ContextReifiers + private implicit def context2taggers(c0: MacroContext): Taggers { val c: c0.type } = new { val c: c0.type = c0 } with Taggers + private implicit def context2contextreifiers(c0: MacroContext): ContextReifiers { val c: c0.type } = new { val c: c0.type = c0 } with ContextReifiers + private implicit def context2macroimplementations(c0: MacroContext): MacroImplementations { val c: c0.type } = new { val c: c0.type = c0 } with MacroImplementations implicit def fastTrackEntry2MacroRuntime(entry: FastTrackEntry): MacroRuntime = args => entry.run(args) type FastTrackExpander = PartialFunction[(MacroContext, Tree), Tree] @@ -42,6 +43,7 @@ trait FastTrack { ApiUniverseReify bindTo { case (c, Apply(TypeApply(_, List(tt)), List(expr))) => c.materializeExpr(c.prefix.tree, EmptyTree, expr) } MacroContextReify bindTo { case (c, Apply(TypeApply(_, List(tt)), List(expr))) => c.materializeExprForMacroContext(c.prefix.tree, expr) } ReflectRuntimeCurrentMirror bindTo { case (c, _) => scala.reflect.runtime.Macros.currentMirror(c).tree } + StringContext_f bindTo { case (c, Apply(Select(Apply(_, parts), _), args)) => c.macro_StringInterpolation_f(parts, args) } registry } }
\ No newline at end of file diff --git a/src/compiler/scala/tools/reflect/MacroImplementations.scala b/src/compiler/scala/tools/reflect/MacroImplementations.scala new file mode 100644 index 0000000000..a5f7928f55 --- /dev/null +++ b/src/compiler/scala/tools/reflect/MacroImplementations.scala @@ -0,0 +1,147 @@ +package scala.tools.reflect + +import scala.reflect.makro.{ReificationError, UnexpectedReificationError} +import scala.reflect.makro.runtime.Context +import scala.collection.mutable.ListBuffer +import scala.collection.mutable.Stack + +abstract class MacroImplementations { + val c: Context + + import c.universe._ + + def macro_StringInterpolation_f(parts: List[Tree], args: List[Tree]): Tree = { + // the parts all have the same position information (as the expression is generated by the compiler) + // the args have correct position information + + // the following conditions can only be violated if invoked directly + if (parts.length != args.length + 1) { + if(parts.length == 0) + c.abort(c.prefix.tree.pos, "too few parts") + else if(args.length + 1 < parts.length) + c.abort(if(args.length==0) c.enclosingPosition else args.last.pos, + "too few arguments for interpolated string") + else + c.abort(args(parts.length-1).pos, + "too many arguments for interpolated string") + } + + val stringParts = parts map { + case Literal(Constant(s: String)) => s; + case _ => throw new IllegalArgumentException("argument parts must be a list of string literals") + } + + val pi = stringParts.iterator + val bldr = new java.lang.StringBuilder + val evals = ListBuffer[ValDef]() + val ids = ListBuffer[Ident]() + val argsStack = Stack(args : _*) + + def defval(value: Tree, tpe: Type): Unit = { + val freshName = newTermName(c.fresh("arg$")) + evals += ValDef(Modifiers(), freshName, TypeTree(tpe), value) + ids += Ident(freshName) + } + + def isFlag(ch: Char): Boolean = { + ch match { + case '-' | '#' | '+' | ' ' | '0' | ',' | '(' => true + case _ => false + } + } + + def checkType(arg: Tree, variants: Type*): Option[Type] = { + variants.find(arg.tpe <:< _).orElse( + variants.find(c.inferImplicitView(arg, arg.tpe, _) != EmptyTree).orElse( + Some(variants(0)) + ) + ) + } + + def conversionType(ch: Char, arg: Tree): Option[Type] = { + ch match { + case 'b' | 'B' => + if(arg.tpe <:< NullTpe) Some(NullTpe) else Some(BooleanTpe) + case 'h' | 'H' => + Some(AnyTpe) + case 's' | 'S' => + Some(AnyTpe) + case 'c' | 'C' => + checkType(arg, CharTpe, ByteTpe, ShortTpe, IntTpe) + case 'd' | 'o' | 'x' | 'X' => + checkType(arg, IntTpe, LongTpe, ByteTpe, ShortTpe, typeOf[BigInt]) + case 'e' | 'E' | 'g' | 'G' | 'f' | 'a' | 'A' => + checkType(arg, DoubleTpe, FloatTpe, typeOf[BigDecimal]) + case 't' | 'T' => + checkType(arg, LongTpe, typeOf[java.util.Calendar], typeOf[java.util.Date]) + case _ => None + } + } + + def copyString(first: Boolean): Unit = { + val str = StringContext.treatEscapes(pi.next()) + val strLen = str.length + val strIsEmpty = strLen == 0 + var start = 0 + var idx = 0 + + if (!first) { + val arg = argsStack.pop + if (strIsEmpty || (str charAt 0) != '%') { + bldr append "%s" + defval(arg, AnyTpe) + } else { + // PRE str is not empty and str(0) == '%' + // argument index parameter is not allowed, thus parse + // [flags][width][.precision]conversion + var pos = 1 + while(pos < strLen && isFlag(str charAt pos)) pos += 1 + while(pos < strLen && Character.isDigit(str charAt pos)) pos += 1 + if(pos < strLen && str.charAt(pos) == '.') { pos += 1 + while(pos < strLen && Character.isDigit(str charAt pos)) pos += 1 + } + if(pos < strLen) { + conversionType(str charAt pos, arg) match { + case Some(tpe) => defval(arg, tpe) + case None => c.error(arg.pos, "illegal conversion character") + } + } else { + // TODO: place error message on conversion string + c.error(arg.pos, "wrong conversion string") + } + } + idx = 1 + } + if (!strIsEmpty) { + val len = str.length + while (idx < len) { + if (str(idx) == '%') { + bldr append (str substring (start, idx)) append "%%" + start = idx + 1 + } + idx += 1 + } + bldr append (str substring (start, idx)) + } + } + + copyString(first = true) + while (pi.hasNext) { + copyString(first = false) + } + + val fstring = bldr.toString +// val expr = c.reify(fstring.format((ids.map(id => Expr(id).eval)) : _*)) +// https://issues.scala-lang.org/browse/SI-5824, therefore + val expr = + Apply( + Select( + Literal(Constant(fstring)), + newTermName("format")), + List(ids: _* ) + ); + + Block(evals.toList, expr) + } + +}
\ No newline at end of file |