diff options
139 files changed, 2539 insertions, 949 deletions
diff --git a/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala b/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala index 2e82e34bd9..1413065a27 100644 --- a/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala +++ b/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala @@ -8,6 +8,11 @@ abstract class DefaultMacroCompiler extends Resolvers with Errors { val global: Global import global._ + import analyzer._ + import treeInfo._ + import definitions._ + val runDefinitions = currentRun.runDefinitions + import runDefinitions.{Predef_???, _} val typer: global.analyzer.Typer val context = typer.context @@ -15,13 +20,72 @@ abstract class DefaultMacroCompiler extends Resolvers val macroDdef: DefDef lazy val macroDef = macroDdef.symbol + case class MacroImplRefCompiler(untypedMacroImplRef: Tree, isImplBundle: Boolean) extends Resolver with Validator with Error private case class MacroImplResolutionException(pos: Position, msg: String) extends Exception def abort(pos: Position, msg: String) = throw MacroImplResolutionException(pos, msg) + /** Resolves a macro impl reference provided in the right-hand side of the given macro definition. + * + * Acceptable shapes of the right-hand side: + * 1) [<static object>].<method name>[[<type args>]] // vanilla macro impl ref + * 2) [<macro bundle>].<method name>[[<type args>]] // shiny new macro bundle impl ref + * + * Produces a tree, which represents a reference to a macro implementation if everything goes well, + * otherwise reports found errors and returns EmptyTree. The resulting tree should have the following format: + * + * qualifier.method[targs] + * + * Qualifier here might be omitted (local macro defs), be a static object (vanilla macro defs) + * or be a dummy instance of a macro bundle (e.g. new MyMacro(???).expand). + */ def resolveMacroImpl: Tree = { + def tryCompile(compiler: MacroImplRefCompiler): scala.util.Try[Tree] = { + try { compiler.validateMacroImplRef(); scala.util.Success(compiler.macroImplRef) } + catch { case ex: MacroImplResolutionException => scala.util.Failure(ex) } + } + val vanillaImplRef = MacroImplRefCompiler(macroDdef.rhs.duplicate, isImplBundle = false) + val (maybeBundleRef, methName, targs) = macroDdef.rhs.duplicate match { + case Applied(Select(Applied(RefTree(qual, bundleName), _, Nil), methName), targs, Nil) => + (RefTree(qual, bundleName.toTypeName), methName, targs) + case Applied(Ident(methName), targs, Nil) => + (Ident(context.owner.enclClass), methName, targs) + case _ => + (EmptyTree, TermName(""), Nil) + } + val bundleImplRef = MacroImplRefCompiler( + atPos(macroDdef.rhs.pos)(gen.mkTypeApply(Select(New(maybeBundleRef, List(List(Ident(Predef_???)))), methName), targs)), + isImplBundle = true + ) + val vanillaResult = tryCompile(vanillaImplRef) + val bundleResult = tryCompile(bundleImplRef) + + def ensureUnambiguousSuccess() = { + // we now face a hard choice of whether to report ambiguity: + // 1) when there are eponymous methods in both bundle and object + // 2) when both references to eponymous methods are resolved successfully + // doing #1 would cause less confusion in the long run, but it would also cause more frequent source incompatibilities + // e.g. it would fail to compile https://github.com/ReifyIt/basis + // therefore here we go for #2 + // if (vanillaImplRef.looksCredible && bundleImplRef.looksCredible) MacroImplAmbiguousError() + if (vanillaResult.isSuccess && bundleResult.isSuccess) MacroImplAmbiguousError() + } + + def reportMostAppropriateFailure() = { + typer.silent(_.typedTypeConstructor(maybeBundleRef)) match { + case SilentResultValue(result) if looksLikeMacroBundleType(result.tpe) => + val bundle = result.tpe.typeSymbol + if (!isMacroBundleType(bundle.tpe)) MacroBundleWrongShapeError() + if (!bundle.owner.isStaticOwner) MacroBundleNonStaticError() + bundleResult.get + case _ => + vanillaResult.get + } + } + try { - validateMacroImplRef() - macroImplRef + if (vanillaResult.isSuccess || bundleResult.isSuccess) ensureUnambiguousSuccess() + if (vanillaResult.isFailure && bundleResult.isFailure) reportMostAppropriateFailure() + vanillaResult.orElse(bundleResult).get } catch { case MacroImplResolutionException(pos, msg) => context.error(pos, msg) diff --git a/src/compiler/scala/reflect/macros/compiler/Errors.scala b/src/compiler/scala/reflect/macros/compiler/Errors.scala index 280baa2a42..490ab3657a 100644 --- a/src/compiler/scala/reflect/macros/compiler/Errors.scala +++ b/src/compiler/scala/reflect/macros/compiler/Errors.scala @@ -13,12 +13,9 @@ trait Errors extends Traces { import treeInfo._ import typer.TyperErrorGen._ import typer.infer.InferErrorGen._ - private val runDefinitions = currentRun.runDefinitions import runDefinitions._ def globalSettings = global.settings - // sanity check errors - private def implRefError(message: String) = { val Applied(culprit, _, _) = macroDdef.rhs abort(culprit.pos, message) @@ -33,111 +30,125 @@ trait Errors extends Traces { abort(culprit.pos, message) } - def MacroImplReferenceWrongShapeError() = implRefError( + def MacroImplAmbiguousError() = implRefError( + "macro implementation reference is ambiguous: makes sense both as\n"+ + "a macro bundle method reference and a vanilla object method reference") + + def MacroBundleNonStaticError() = bundleRefError("macro bundles must be static") + + def MacroBundleWrongShapeError() = bundleRefError("macro bundles must be concrete classes having a single constructor with a `val c: Context` parameter") + + trait Error { + self: MacroImplRefCompiler => + + // sanity check errors + + def MacroImplReferenceWrongShapeError() = implRefError( "macro implementation reference has wrong shape. required:\n"+ "macro [<static object>].<method name>[[<type args>]] or\n" + "macro [<macro bundle>].<method name>[[<type args>]]") - def MacroImplWrongNumberOfTypeArgumentsError() = { - val diagnostic = if (macroImpl.typeParams.length > targs.length) "has too few type arguments" else "has too many arguments" - implRefError(s"macro implementation reference $diagnostic for " + treeSymTypeMsg(macroImplRef)) - } - - def MacroImplNotPublicError() = implRefError("macro implementation must be public") + def MacroImplWrongNumberOfTypeArgumentsError() = { + val diagnostic = if (macroImpl.typeParams.length > targs.length) "has too few type arguments" else "has too many arguments" + implRefError(s"macro implementation reference $diagnostic for " + treeSymTypeMsg(macroImplRef)) + } - def MacroImplOverloadedError() = implRefError("macro implementation cannot be overloaded") + private def macroImplementationWording = + if (isImplBundle) "bundle implementation" + else "macro implementation" - def MacroImplNonTagImplicitParameters(params: List[Symbol]) = implRefError("macro implementations cannot have implicit parameters other than WeakTypeTag evidences") + def MacroImplNotPublicError() = implRefError(s"${macroImplementationWording} must be public") - def MacroBundleNonStaticError() = bundleRefError("macro bundles must be static") + def MacroImplOverloadedError() = implRefError(s"${macroImplementationWording} cannot be overloaded") - def MacroBundleWrongShapeError() = bundleRefError("macro bundles must be concrete classes having a single constructor with a `val c: Context` parameter") + def MacroImplNonTagImplicitParameters(params: List[Symbol]) = implRefError(s"${macroImplementationWording}s cannot have implicit parameters other than WeakTypeTag evidences") - // compatibility errors + // compatibility errors - // helpers + // helpers - private def lengthMsg(flavor: String, violation: String, extra: Symbol) = { - val noun = if (flavor == "value") "parameter" else "type parameter" - val message = noun + " lists have different length, " + violation + " extra " + noun - val suffix = if (extra ne NoSymbol) " " + extra.defString else "" - message + suffix - } + private def lengthMsg(flavor: String, violation: String, extra: Symbol) = { + val noun = if (flavor == "value") "parameter" else "type parameter" + val message = noun + " lists have different length, " + violation + " extra " + noun + val suffix = if (extra ne NoSymbol) " " + extra.defString else "" + message + suffix + } - private def abbreviateCoreAliases(s: String): String = { - val coreAliases = List("WeakTypeTag", "Expr", "Tree") - coreAliases.foldLeft(s)((res, x) => res.replace("c.universe." + x, "c." + x)) - } + private def abbreviateCoreAliases(s: String): String = { + val coreAliases = List("WeakTypeTag", "Expr", "Tree") + coreAliases.foldLeft(s)((res, x) => res.replace("c.universe." + x, "c." + x)) + } - private def showMeth(pss: List[List[Symbol]], restpe: Type, abbreviate: Boolean, untype: Boolean) = { - def preprocess(tpe: Type) = if (untype) untypeMetalevel(tpe) else tpe - var pssPart = (pss map (ps => ps map (p => p.defStringSeenAs(preprocess(p.info))) mkString ("(", ", ", ")"))).mkString - if (abbreviate) pssPart = abbreviateCoreAliases(pssPart) - var retPart = preprocess(restpe).toString - if (abbreviate || macroDdef.tpt.tpe == null) retPart = abbreviateCoreAliases(retPart) - pssPart + ": " + retPart - } + private def showMeth(pss: List[List[Symbol]], restpe: Type, abbreviate: Boolean, untype: Boolean) = { + def preprocess(tpe: Type) = if (untype) untypeMetalevel(tpe) else tpe + var pssPart = (pss map (ps => ps map (p => p.defStringSeenAs(preprocess(p.info))) mkString ("(", ", ", ")"))).mkString + if (abbreviate) pssPart = abbreviateCoreAliases(pssPart) + var retPart = preprocess(restpe).toString + if (abbreviate || macroDdef.tpt.tpe == null) retPart = abbreviateCoreAliases(retPart) + pssPart + ": " + retPart + } - // not exactly an error generator, but very related - // and I dearly wanted to push it away from Macros.scala - private def checkConforms(slot: String, rtpe: Type, atpe: Type) = { - val verbose = macroDebugVerbose - - def check(rtpe: Type, atpe: Type): Boolean = { - def success() = { if (verbose) println(rtpe + " <: " + atpe + "?" + EOL + "true"); true } - (rtpe, atpe) match { - case _ if rtpe eq atpe => success() - case (TypeRef(_, RepeatedParamClass, rtpe :: Nil), TypeRef(_, RepeatedParamClass, atpe :: Nil)) => check(rtpe, atpe) - case (ExprClassOf(_), TreeType()) if rtpe.prefix =:= atpe.prefix => success() - case (SubtreeType(), ExprClassOf(_)) if rtpe.prefix =:= atpe.prefix => success() - case _ => rtpe <:< atpe + // not exactly an error generator, but very related + // and I dearly wanted to push it away from Macros.scala + private def checkConforms(slot: String, rtpe: Type, atpe: Type) = { + val verbose = macroDebugVerbose + + def check(rtpe: Type, atpe: Type): Boolean = { + def success() = { if (verbose) println(rtpe + " <: " + atpe + "?" + EOL + "true"); true } + (rtpe, atpe) match { + case _ if rtpe eq atpe => success() + case (TypeRef(_, RepeatedParamClass, rtpe :: Nil), TypeRef(_, RepeatedParamClass, atpe :: Nil)) => check(rtpe, atpe) + case (ExprClassOf(_), TreeType()) if rtpe.prefix =:= atpe.prefix => success() + case (SubtreeType(), ExprClassOf(_)) if rtpe.prefix =:= atpe.prefix => success() + case _ => rtpe <:< atpe + } } - } - val ok = - if (verbose) withTypesExplained(check(rtpe, atpe)) - else check(rtpe, atpe) - if (!ok) { - if (!verbose) explainTypes(rtpe, atpe) - val msg = { - val ss = Seq(rtpe, atpe) map (this abbreviateCoreAliases _.toString) - s"type mismatch for $slot: ${ss(0)} does not conform to ${ss(1)}" + val ok = + if (verbose) withTypesExplained(check(rtpe, atpe)) + else check(rtpe, atpe) + if (!ok) { + if (!verbose) explainTypes(rtpe, atpe) + val msg = { + val ss = Seq(rtpe, atpe) map (this abbreviateCoreAliases _.toString) + s"type mismatch for $slot: ${ss(0)} does not conform to ${ss(1)}" + } + compatibilityError(msg) } - compatibilityError(msg) } - } - private def compatibilityError(message: String) = - implRefError( - "macro implementation has incompatible shape:"+ - "\n required: " + showMeth(rparamss, rret, abbreviate = true, untype = false) + - "\n or : " + showMeth(rparamss, rret, abbreviate = true, untype = true) + - "\n found : " + showMeth(aparamss, aret, abbreviate = false, untype = false) + - "\n" + message) + private def compatibilityError(message: String) = + implRefError( + s"${macroImplementationWording} has incompatible shape:"+ + "\n required: " + showMeth(rparamss, rret, abbreviate = true, untype = false) + + "\n or : " + showMeth(rparamss, rret, abbreviate = true, untype = true) + + "\n found : " + showMeth(aparamss, aret, abbreviate = false, untype = false) + + "\n" + message) - def MacroImplParamssMismatchError() = compatibilityError("number of parameter sections differ") + def MacroImplParamssMismatchError() = compatibilityError("number of parameter sections differ") - def MacroImplExtraParamsError(aparams: List[Symbol], rparams: List[Symbol]) = compatibilityError(lengthMsg("value", "found", aparams(rparams.length))) + def MacroImplExtraParamsError(aparams: List[Symbol], rparams: List[Symbol]) = compatibilityError(lengthMsg("value", "found", aparams(rparams.length))) - def MacroImplMissingParamsError(aparams: List[Symbol], rparams: List[Symbol]) = compatibilityError(abbreviateCoreAliases(lengthMsg("value", "required", rparams(aparams.length)))) + def MacroImplMissingParamsError(aparams: List[Symbol], rparams: List[Symbol]) = compatibilityError(abbreviateCoreAliases(lengthMsg("value", "required", rparams(aparams.length)))) - def checkMacroImplParamTypeMismatch(atpe: Type, rparam: Symbol) = checkConforms("parameter " + rparam.name, rparam.tpe, atpe) + def checkMacroImplParamTypeMismatch(atpe: Type, rparam: Symbol) = checkConforms("parameter " + rparam.name, rparam.tpe, atpe) - def checkMacroImplResultTypeMismatch(atpe: Type, rret: Type) = checkConforms("return type", atpe, rret) + def checkMacroImplResultTypeMismatch(atpe: Type, rret: Type) = checkConforms("return type", atpe, rret) - def MacroImplParamNameMismatchError(aparam: Symbol, rparam: Symbol) = compatibilityError("parameter names differ: " + rparam.name + " != " + aparam.name) + def MacroImplParamNameMismatchError(aparam: Symbol, rparam: Symbol) = compatibilityError("parameter names differ: " + rparam.name + " != " + aparam.name) - def MacroImplVarargMismatchError(aparam: Symbol, rparam: Symbol) = { - def fail(paramName: Name) = compatibilityError("types incompatible for parameter " + paramName + ": corresponding is not a vararg parameter") - if (isRepeated(rparam) && !isRepeated(aparam)) fail(rparam.name) - if (!isRepeated(rparam) && isRepeated(aparam)) fail(aparam.name) - } + def MacroImplVarargMismatchError(aparam: Symbol, rparam: Symbol) = { + def fail(paramName: Name) = compatibilityError("types incompatible for parameter " + paramName + ": corresponding is not a vararg parameter") + if (isRepeated(rparam) && !isRepeated(aparam)) fail(rparam.name) + if (!isRepeated(rparam) && isRepeated(aparam)) fail(aparam.name) + } - def MacroImplTargMismatchError(atargs: List[Type], atparams: List[Symbol]) = - compatibilityError(NotWithinBoundsErrorMessage("", atargs, atparams, macroDebugVerbose || settings.explaintypes.value)) + def MacroImplTargMismatchError(atargs: List[Type], atparams: List[Symbol]) = + compatibilityError(NotWithinBoundsErrorMessage("", atargs, atparams, macroDebugVerbose || settings.explaintypes.value)) - def MacroImplTparamInstantiationError(atparams: List[Symbol], e: NoInstance) = { - val badps = atparams map (_.defString) mkString ", " - compatibilityError(f"type parameters $badps cannot be instantiated%n${e.getMessage}") + def MacroImplTparamInstantiationError(atparams: List[Symbol], e: NoInstance) = { + val badps = atparams map (_.defString) mkString ", " + compatibilityError(f"type parameters $badps cannot be instantiated%n${e.getMessage}") + } } } diff --git a/src/compiler/scala/reflect/macros/compiler/Resolvers.scala b/src/compiler/scala/reflect/macros/compiler/Resolvers.scala index d2f9886f3c..807fb688a0 100644 --- a/src/compiler/scala/reflect/macros/compiler/Resolvers.scala +++ b/src/compiler/scala/reflect/macros/compiler/Resolvers.scala @@ -12,61 +12,35 @@ trait Resolvers { import definitions._ import treeInfo._ import gen._ - private val runDefinitions = currentRun.runDefinitions - import runDefinitions.{Predef_???, _} + import runDefinitions._ - /** Resolves a macro impl reference provided in the right-hand side of the given macro definition. - * - * Acceptable shapes of the right-hand side: - * 1) [<static object>].<method name>[[<type args>]] // vanilla macro def - * 2) [<macro bundle>].<method name>[[<type args>]] // shiny new macro bundle - * - * Produces a tree, which represents a reference to a macro implementation if everything goes well, - * otherwise reports found errors and returns EmptyTree. The resulting tree should have the following format: - * - * qualifier.method[targs] - * - * Qualifier here might be omitted (macro defs local to blocks), be a static object (vanilla macro defs) - * or be a dummy instance of a macro bundle (e.g. new MyMacro(???).expand). - */ - lazy val macroImplRef: Tree = { - val (maybeBundleRef, methName, targs) = macroDdef.rhs match { - case Applied(Select(Applied(RefTree(qual, bundleName), _, Nil), methName), targs, Nil) => - (RefTree(qual, bundleName.toTypeName), methName, targs) - case Applied(Ident(methName), targs, Nil) => - (Ident(context.owner.enclClass), methName, targs) - case _ => - (EmptyTree, TermName(""), Nil) - } + trait Resolver { + self: MacroImplRefCompiler => - val untypedImplRef = typer.silent(_.typedTypeConstructor(maybeBundleRef)) match { - case SilentResultValue(result) if looksLikeMacroBundleType(result.tpe) => - val bundle = result.tpe.typeSymbol - if (!isMacroBundleType(bundle.tpe)) MacroBundleWrongShapeError() - if (!bundle.owner.isStaticOwner) MacroBundleNonStaticError() - atPos(macroDdef.rhs.pos)(gen.mkTypeApply(Select(New(bundle, Ident(Predef_???)), methName), targs)) - case _ => - macroDdef.rhs - } + val isImplBundle: Boolean + val isImplMethod = !isImplBundle - val typedImplRef = typer.silent(_.typed(markMacroImplRef(untypedImplRef)), reportAmbiguousErrors = false) - typedImplRef match { - case SilentResultValue(success) => success - case SilentTypeError(err) => abort(err.errPos, err.errMsg) + lazy val looksCredible: Boolean = { + val Applied(core, _, _) = untypedMacroImplRef + typer.silent(_.typed(markMacroImplRef(core)), reportAmbiguousErrors = false).nonEmpty } - } - // FIXME: cannot write this concisely because of SI-7507 - // lazy val (isImplBundle, macroImplOwner, macroImpl, macroImplTargs) = - private lazy val dissectedMacroImplRef = - macroImplRef match { - case MacroImplReference(isBundle, isBlackbox, owner, meth, targs) => (isBundle, isBlackbox, owner, meth, targs) - case _ => MacroImplReferenceWrongShapeError() - } - lazy val isImplBundle = dissectedMacroImplRef._1 - lazy val isImplMethod = !isImplBundle - lazy val isImplBlackbox = dissectedMacroImplRef._2 - lazy val macroImplOwner = dissectedMacroImplRef._3 - lazy val macroImpl = dissectedMacroImplRef._4 - lazy val targs = dissectedMacroImplRef._5 + lazy val macroImplRef: Tree = + typer.silent(_.typed(markMacroImplRef(untypedMacroImplRef)), reportAmbiguousErrors = false) match { + case SilentResultValue(success) => success + case SilentTypeError(err) => abort(err.errPos, err.errMsg) + } + + // FIXME: cannot write this concisely because of SI-7507 + // lazy val (_, macroImplOwner, macroImpl, macroImplTargs) = + private lazy val dissectedMacroImplRef = + macroImplRef match { + case MacroImplReference(isBundle, isBlackbox, owner, meth, targs) => (isBlackbox, owner, meth, targs) + case _ => MacroImplReferenceWrongShapeError() + } + lazy val isImplBlackbox = dissectedMacroImplRef._1 + lazy val macroImplOwner = dissectedMacroImplRef._2 + lazy val macroImpl = dissectedMacroImplRef._3 + lazy val targs = dissectedMacroImplRef._4 + } } diff --git a/src/compiler/scala/reflect/macros/compiler/Validators.scala b/src/compiler/scala/reflect/macros/compiler/Validators.scala index 02c1f7c431..fc118028dd 100644 --- a/src/compiler/scala/reflect/macros/compiler/Validators.scala +++ b/src/compiler/scala/reflect/macros/compiler/Validators.scala @@ -1,9 +1,7 @@ package scala.reflect.macros package compiler -import java.util.UUID.randomUUID import scala.reflect.internal.Flags._ -import scala.reflect.macros.TypecheckException trait Validators { self: DefaultMacroCompiler => @@ -11,189 +9,193 @@ trait Validators { import global._ import analyzer._ import definitions._ - private val runDefinitions = currentRun.runDefinitions import runDefinitions.{Predef_???, _} - def validateMacroImplRef() = { - sanityCheck() - if (macroImpl != Predef_???) checkMacroDefMacroImplCorrespondence() - } + trait Validator { + self: MacroImplRefCompiler => - private def sanityCheck() = { - if (!macroImpl.isMethod) MacroImplReferenceWrongShapeError() - if (macroImpl.typeParams.length != targs.length) MacroImplWrongNumberOfTypeArgumentsError() - if (!macroImpl.isPublic) MacroImplNotPublicError() - if (macroImpl.isOverloaded) MacroImplOverloadedError() - val implicitParams = aparamss.flatten filter (_.isImplicit) - if (implicitParams.nonEmpty) MacroImplNonTagImplicitParameters(implicitParams) - val effectiveOwner = if (isImplMethod) macroImplOwner else macroImplOwner.owner - val declaredInStaticObject = effectiveOwner.isStaticOwner || effectiveOwner.moduleClass.isStaticOwner - if (!declaredInStaticObject) MacroImplReferenceWrongShapeError() - } + def validateMacroImplRef() = { + sanityCheck() + if (macroImpl != Predef_???) checkMacroDefMacroImplCorrespondence() + } - private def checkMacroDefMacroImplCorrespondence() = { - val atvars = atparams map freshVar - def atpeToRtpe(atpe: Type) = atpe.substSym(aparamss.flatten, rparamss.flatten).instantiateTypeParams(atparams, atvars) - - // we only check strict correspondence between value parameterss - // type parameters of macro defs and macro impls don't have to coincide with each other - if (aparamss.length != rparamss.length) MacroImplParamssMismatchError() - map2(aparamss, rparamss)((aparams, rparams) => { - if (aparams.length < rparams.length) MacroImplMissingParamsError(aparams, rparams) - if (rparams.length < aparams.length) MacroImplExtraParamsError(aparams, rparams) - }) - - try { - // cannot fuse this map2 and the map2 above because if aparamss.flatten != rparamss.flatten - // then `atpeToRtpe` is going to fail with an unsound substitution - map2(aparamss.flatten, rparamss.flatten)((aparam, rparam) => { - if (aparam.name != rparam.name && !rparam.isSynthetic) MacroImplParamNameMismatchError(aparam, rparam) - if (isRepeated(aparam) ^ isRepeated(rparam)) MacroImplVarargMismatchError(aparam, rparam) - val aparamtpe = aparam.tpe match { - case MacroContextType(tpe) => tpe - case tpe => tpe - } - checkMacroImplParamTypeMismatch(atpeToRtpe(aparamtpe), rparam) + private def sanityCheck() = { + if (!macroImpl.isMethod) MacroImplReferenceWrongShapeError() + if (macroImpl.typeParams.length != targs.length) MacroImplWrongNumberOfTypeArgumentsError() + if (!macroImpl.isPublic) MacroImplNotPublicError() + if (macroImpl.isOverloaded) MacroImplOverloadedError() + val implicitParams = aparamss.flatten filter (_.isImplicit) + if (implicitParams.nonEmpty) MacroImplNonTagImplicitParameters(implicitParams) + val effectiveOwner = if (isImplMethod) macroImplOwner else macroImplOwner.owner + val effectivelyStatic = effectiveOwner.isStaticOwner || effectiveOwner.moduleClass.isStaticOwner + val correctBundleness = if (isImplMethod) macroImplOwner.isModuleClass else macroImplOwner.isClass && !macroImplOwner.isModuleClass + if (!effectivelyStatic || !correctBundleness) MacroImplReferenceWrongShapeError() + } + + private def checkMacroDefMacroImplCorrespondence() = { + val atvars = atparams map freshVar + def atpeToRtpe(atpe: Type) = atpe.substSym(aparamss.flatten, rparamss.flatten).instantiateTypeParams(atparams, atvars) + + // we only check strict correspondence between value parameterss + // type parameters of macro defs and macro impls don't have to coincide with each other + if (aparamss.length != rparamss.length) MacroImplParamssMismatchError() + map2(aparamss, rparamss)((aparams, rparams) => { + if (aparams.length < rparams.length) MacroImplMissingParamsError(aparams, rparams) + if (rparams.length < aparams.length) MacroImplExtraParamsError(aparams, rparams) }) - checkMacroImplResultTypeMismatch(atpeToRtpe(aret), rret) + try { + // cannot fuse this map2 and the map2 above because if aparamss.flatten != rparamss.flatten + // then `atpeToRtpe` is going to fail with an unsound substitution + map2(aparamss.flatten, rparamss.flatten)((aparam, rparam) => { + if (aparam.name != rparam.name && !rparam.isSynthetic) MacroImplParamNameMismatchError(aparam, rparam) + if (isRepeated(aparam) ^ isRepeated(rparam)) MacroImplVarargMismatchError(aparam, rparam) + val aparamtpe = aparam.tpe match { + case MacroContextType(tpe) => tpe + case tpe => tpe + } + checkMacroImplParamTypeMismatch(atpeToRtpe(aparamtpe), rparam) + }) - val maxLubDepth = lubDepth(aparamss.flatten map (_.tpe)) max lubDepth(rparamss.flatten map (_.tpe)) - val atargs = solvedTypes(atvars, atparams, atparams map varianceInType(aret), upper = false, maxLubDepth) - val boundsOk = typer.silent(_.infer.checkBounds(macroDdef, NoPrefix, NoSymbol, atparams, atargs, "")) - boundsOk match { - case SilentResultValue(true) => // do nothing, success - case SilentResultValue(false) | SilentTypeError(_) => MacroImplTargMismatchError(atargs, atparams) + checkMacroImplResultTypeMismatch(atpeToRtpe(aret), rret) + + val maxLubDepth = lubDepth(aparamss.flatten map (_.tpe)) max lubDepth(rparamss.flatten map (_.tpe)) + val atargs = solvedTypes(atvars, atparams, atparams map varianceInType(aret), upper = false, maxLubDepth) + val boundsOk = typer.silent(_.infer.checkBounds(macroDdef, NoPrefix, NoSymbol, atparams, atargs, "")) + boundsOk match { + case SilentResultValue(true) => // do nothing, success + case SilentResultValue(false) | SilentTypeError(_) => MacroImplTargMismatchError(atargs, atparams) + } + } catch { + case ex: NoInstance => MacroImplTparamInstantiationError(atparams, ex) } - } catch { - case ex: NoInstance => MacroImplTparamInstantiationError(atparams, ex) } - } - // aXXX (e.g. aparamss) => characteristics of the actual macro impl signature extracted from the macro impl ("a" stands for "actual") - // rXXX (e.g. rparamss) => characteristics of the reference macro impl signature synthesized from the macro def ("r" stands for "reference") - // FIXME: cannot write this concisely because of SI-7507 - //lazy val MacroImplSig(atparams, aparamss, aret) = macroImplSig - //lazy val MacroImplSig(_, rparamss, rret) = referenceMacroImplSig - lazy val atparams = macroImplSig.tparams - lazy val aparamss = macroImplSig.paramss - lazy val aret = macroImplSig.ret - lazy val rparamss = referenceMacroImplSig.paramss - lazy val rret = referenceMacroImplSig.ret - - // Technically this can be just an alias to MethodType, but promoting it to a first-class entity - // provides better encapsulation and convenient syntax for pattern matching. - private case class MacroImplSig(tparams: List[Symbol], paramss: List[List[Symbol]], ret: Type) { - private def tparams_s = if (tparams.isEmpty) "" else tparams.map(_.defString).mkString("[", ", ", "]") - private def paramss_s = paramss map (ps => ps.map(s => s"${s.name}: ${s.tpe_*}").mkString("(", ", ", ")")) mkString "" - override def toString = "MacroImplSig(" + tparams_s + paramss_s + ret + ")" - } + // aXXX (e.g. aparamss) => characteristics of the actual macro impl signature extracted from the macro impl ("a" stands for "actual") + // rXXX (e.g. rparamss) => characteristics of the reference macro impl signature synthesized from the macro def ("r" stands for "reference") + // FIXME: cannot write this concisely because of SI-7507 + //lazy val MacroImplSig(atparams, aparamss, aret) = macroImplSig + //lazy val MacroImplSig(_, rparamss, rret) = referenceMacroImplSig + lazy val atparams = macroImplSig.tparams + lazy val aparamss = macroImplSig.paramss + lazy val aret = macroImplSig.ret + lazy val rparamss = referenceMacroImplSig.paramss + lazy val rret = referenceMacroImplSig.ret + + // Technically this can be just an alias to MethodType, but promoting it to a first-class entity + // provides better encapsulation and convenient syntax for pattern matching. + private case class MacroImplSig(tparams: List[Symbol], paramss: List[List[Symbol]], ret: Type) { + private def tparams_s = if (tparams.isEmpty) "" else tparams.map(_.defString).mkString("[", ", ", "]") + private def paramss_s = paramss map (ps => ps.map(s => s"${s.name}: ${s.tpe_*}").mkString("(", ", ", ")")) mkString "" + override def toString = "MacroImplSig(" + tparams_s + paramss_s + ret + ")" + } - /** An actual macro implementation signature extracted from a macro implementation method. - * - * For the following macro impl: - * def fooBar[T: c.WeakTypeTag] - * (c: scala.reflect.macros.blackbox.Context) - * (xs: c.Expr[List[T]]) - * : c.Expr[T] = ... - * - * This function will return: - * (c: scala.reflect.macros.blackbox.Context)(xs: c.Expr[List[T]])c.Expr[T] - * - * Note that type tag evidence parameters are not included into the result. - * Type tag context bounds for macro impl tparams are optional. - * Therefore compatibility checks ignore such parameters, and we don't need to bother about them here. - * - * This method cannot be reduced to just macroImpl.info, because macro implementations might - * come in different shapes. If the implementation is an apply method of a *box.Macro-compatible object, - * then it won't have (c: *box.Context) in its parameters, but will rather refer to *boxMacro.c. - * - * @param macroImpl The macro implementation symbol - */ - private lazy val macroImplSig: MacroImplSig = { - val tparams = macroImpl.typeParams - val paramss = transformTypeTagEvidenceParams(macroImplRef, (param, tparam) => NoSymbol) - val ret = macroImpl.info.finalResultType - MacroImplSig(tparams, paramss, ret) - } + /** An actual macro implementation signature extracted from a macro implementation method. + * + * For the following macro impl: + * def fooBar[T: c.WeakTypeTag] + * (c: scala.reflect.macros.blackbox.Context) + * (xs: c.Expr[List[T]]) + * : c.Expr[T] = ... + * + * This function will return: + * (c: scala.reflect.macros.blackbox.Context)(xs: c.Expr[List[T]])c.Expr[T] + * + * Note that type tag evidence parameters are not included into the result. + * Type tag context bounds for macro impl tparams are optional. + * Therefore compatibility checks ignore such parameters, and we don't need to bother about them here. + * + * This method cannot be reduced to just macroImpl.info, because macro implementations might + * come in different shapes. If the implementation is an apply method of a *box.Macro-compatible object, + * then it won't have (c: *box.Context) in its parameters, but will rather refer to *boxMacro.c. + * + * @param macroImpl The macro implementation symbol + */ + private lazy val macroImplSig: MacroImplSig = { + val tparams = macroImpl.typeParams + val paramss = transformTypeTagEvidenceParams(macroImplRef, (param, tparam) => NoSymbol) + val ret = macroImpl.info.finalResultType + MacroImplSig(tparams, paramss, ret) + } - /** A reference macro implementation signature extracted from a given macro definition. - * - * For the following macro def: - * def foo[T](xs: List[T]): T = macro fooBar - * - * This function will return: - * (c: scala.reflect.macros.blackbox.Context)(xs: c.Expr[List[T]])c.Expr[T] or - * (c: scala.reflect.macros.whitebox.Context)(xs: c.Expr[List[T]])c.Expr[T] - * - * Note that type tag evidence parameters are not included into the result. - * Type tag context bounds for macro impl tparams are optional. - * Therefore compatibility checks ignore such parameters, and we don't need to bother about them here. - * - * Also note that we need a DefDef, not the corresponding MethodSymbol, because that symbol would be of no use for us. - * Macro signatures are verified when typechecking macro defs, which means that at that moment inspecting macroDef.info - * means asking for cyclic reference errors. - * - * We need macro implementation symbol as well, because the return type of the macro definition might be omitted, - * and in that case we'd need to infer it from the return type of the macro implementation. Luckily for us, we can - * use that symbol without a risk of running into cycles. - * - * @param typer Typechecker of `macroDdef` - * @param macroDdef The macro definition tree - * @param macroImpl The macro implementation symbol - */ - private lazy val referenceMacroImplSig: MacroImplSig = { - // had to move method's body to an object because of the recursive dependencies between sigma and param - object SigGenerator { - val cache = scala.collection.mutable.Map[Symbol, Symbol]() - val ctxTpe = if (isImplBlackbox) BlackboxContextClass.tpe else WhiteboxContextClass.tpe - val ctxPrefix = - if (isImplMethod) singleType(NoPrefix, makeParam(nme.macroContext, macroDdef.pos, ctxTpe, SYNTHETIC)) - else singleType(ThisType(macroImpl.owner), macroImpl.owner.tpe.member(nme.c)) - val paramss = - if (isImplMethod) List(ctxPrefix.termSymbol) :: mmap(macroDdef.vparamss)(param) - else mmap(macroDdef.vparamss)(param) - val macroDefRet = - if (!macroDdef.tpt.isEmpty) typer.typedType(macroDdef.tpt).tpe - else computeMacroDefTypeFromMacroImplRef(macroDdef, macroImplRef) orElse AnyTpe - val implReturnType = sigma(increaseMetalevel(ctxPrefix, macroDefRet)) - - object SigmaTypeMap extends TypeMap { - def mapPrefix(pre: Type) = pre match { - case ThisType(sym) if sym == macroDef.owner => - singleType(singleType(ctxPrefix, MacroContextPrefix), ExprValue) - case SingleType(NoPrefix, sym) => - mfind(macroDdef.vparamss)(_.symbol == sym).fold(pre)(p => singleType(singleType(NoPrefix, param(p)), ExprValue)) - case _ => - mapOver(pre) - } - def apply(tp: Type): Type = tp match { - case TypeRef(pre, sym, args) => - val pre1 = mapPrefix(pre) - val args1 = mapOverArgs(args, sym.typeParams) - if ((pre eq pre1) && (args eq args1)) tp - else typeRef(pre1, sym, args1) - case _ => - mapOver(tp) + /** A reference macro implementation signature extracted from a given macro definition. + * + * For the following macro def: + * def foo[T](xs: List[T]): T = macro fooBar + * + * This function will return: + * (c: scala.reflect.macros.blackbox.Context)(xs: c.Expr[List[T]])c.Expr[T] or + * (c: scala.reflect.macros.whitebox.Context)(xs: c.Expr[List[T]])c.Expr[T] + * + * Note that type tag evidence parameters are not included into the result. + * Type tag context bounds for macro impl tparams are optional. + * Therefore compatibility checks ignore such parameters, and we don't need to bother about them here. + * + * Also note that we need a DefDef, not the corresponding MethodSymbol, because that symbol would be of no use for us. + * Macro signatures are verified when typechecking macro defs, which means that at that moment inspecting macroDef.info + * means asking for cyclic reference errors. + * + * We need macro implementation symbol as well, because the return type of the macro definition might be omitted, + * and in that case we'd need to infer it from the return type of the macro implementation. Luckily for us, we can + * use that symbol without a risk of running into cycles. + * + * @param typer Typechecker of `macroDdef` + * @param macroDdef The macro definition tree + * @param macroImpl The macro implementation symbol + */ + private lazy val referenceMacroImplSig: MacroImplSig = { + // had to move method's body to an object because of the recursive dependencies between sigma and param + object SigGenerator { + val cache = scala.collection.mutable.Map[Symbol, Symbol]() + val ctxTpe = if (isImplBlackbox) BlackboxContextClass.tpe else WhiteboxContextClass.tpe + val ctxPrefix = + if (isImplMethod) singleType(NoPrefix, makeParam(nme.macroContext, macroDdef.pos, ctxTpe, SYNTHETIC)) + else singleType(ThisType(macroImpl.owner), macroImpl.owner.tpe.member(nme.c)) + val paramss = + if (isImplMethod) List(ctxPrefix.termSymbol) :: mmap(macroDdef.vparamss)(param) + else mmap(macroDdef.vparamss)(param) + val macroDefRet = + if (!macroDdef.tpt.isEmpty) typer.typedType(macroDdef.tpt).tpe + else computeMacroDefTypeFromMacroImplRef(macroDdef, macroImplRef) orElse AnyTpe + val implReturnType = sigma(increaseMetalevel(ctxPrefix, macroDefRet)) + + object SigmaTypeMap extends TypeMap { + def mapPrefix(pre: Type) = pre match { + case ThisType(sym) if sym == macroDef.owner => + singleType(singleType(ctxPrefix, MacroContextPrefix), ExprValue) + case SingleType(NoPrefix, sym) => + mfind(macroDdef.vparamss)(_.symbol == sym).fold(pre)(p => singleType(singleType(NoPrefix, param(p)), ExprValue)) + case _ => + mapOver(pre) + } + def apply(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) => + val pre1 = mapPrefix(pre) + val args1 = mapOverArgs(args, sym.typeParams) + if ((pre eq pre1) && (args eq args1)) tp + else typeRef(pre1, sym, args1) + case _ => + mapOver(tp) + } } + def sigma(tpe: Type): Type = SigmaTypeMap(tpe) + + def makeParam(name: Name, pos: Position, tpe: Type, flags: Long) = + macroDef.newValueParameter(name.toTermName, pos, flags) setInfo tpe + def param(tree: Tree): Symbol = ( + cache.getOrElseUpdate(tree.symbol, { + val sym = tree.symbol + assert(sym.isTerm, s"sym = $sym, tree = $tree") + makeParam(sym.name, sym.pos, sigma(increaseMetalevel(ctxPrefix, sym.tpe)), sym.flags) + }) + ) } - def sigma(tpe: Type): Type = SigmaTypeMap(tpe) - - def makeParam(name: Name, pos: Position, tpe: Type, flags: Long) = - macroDef.newValueParameter(name.toTermName, pos, flags) setInfo tpe - def param(tree: Tree): Symbol = ( - cache.getOrElseUpdate(tree.symbol, { - val sym = tree.symbol - assert(sym.isTerm, s"sym = $sym, tree = $tree") - makeParam(sym.name, sym.pos, sigma(increaseMetalevel(ctxPrefix, sym.tpe)), sym.flags) - }) - ) - } - import SigGenerator._ - macroLogVerbose(s"generating macroImplSigs for: $macroDdef") - val result = MacroImplSig(macroDdef.tparams map (_.symbol), paramss, implReturnType) - macroLogVerbose(s"result is: $result") - result + import SigGenerator._ + macroLogVerbose(s"generating macroImplSigs for: $macroDdef") + val result = MacroImplSig(macroDdef.tparams map (_.symbol), paramss, implReturnType) + macroLogVerbose(s"result is: $result") + result + } } } diff --git a/src/compiler/scala/reflect/macros/contexts/Typers.scala b/src/compiler/scala/reflect/macros/contexts/Typers.scala index f1620b764b..28c1e3ddb3 100644 --- a/src/compiler/scala/reflect/macros/contexts/Typers.scala +++ b/src/compiler/scala/reflect/macros/contexts/Typers.scala @@ -20,15 +20,12 @@ trait Typers { def typecheck(tree: Tree, mode: TypecheckMode = TERMmode, pt: Type = universe.WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree = { macroLogVerbose("typechecking %s with expected type %s, implicit views = %s, macros = %s".format(tree, pt, !withImplicitViewsDisabled, !withMacrosDisabled)) val context = callsiteTyper.context - val wrapper1 = if (!withImplicitViewsDisabled) (context.withImplicitsEnabled[Tree] _) else (context.withImplicitsDisabled[Tree] _) - val wrapper2 = if (!withMacrosDisabled) (context.withMacrosEnabled[Tree] _) else (context.withMacrosDisabled[Tree] _) - def wrapper (tree: => Tree) = wrapper1(wrapper2(tree)) - // if you get a "silent mode is not available past typer" here - // don't rush to change the typecheck not to use the silent method when the silent parameter is false - // typechecking uses silent anyways (e.g. in typedSelect), so you'll only waste your time - // I'd advise fixing the root cause: finding why the context is not set to report errors - // (also see reflect.runtime.ToolBoxes.typeCheckExpr for a workaround that might work for you) - wrapper(callsiteTyper.silent(_.typed(universe.duplicateAndKeepPositions(tree), mode, pt), reportAmbiguousErrors = false) match { + val withImplicitFlag = if (!withImplicitViewsDisabled) (context.withImplicitsEnabled[Tree] _) else (context.withImplicitsDisabled[Tree] _) + val withMacroFlag = if (!withMacrosDisabled) (context.withMacrosEnabled[Tree] _) else (context.withMacrosDisabled[Tree] _) + def withContext(tree: => Tree) = withImplicitFlag(withMacroFlag(tree)) + def withWrapping(tree: Tree)(op: Tree => Tree) = if (mode == TERMmode) universe.wrappingIntoTerm(tree)(op) else op(tree) + def typecheckInternal(tree: Tree) = callsiteTyper.silent(_.typed(universe.duplicateAndKeepPositions(tree), mode, pt), reportAmbiguousErrors = false) + withWrapping(tree)(wrappedTree => withContext(typecheckInternal(wrappedTree) match { case universe.analyzer.SilentResultValue(result) => macroLogVerbose(result) result @@ -36,7 +33,7 @@ trait Typers { macroLogVerbose(error.err.errMsg) if (!silent) throw new TypecheckException(error.err.errPos, error.err.errMsg) universe.EmptyTree - }) + })) } def inferImplicitValue(pt: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: Position = enclosingPosition): Tree = { diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala index d553d71bf5..c2d62db558 100644 --- a/src/compiler/scala/tools/nsc/ScriptRunner.scala +++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala @@ -19,7 +19,7 @@ import util.Exceptional.unwrap * exec scala "$0" "$@" * !# * Console.println("Hello, world!") - * argv.toList foreach Console.println + * args.toList foreach Console.println * </pre> * <p>And here is a batch file example on Windows XP:</p> * <pre> @@ -29,7 +29,7 @@ import util.Exceptional.unwrap * goto :eof * ::!# * Console.println("Hello, world!") - * argv.toList foreach Console.println + * args.toList foreach Console.println * </pre> * * @author Lex Spoon diff --git a/src/compiler/scala/tools/nsc/ast/Printers.scala b/src/compiler/scala/tools/nsc/ast/Printers.scala index c64b18207a..f3def3c80c 100644 --- a/src/compiler/scala/tools/nsc/ast/Printers.scala +++ b/src/compiler/scala/tools/nsc/ast/Printers.scala @@ -178,9 +178,9 @@ trait Printers extends scala.reflect.internal.Printers { this: Global => } } - def asString(t: Tree): String = render(t, newStandardTreePrinter, settings.printtypes, settings.uniqid, settings.Yshowsymkinds) - def asCompactString(t: Tree): String = render(t, newCompactTreePrinter, settings.printtypes, settings.uniqid, settings.Yshowsymkinds) - def asCompactDebugString(t: Tree): String = render(t, newCompactTreePrinter, true, true, true) + def asString(t: Tree): String = render(t, newStandardTreePrinter, settings.printtypes, settings.uniqid, settings.Yshowsymowners, settings.Yshowsymkinds) + def asCompactString(t: Tree): String = render(t, newCompactTreePrinter, settings.printtypes, settings.uniqid, settings.Yshowsymowners, settings.Yshowsymkinds) + def asCompactDebugString(t: Tree): String = render(t, newCompactTreePrinter, true, true, true, true) def newStandardTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) def newCompactTreePrinter(writer: PrintWriter): CompactTreePrinter = new CompactTreePrinter(writer) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 0a753f157c..3542fe5945 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -413,12 +413,10 @@ self => * * {{{ * object moduleName { - * def main(argv: Array[String]): Unit = { - * val args = argv + * def main(args: Array[String]): Unit = * new AnyRef { * stmts * } - * } * } * }}} */ @@ -433,9 +431,8 @@ self => // def main def mainParamType = AppliedTypeTree(Ident(tpnme.Array), List(Ident(tpnme.String))) - def mainParameter = List(ValDef(Modifiers(Flags.PARAM), nme.argv, mainParamType, EmptyTree)) - def mainSetArgv = List(ValDef(NoMods, nme.args, TypeTree(), Ident(nme.argv))) - def mainDef = DefDef(NoMods, nme.main, Nil, List(mainParameter), scalaDot(tpnme.Unit), Block(mainSetArgv, gen.mkAnonymousNew(stmts))) + def mainParameter = List(ValDef(Modifiers(Flags.PARAM), nme.args, mainParamType, EmptyTree)) + def mainDef = DefDef(NoMods, nme.main, Nil, List(mainParameter), scalaDot(tpnme.Unit), gen.mkAnonymousNew(stmts)) // object Main def moduleName = newTermName(ScriptRunner scriptMain settings) @@ -2495,7 +2492,7 @@ self => def mkDefs(p: Tree, tp: Tree, rhs: Tree): List[Tree] = { val trees = { val pat = if (tp.isEmpty) p else Typed(p, tp) setPos (p.pos union tp.pos) - gen.mkPatDef(newmods, pat, rhs) + makePatDef(newmods, pat, rhs) } if (newmods.isDeferred) { trees match { diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 525dcffb0c..6e5a3f6ef7 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -168,4 +168,6 @@ abstract class TreeBuilder { vparamss ::: List(evidenceParams) } } + + def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree) = gen.mkPatDef(mods, pat, rhs) } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala index 18ccced75e..359e5d6c29 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala @@ -583,53 +583,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { trait BCAnnotGen extends BCInnerClassGen { - /* - * can-multi-thread - */ - def ubytesToCharArray(bytes: Array[Byte]): Array[Char] = { - val ca = new Array[Char](bytes.length) - var idx = 0 - while (idx < bytes.length) { - val b: Byte = bytes(idx) - assert((b & ~0x7f) == 0) - ca(idx) = b.asInstanceOf[Char] - idx += 1 - } - - ca - } - - /* - * can-multi-thread - */ - private def arrEncode(sb: ScalaSigBytes): Array[String] = { - var strs: List[String] = Nil - val bSeven: Array[Byte] = sb.sevenBitsMayBeZero - // chop into slices of at most 65535 bytes, counting 0x00 as taking two bytes (as per JVMS 4.4.7 The CONSTANT_Utf8_info Structure) - var prevOffset = 0 - var offset = 0 - var encLength = 0 - while (offset < bSeven.size) { - val deltaEncLength = (if (bSeven(offset) == 0) 2 else 1) - val newEncLength = encLength.toLong + deltaEncLength - if (newEncLength >= 65535) { - val ba = bSeven.slice(prevOffset, offset) - strs ::= new java.lang.String(ubytesToCharArray(ba)) - encLength = 0 - prevOffset = offset - } else { - encLength += deltaEncLength - offset += 1 - } - } - if (prevOffset < offset) { - assert(offset == bSeven.length) - val ba = bSeven.slice(prevOffset, offset) - strs ::= new java.lang.String(ubytesToCharArray(ba)) - } - assert(strs.size > 1, "encode instead as one String via strEncode()") // TODO too strict? - mkArrayReverse(strs) - } + import genASM.{ubytesToCharArray, arrEncode} /* * can-multi-thread @@ -676,7 +630,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { av.visit(name, strEncode(sb)) } else { val arrAnnotV: asm.AnnotationVisitor = av.visitArray(name) - for(arg <- arrEncode(sb)) { arrAnnotV.visit(name, arg) } + for(arg <- genASM.arrEncode(sb)) { arrAnnotV.visit(name, arg) } arrAnnotV.visitEnd() } // for the lazy val in ScalaSigBytes to be GC'ed, the invoker of emitAnnotations() should hold the ScalaSigBytes in a method-local var that doesn't escape. @@ -772,25 +726,6 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { trait BCJGenSigGen { - // @M don't generate java generics sigs for (members of) implementation - // classes, as they are monomorphic (TODO: ok?) - /* - * must-single-thread - */ - private def needsGenericSignature(sym: Symbol) = !( - // PP: This condition used to include sym.hasExpandedName, but this leads - // to the total loss of generic information if a private member is - // accessed from a closure: both the field and the accessor were generated - // without it. This is particularly bad because the availability of - // generic information could disappear as a consequence of a seemingly - // unrelated change. - settings.Ynogenericsig - || sym.isArtifact - || sym.isLiftedMethod - || sym.isBridge - || (sym.ownerChain exists (_.isImplClass)) - ) - def getCurrentCUnit(): CompilationUnit /* @return @@ -799,61 +734,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { * * must-single-thread */ - def getGenericSignature(sym: Symbol, owner: Symbol): String = { - - if (!needsGenericSignature(sym)) { return null } - - val memberTpe = enteringErasure(owner.thisType.memberInfo(sym)) - - val jsOpt: Option[String] = erasure.javaSig(sym, memberTpe) - if (jsOpt.isEmpty) { return null } - - val sig = jsOpt.get - log(sig) // This seems useful enough in the general case. - - def wrap(op: => Unit) = { - try { op; true } - catch { case _: Throwable => false } - } - - if (settings.Xverify) { - // Run the signature parser to catch bogus signatures. - val isValidSignature = wrap { - // Alternative: scala.tools.reflect.SigParser (frontend to sun.reflect.generics.parser.SignatureParser) - import scala.tools.asm.util.CheckClassAdapter - if (sym.isMethod) { CheckClassAdapter checkMethodSignature sig } - else if (sym.isTerm) { CheckClassAdapter checkFieldSignature sig } - else { CheckClassAdapter checkClassSignature sig } - } - - if (!isValidSignature) { - getCurrentCUnit().warning(sym.pos, - """|compiler bug: created invalid generic signature for %s in %s - |signature: %s - |if this is reproducible, please report bug at https://issues.scala-lang.org/ - """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig)) - return null - } - } - - if ((settings.check containsName phaseName)) { - val normalizedTpe = enteringErasure(erasure.prepareSigMap(memberTpe)) - val bytecodeTpe = owner.thisType.memberInfo(sym) - if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym)(normalizedTpe) =:= bytecodeTpe)) { - getCurrentCUnit().warning(sym.pos, - """|compiler bug: created generic signature for %s in %s that does not conform to its erasure - |signature: %s - |original type: %s - |normalized type: %s - |erasure type: %s - |if this is reproducible, please report bug at http://issues.scala-lang.org/ - """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig, memberTpe, normalizedTpe, bytecodeTpe)) - return null - } - } - - sig - } + def getGenericSignature(sym: Symbol, owner: Symbol): String = genASM.getGenericSignature(sym, owner, getCurrentCUnit()) } // end of trait BCJGenSigGen @@ -906,7 +787,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { ) // TODO needed? for(ann <- m.annotations) { ann.symbol.initialize } - val jgensig = if (m.isDeferred) null else getGenericSignature(m, module); // only add generic signature if method concrete; bug #1745 + val jgensig = genASM.staticForwarderGenericSignature(m, module, getCurrentCUnit()) addRemoteExceptionAnnot(isRemoteClass, hasPublicBitSet(flags), m) val (throws, others) = m.annotations partition (_.symbol == definitions.ThrowsClass) val thrownExceptions: List[String] = getExceptions(throws) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index b03519ce7d..a389816caf 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -20,7 +20,7 @@ import scala.annotation.tailrec * * Documentation at http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/2012Q2/GenASM.pdf */ -abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { +abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { self => import global._ import icodes._ import icodes.opcodes._ @@ -803,134 +803,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { annot.args.isEmpty && !annot.matches(DeprecatedAttr) - // @M don't generate java generics sigs for (members of) implementation - // classes, as they are monomorphic (TODO: ok?) - private def needsGenericSignature(sym: Symbol) = !( - // PP: This condition used to include sym.hasExpandedName, but this leads - // to the total loss of generic information if a private member is - // accessed from a closure: both the field and the accessor were generated - // without it. This is particularly bad because the availability of - // generic information could disappear as a consequence of a seemingly - // unrelated change. - settings.Ynogenericsig - || sym.isArtifact - || sym.isLiftedMethod - || sym.isBridge - || (sym.ownerChain exists (_.isImplClass)) - ) - def getCurrentCUnit(): CompilationUnit - /** @return - * - `null` if no Java signature is to be added (`null` is what ASM expects in these cases). - * - otherwise the signature in question - */ - def getGenericSignature(sym: Symbol, owner: Symbol): String = { - - if (!needsGenericSignature(sym)) { return null } - - val memberTpe = enteringErasure(owner.thisType.memberInfo(sym)) - - val jsOpt: Option[String] = erasure.javaSig(sym, memberTpe) - if (jsOpt.isEmpty) { return null } - - val sig = jsOpt.get - log(sig) // This seems useful enough in the general case. - - def wrap(op: => Unit) = { - try { op; true } - catch { case _: Throwable => false } - } - - if (settings.Xverify) { - // Run the signature parser to catch bogus signatures. - val isValidSignature = wrap { - // Alternative: scala.tools.reflect.SigParser (frontend to sun.reflect.generics.parser.SignatureParser) - import scala.tools.asm.util.CheckClassAdapter - if (sym.isMethod) { CheckClassAdapter checkMethodSignature sig } // requires asm-util.jar - else if (sym.isTerm) { CheckClassAdapter checkFieldSignature sig } - else { CheckClassAdapter checkClassSignature sig } - } - - if(!isValidSignature) { - getCurrentCUnit().warning(sym.pos, - """|compiler bug: created invalid generic signature for %s in %s - |signature: %s - |if this is reproducible, please report bug at https://issues.scala-lang.org/ - """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig)) - return null - } - } - - if ((settings.check containsName phaseName)) { - val normalizedTpe = enteringErasure(erasure.prepareSigMap(memberTpe)) - val bytecodeTpe = owner.thisType.memberInfo(sym) - if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym)(normalizedTpe) =:= bytecodeTpe)) { - getCurrentCUnit().warning(sym.pos, - """|compiler bug: created generic signature for %s in %s that does not conform to its erasure - |signature: %s - |original type: %s - |normalized type: %s - |erasure type: %s - |if this is reproducible, please report bug at http://issues.scala-lang.org/ - """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig, memberTpe, normalizedTpe, bytecodeTpe)) - return null - } - } - - sig - } - - def ubytesToCharArray(bytes: Array[Byte]): Array[Char] = { - val ca = new Array[Char](bytes.length) - var idx = 0 - while(idx < bytes.length) { - val b: Byte = bytes(idx) - assert((b & ~0x7f) == 0) - ca(idx) = b.asInstanceOf[Char] - idx += 1 - } - - ca - } - - private def arrEncode(sb: ScalaSigBytes): Array[String] = { - var strs: List[String] = Nil - val bSeven: Array[Byte] = sb.sevenBitsMayBeZero - // chop into slices of at most 65535 bytes, counting 0x00 as taking two bytes (as per JVMS 4.4.7 The CONSTANT_Utf8_info Structure) - var prevOffset = 0 - var offset = 0 - var encLength = 0 - while(offset < bSeven.length) { - val deltaEncLength = (if(bSeven(offset) == 0) 2 else 1) - val newEncLength = encLength.toLong + deltaEncLength - if(newEncLength >= 65535) { - val ba = bSeven.slice(prevOffset, offset) - strs ::= new java.lang.String(ubytesToCharArray(ba)) - encLength = 0 - prevOffset = offset - } else { - encLength += deltaEncLength - offset += 1 - } - } - if(prevOffset < offset) { - assert(offset == bSeven.length) - val ba = bSeven.slice(prevOffset, offset) - strs ::= new java.lang.String(ubytesToCharArray(ba)) - } - assert(strs.size > 1, "encode instead as one String via strEncode()") // TODO too strict? - strs.reverse.toArray - } - - private def strEncode(sb: ScalaSigBytes): String = { - val ca = ubytesToCharArray(sb.sevenBitsMayBeZero) - new java.lang.String(ca) - // debug val bvA = new asm.ByteVector; bvA.putUTF8(s) - // debug val enc: Array[Byte] = scala.reflect.internal.pickling.ByteCodecs.encode(bytes) - // debug assert(enc(idx) == bvA.getByte(idx + 2)) - // debug assert(bvA.getLength == enc.size + 2) - } + def getGenericSignature(sym: Symbol, owner: Symbol) = self.getGenericSignature(sym, owner, getCurrentCUnit()) def emitArgument(av: asm.AnnotationVisitor, name: String, @@ -1064,7 +939,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { ) // TODO needed? for(ann <- m.annotations) { ann.symbol.initialize } - val jgensig = if (m.isDeferred) null else getGenericSignature(m, module); // only add generic signature if method concrete; bug #1745 + val jgensig = staticForwarderGenericSignature(m, module, getCurrentCUnit()) addRemoteExceptionAnnot(isRemoteClass, hasPublicBitSet(flags), m) val (throws, others) = m.annotations partition (_.symbol == ThrowsClass) val thrownExceptions: List[String] = getExceptions(throws) @@ -3303,4 +3178,147 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } + // @M don't generate java generics sigs for (members of) implementation + // classes, as they are monomorphic (TODO: ok?) + private def needsGenericSignature(sym: Symbol) = !( + // PP: This condition used to include sym.hasExpandedName, but this leads + // to the total loss of generic information if a private member is + // accessed from a closure: both the field and the accessor were generated + // without it. This is particularly bad because the availability of + // generic information could disappear as a consequence of a seemingly + // unrelated change. + settings.Ynogenericsig + || sym.isArtifact + || sym.isLiftedMethod + || sym.isBridge + || (sym.ownerChain exists (_.isImplClass)) + ) + + final def staticForwarderGenericSignature(sym: Symbol, moduleClass: Symbol, unit: CompilationUnit): String = { + if (sym.isDeferred) null // only add generic signature if method concrete; bug #1745 + else { + // SI-3452 Static forwarder generation uses the same erased signature as the method if forwards to. + // By rights, it should use the signature as-seen-from the module class, and add suitable + // primitive and value-class boxing/unboxing. + // But for now, just like we did in mixin, we just avoid writing a wrong generic signature + // (one that doesn't erase to the actual signature). See run/t3452b for a test case. + val memberTpe = enteringErasure(moduleClass.thisType.memberInfo(sym)) + val erasedMemberType = erasure.erasure(sym)(memberTpe) + if (erasedMemberType =:= sym.info) + getGenericSignature(sym, moduleClass, memberTpe, unit) + else null + } + } + + /** @return + * - `null` if no Java signature is to be added (`null` is what ASM expects in these cases). + * - otherwise the signature in question + */ + def getGenericSignature(sym: Symbol, owner: Symbol, unit: CompilationUnit): String = { + val memberTpe = enteringErasure(owner.thisType.memberInfo(sym)) + getGenericSignature(sym, owner, memberTpe, unit) + } + def getGenericSignature(sym: Symbol, owner: Symbol, memberTpe: Type, unit: CompilationUnit): String = { + if (!needsGenericSignature(sym)) { return null } + + val jsOpt: Option[String] = erasure.javaSig(sym, memberTpe) + if (jsOpt.isEmpty) { return null } + + val sig = jsOpt.get + log(sig) // This seems useful enough in the general case. + + def wrap(op: => Unit) = { + try { op; true } + catch { case _: Throwable => false } + } + + if (settings.Xverify) { + // Run the signature parser to catch bogus signatures. + val isValidSignature = wrap { + // Alternative: scala.tools.reflect.SigParser (frontend to sun.reflect.generics.parser.SignatureParser) + import scala.tools.asm.util.CheckClassAdapter + if (sym.isMethod) { CheckClassAdapter checkMethodSignature sig } // requires asm-util.jar + else if (sym.isTerm) { CheckClassAdapter checkFieldSignature sig } + else { CheckClassAdapter checkClassSignature sig } + } + + if(!isValidSignature) { + unit.warning(sym.pos, + """|compiler bug: created invalid generic signature for %s in %s + |signature: %s + |if this is reproducible, please report bug at https://issues.scala-lang.org/ + """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig)) + return null + } + } + + if ((settings.check containsName phaseName)) { + val normalizedTpe = enteringErasure(erasure.prepareSigMap(memberTpe)) + val bytecodeTpe = owner.thisType.memberInfo(sym) + if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym)(normalizedTpe) =:= bytecodeTpe)) { + unit.warning(sym.pos, + """|compiler bug: created generic signature for %s in %s that does not conform to its erasure + |signature: %s + |original type: %s + |normalized type: %s + |erasure type: %s + |if this is reproducible, please report bug at http://issues.scala-lang.org/ + """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig, memberTpe, normalizedTpe, bytecodeTpe)) + return null + } + } + + sig + } + + def ubytesToCharArray(bytes: Array[Byte]): Array[Char] = { + val ca = new Array[Char](bytes.length) + var idx = 0 + while(idx < bytes.length) { + val b: Byte = bytes(idx) + assert((b & ~0x7f) == 0) + ca(idx) = b.asInstanceOf[Char] + idx += 1 + } + + ca + } + + final def arrEncode(sb: ScalaSigBytes): Array[String] = { + var strs: List[String] = Nil + val bSeven: Array[Byte] = sb.sevenBitsMayBeZero + // chop into slices of at most 65535 bytes, counting 0x00 as taking two bytes (as per JVMS 4.4.7 The CONSTANT_Utf8_info Structure) + var prevOffset = 0 + var offset = 0 + var encLength = 0 + while(offset < bSeven.length) { + val deltaEncLength = (if(bSeven(offset) == 0) 2 else 1) + val newEncLength = encLength.toLong + deltaEncLength + if(newEncLength >= 65535) { + val ba = bSeven.slice(prevOffset, offset) + strs ::= new java.lang.String(ubytesToCharArray(ba)) + encLength = 0 + prevOffset = offset + } else { + encLength += deltaEncLength + offset += 1 + } + } + if(prevOffset < offset) { + assert(offset == bSeven.length) + val ba = bSeven.slice(prevOffset, offset) + strs ::= new java.lang.String(ubytesToCharArray(ba)) + } + assert(strs.size > 1, "encode instead as one String via strEncode()") // TODO too strict? + strs.reverse.toArray + } + + private def strEncode(sb: ScalaSigBytes): String = { + val ca = ubytesToCharArray(sb.sevenBitsMayBeZero) + new java.lang.String(ca) + // debug val bvA = new asm.ByteVector; bvA.putUTF8(s) + // debug val enc: Array[Byte] = scala.reflect.internal.pickling.ByteCodecs.encode(bytes) + // debug assert(enc(idx) == bvA.getByte(idx + 2)) + // debug assert(bvA.getLength == enc.size + 2) + } } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index a3114a3d7b..a385a31165 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -168,6 +168,7 @@ trait ScalaSettings extends AbsScalaSettings = BooleanSetting ("-Yshow-trees-stringified", "(Requires -Xprint:) Print stringifications along with detailed ASTs.") val Yshowsyms = BooleanSetting ("-Yshow-syms", "Print the AST symbol hierarchy after each phase.") val Yshowsymkinds = BooleanSetting ("-Yshow-symkinds", "Print abbreviated symbol kinds next to symbol names.") + val Yshowsymowners = BooleanSetting ("-Yshow-symowners", "Print owner identifiers next to symbol names.") val skip = PhasesSetting ("-Yskip", "Skip") val Ygenjavap = StringSetting ("-Ygen-javap", "dir", "Generate a parallel output directory of .javap files.", "") val Ygenasmp = StringSetting ("-Ygen-asmp", "dir", "Generate a parallel output directory of .asmp files (ie ASM Textifier output).", "") diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 89f9cb4b06..673bc04bd9 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -172,18 +172,23 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // info) as they are seen from the class. We can't use the member that we get from the // implementation class, as it's a clone that was made after erasure, and thus it does not // know its info at the beginning of erasure anymore. - // Optimize: no need if mixinClass has no typeparams. - mixinMember cloneSymbol clazz modifyInfo (info => - if (mixinClass.typeParams.isEmpty) info - else (clazz.thisType baseType mixinClass) memberInfo mixinMember - ) + val sym = mixinMember cloneSymbol clazz + + val erasureMap = erasure.erasure(mixinMember) + val erasedInterfaceInfo: Type = erasureMap(mixinMember.info) + val specificForwardInfo = (clazz.thisType baseType mixinClass) memberInfo mixinMember + val forwarderInfo = + if (erasureMap(specificForwardInfo) =:= erasedInterfaceInfo) + specificForwardInfo + else { + erasedInterfaceInfo + } + // Optimize: no need if mixinClass has no typeparams. + // !!! JZ Really? What about the effect of abstract types, prefix? + if (mixinClass.typeParams.isEmpty) sym + else sym modifyInfo (_ => forwarderInfo) } - // clone before erasure got rid of type info we'll need to generate a javaSig - // now we'll have the type info at (the beginning of) erasure in our history, - // and now newSym has the info that's been transformed to fit this period - // (no need for asSeenFrom as phase.erasedTypes) - // TODO: verify we need the updateInfo and document why - newSym updateInfo (mixinMember.info cloneInfo newSym) + newSym } /** Add getters and setters for all non-module fields of an implementation diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 2043eb5d5d..40b97394f2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -501,10 +501,6 @@ trait ContextErrors { } // doTypeApply - //tryNamesDefaults - def NamedAndDefaultArgumentsNotSupportedForMacros(tree: Tree, fun: Tree) = - NormalTypeError(tree, "macro applications do not support named and/or default arguments") - def TooManyArgsNamesDefaultsError(tree: Tree, fun: Tree) = NormalTypeError(tree, "too many arguments for "+treeSymTypeMsg(fun)) @@ -603,12 +599,11 @@ trait ContextErrors { //adapt def MissingArgsForMethodTpeError(tree: Tree, meth: Symbol) = { + val errorExplanation = "missing arguments for " + meth.fullLocationString + val suggestPartialApplication = ";\nfollow this method with `_' if you want to treat it as a partially applied function" val message = - if (meth.isMacro) MacroTooFewArgumentListsMessage - else "missing arguments for " + meth.fullLocationString + ( - if (meth.isConstructor) "" - else ";\nfollow this method with `_' if you want to treat it as a partially applied function" - ) + if (meth.isMacro || meth.isConstructor) errorExplanation + else errorExplanation + suggestPartialApplication issueNormalTypeError(tree, message) setError(tree) } @@ -748,15 +743,12 @@ trait ContextErrors { throw MacroExpansionException } - private def MacroTooFewArgumentListsMessage = "too few argument lists for macro invocation" - def MacroTooFewArgumentListsError(expandee: Tree) = macroExpansionError2(expandee, MacroTooFewArgumentListsMessage) - - private def MacroTooManyArgumentListsMessage = "too many argument lists for macro invocation" - def MacroTooManyArgumentListsError(expandee: Tree) = macroExpansionError2(expandee, MacroTooManyArgumentListsMessage) - - def MacroTooFewArgumentsError(expandee: Tree) = macroExpansionError2(expandee, "too few arguments for macro invocation") - - def MacroTooManyArgumentsError(expandee: Tree) = macroExpansionError2(expandee, "too many arguments for macro invocation") + def MacroFastTrackFailed(expandee: Tree) = { + // here we speculate that the reason why FastTrackEntry.validate failed is the lack arguments for a given method + // that's not ideal, but on the other hand this allows us to keep FastTrack simple without hooking errorgen into it + MissingArgsForMethodTpeError(expandee, expandee.symbol) + throw MacroExpansionException + } def MacroGeneratedAbort(expandee: Tree, ex: AbortMacroException) = { // errors have been reported by the macro itself, so we do nothing here diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 677c94e063..1f90dd4939 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -353,7 +353,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { new { val universe: self.global.type = self.global val callsiteTyper: universe.analyzer.Typer = typer.asInstanceOf[global.analyzer.Typer] - val expandee = universe.analyzer.macroExpanderAttachment(expandeeTree).original orElse duplicateAndKeepPositions(expandeeTree) + val expandee = universe.analyzer.macroExpanderAttachment(expandeeTree).desugared orElse duplicateAndKeepPositions(expandeeTree) } with UnaffiliatedMacroContext { val prefix = Expr[Nothing](prefixTree)(TypeTag.Nothing) override def toString = "MacroContext(%s@%s +%d)".format(expandee.symbol.name, expandee.pos, enclosingMacros.length - 1 /* exclude myself */) @@ -371,7 +371,15 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { def standardMacroArgs(typer: Typer, expandee: Tree): MacroArgs = { val macroDef = expandee.symbol val paramss = macroDef.paramss - val treeInfo.Applied(core, targs, argss) = expandee + val treeInfo.Applied(core, targs, maybeNamedArgss) = expandee + val argss = map2(maybeNamedArgss, paramss)((args, params) => { + if (args.exists(_.isInstanceOf[AssignOrNamedArg])) { + val sorted = ListBuffer.fill(params.length)(EmptyTree: Tree) + args foreach { case AssignOrNamedArg(Ident(name), arg) => sorted(params.indexWhere(_.name == name)) = arg } + sorted.toList + } else if (params.length == args.length) args + else args ++ List.fill(params.length - args.length)(EmptyTree) + }) val prefix = core match { case Select(qual, _) => qual; case _ => EmptyTree } val context = expandee.attachments.get[MacroRuntimeAttachment].flatMap(_.macroContext).getOrElse(macroContext(typer, prefix, expandee)) @@ -383,16 +391,11 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { |paramss: $paramss """.trim) - import typer.TyperErrorGen._ - val isNullaryArgsEmptyParams = argss.isEmpty && paramss == ListOfNil - if (paramss.length < argss.length) MacroTooManyArgumentListsError(expandee) - if (paramss.length > argss.length && !isNullaryArgsEmptyParams) MacroTooFewArgumentListsError(expandee) - val macroImplArgs: List[Any] = if (fastTrack contains macroDef) { // Take a dry run of the fast track implementation if (fastTrack(macroDef) validate expandee) argss.flatten - else MacroTooFewArgumentListsError(expandee) + else typer.TyperErrorGen.MacroFastTrackFailed(expandee) } else { def calculateMacroArgs(binding: MacroImplBinding) = { @@ -403,14 +406,6 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { // wrap argss in c.Expr if necessary (i.e. if corresponding macro impl param is of type c.Expr[T]) // expand varargs (nb! varargs can apply to any parameter section, not necessarily to the last one) val trees = map3(argss, paramss, signature)((args, defParams, implParams) => { - val isVarargs = isVarArgsList(defParams) - if (isVarargs) { - if (defParams.length > args.length + 1) MacroTooFewArgumentsError(expandee) - } else { - if (defParams.length < args.length) MacroTooManyArgumentsError(expandee) - if (defParams.length > args.length) MacroTooFewArgumentsError(expandee) - } - val wrappedArgs = mapWithIndex(args)((arg, j) => { val fingerprint = implParams(min(j, implParams.length - 1)) fingerprint match { @@ -421,7 +416,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { } }) - if (isVarargs) { + if (isVarArgsList(defParams)) { val (normal, varargs) = wrappedArgs splitAt (defParams.length - 1) normal :+ varargs // pack all varargs into a single Seq argument (varargs Scala style) } else wrappedArgs @@ -529,9 +524,11 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { * the expandee with an error marker set if there has been an error */ abstract class MacroExpander(val typer: Typer, val expandee: Tree) { + val symbol = expandee match { case Block(_, expr) => expr.symbol; case tree => tree.symbol } + def onSuccess(expanded: Tree): Tree def onFallback(expanded: Tree): Tree - def onSuppressed(expandee: Tree): Tree = expandee + def onSuppressed(expanded: Tree): Tree = expanded def onDelayed(expanded: Tree): Tree = expanded def onSkipped(expanded: Tree): Tree = expanded def onFailure(expanded: Tree): Tree = { typer.infer.setError(expandee); expandee } @@ -551,15 +548,15 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { if (Statistics.canEnable) Statistics.incCounter(macroExpandCount) try { withInfoLevel(nodePrinters.InfoLevel.Quiet) { // verbose printing might cause recursive macro expansions - if (expandee.symbol.isErroneous || (expandee exists (_.isErroneous))) { - val reason = if (expandee.symbol.isErroneous) "not found or incompatible macro implementation" else "erroneous arguments" + if (symbol.isErroneous || (expandee exists (_.isErroneous)) || (desugared exists (_.isErroneous))) { + val reason = if (symbol.isErroneous) "not found or incompatible macro implementation" else "erroneous arguments" macroLogVerbose(s"cancelled macro expansion because of $reason: $expandee") onFailure(typer.infer.setError(expandee)) } else try { val expanded = { - val runtime = macroRuntime(expandee) - if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime) - else macroExpandWithoutRuntime(typer, expandee) + val runtime = macroRuntime(desugared) + if (runtime != null) macroExpandWithRuntime(typer, desugared, runtime) + else macroExpandWithoutRuntime(typer, desugared) } expanded match { case Success(expanded) => @@ -591,7 +588,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { extends MacroExpander(typer, expandee) { lazy val innerPt = { val tp = if (isNullaryInvocation(expandee)) expandee.tpe.finalResultType else expandee.tpe - if (isBlackbox(expandee)) tp + if (isBlackbox(symbol)) tp else { // approximation is necessary for whitebox macros to guide type inference // read more in the comments for onDelayed below @@ -599,6 +596,50 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { deriveTypeWithWildcards(undetparams)(tp) } } + override protected def expand(desugared: Tree) = { + // SI-5940 in order for a macro expansion that involves named or default arguments + // to see the actual prefix and arguments being passed by the user instead of their desugarings + // we need to inline synthetics in case when `fun` is actually a macro + // underlying macro implementation is going to get explicitly passed arguments in correct order + // and the rest (defaults filled in by the vanilla part of `tryNamesDefaults`) will become empty trees + // in order for the macro to be able to account for evaluation order, the original is provided in `c.macroApplication` + // of course, ideally we would like to provide the impl with right-hand sides of those default arguments + // but currently that is flat out impossible because of the difference in scopes + // anyway this is already an improvement over the former status quo when named/default invocations were outright prohibited + def undoNamesDefaults(tree: Tree): Tree = { + val (qualsym, qual, vdefs0, app @ Applied(_, _, argss)) = tree match { + case Block((qualdef @ ValDef(_, name, _, qual)) +: vdefs, app) if name.startsWith(nme.QUAL_PREFIX) => (qualdef.symbol, qual, vdefs, app) + case Block(vdefs, app) => (NoSymbol, EmptyTree, vdefs, app) + case tree => (NoSymbol, EmptyTree, Nil, tree) + } + val vdefs = vdefs0.map{ case vdef: ValDef => vdef } + def hasNamesDefaults(args: List[Tree]) = { + args.exists(arg => isDefaultGetter(arg) || vdefs.exists(_.symbol == arg.symbol)) + } + def undoNamesDefaults(args: List[Tree], depth: Int) = { + def extractRhs(vdef: ValDef) = vdef.rhs.changeOwner(vdef.symbol -> typer.context.owner) + case class Arg(tree: Tree, ipos: Int, inamed: Int) { val param = app.symbol.paramss(depth)(ipos) } + val indexed = args.map(arg => arg -> vdefs.indexWhere(_.symbol == arg.symbol)).zipWithIndex.flatMap({ + /* default */ case ((arg, _), _) if isDefaultGetter(arg) => None + /* positional */ case ((arg, -1), ipos) => Some(Arg(arg, ipos, -1)) + /* default+named */ case ((_, inamed), _) if isDefaultGetter(extractRhs(vdefs(inamed))) => None + /* named */ case ((arg, inamed), ipos) => Some(Arg(extractRhs(vdefs(inamed)), ipos, inamed)) + }) + if (indexed.forall(_.inamed == -1)) indexed.map(_.tree) + else indexed.sortBy(_.inamed).map(arg => AssignOrNamedArg(Ident(arg.param.name), arg.tree)) + } + def loop(tree: Tree, depth: Int): Tree = tree match { + case Apply(fun, args) if hasNamesDefaults(args) => treeCopy.Apply(tree, loop(fun, depth - 1), undoNamesDefaults(args, depth)) + case Apply(fun, args) => treeCopy.Apply(tree, loop(fun, depth - 1), args) + case TypeApply(core, targs) => treeCopy.TypeApply(tree, core, targs) + case Select(core, name) if qualsym != NoSymbol && core.symbol == qualsym => treeCopy.Select(tree, qual, name) + case core => core + } + if (app.symbol == null || app.symbol == NoSymbol || app.exists(_.isErroneous)) tree + else loop(app, depth = argss.length - 1) + } + super.expand(undoNamesDefaults(desugared)) + } override def onSuccess(expanded0: Tree) = { // prematurely annotate the tree with a macro expansion attachment // so that adapt called indirectly by typer.typed knows that it needs to apply the existential fixup @@ -616,7 +657,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { } } - if (isBlackbox(expandee)) { + if (isBlackbox(symbol)) { val expanded1 = atPos(enclosingMacroPosition.makeTransparent)(Typed(expanded0, TypeTree(innerPt))) typecheck("blackbox typecheck", expanded1, outerPt) } else { @@ -678,7 +719,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { // Thanks to that the materializer can take a look at what's going on and react accordingly. val shouldInstantiate = typer.context.undetparams.nonEmpty && !mode.inPolyMode if (shouldInstantiate) { - if (isBlackbox(expandee)) typer.instantiatePossiblyExpectingUnit(delayed, mode, outerPt) + if (isBlackbox(symbol)) typer.instantiatePossiblyExpectingUnit(delayed, mode, outerPt) else { forced += delayed typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), outerPt, keepNothings = false) diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 6a4df415ae..dceb0a47d8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -162,7 +162,7 @@ trait NamesDefaults { self: Analyzer => // never used for constructor calls, they always have a stable qualifier def blockWithQualifier(qual: Tree, selected: Name) = { - val sym = blockTyper.context.owner.newValue(unit.freshTermName("qual$"), newFlags = ARTIFACT) setInfo uncheckedBounds(qual.tpe) setPos (qual.pos.makeTransparent) + val sym = blockTyper.context.owner.newValue(unit.freshTermName(nme.QUAL_PREFIX), newFlags = ARTIFACT) setInfo uncheckedBounds(qual.tpe) setPos (qual.pos.makeTransparent) blockTyper.context.scope enter sym val vd = atPos(sym.pos)(ValDef(sym, qual) setType NoType) // it stays in Vegas: SI-5720, SI-5727 @@ -292,7 +292,7 @@ trait NamesDefaults { self: Analyzer => arg.tpe } ).widen // have to widen or types inferred from literal defaults will be singletons - val s = context.owner.newValue(unit.freshTermName(), arg.pos, newFlags = ARTIFACT) setInfo { + val s = context.owner.newValue(unit.freshTermName(nme.NAMEDARG_PREFIX), arg.pos, newFlags = ARTIFACT) setInfo { val tp = if (byName) functionType(Nil, argTpe) else argTpe uncheckedBounds(tp) } diff --git a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala index 57f27a05fd..1a6d2f0011 100644 --- a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala +++ b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala @@ -85,6 +85,7 @@ trait StdAttachments { tree match { // see the comment to `isMacroExpansionSuppressed` to learn why we need // a special traversal strategy here + case Block(_, expr) => unsuppressMacroExpansion(expr) case Apply(fn, _) => unsuppressMacroExpansion(fn) case TypeApply(fn, _) => unsuppressMacroExpansion(fn) case _ => // do nothing @@ -101,6 +102,8 @@ trait StdAttachments { // we have to account for the fact that during typechecking an expandee might become wrapped, // i.e. surrounded by an inferred implicit argument application or by an inferred type argument application. // in that case the expandee itself will no longer be suppressed and we need to look at the core + // upd. we also need to allow for blocks, because that's what names and defaults are often desugared to + case Block(_, expr) => isMacroExpansionSuppressed(expr) case Apply(fn, _) => isMacroExpansionSuppressed(fn) case TypeApply(fn, _) => isMacroExpansionSuppressed(fn) case _ => false diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 4e3e00b66a..ca960d7b11 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -525,7 +525,6 @@ trait TypeDiagnostics { val unused = p.unusedTerms unused foreach { defn: DefTree => val sym = defn.symbol - val isDefaultGetter = sym.name containsName nme.DEFAULT_GETTER_STRING val pos = ( if (defn.pos.isDefined) defn.pos else if (sym.pos.isDefined) sym.pos @@ -536,7 +535,7 @@ trait TypeDiagnostics { ) val why = if (sym.isPrivate) "private" else "local" val what = ( - if (isDefaultGetter) "default argument" + if (sym.isDefaultGetter) "default argument" else if (sym.isConstructor) "constructor" else if (sym.isVar || sym.isGetter && sym.accessed.isVar) "var" else if (sym.isVal || sym.isGetter && sym.accessed.isVal) "val" diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ea8ad0bf42..ba4b3499e5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -56,6 +56,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } sealed abstract class SilentResult[+T] { + def isEmpty: Boolean + def nonEmpty = !isEmpty + @inline final def fold[U](none: => U)(f: T => U): U = this match { case SilentResultValue(value) => f(value) case _ => none @@ -74,6 +77,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } } class SilentTypeError private(val errors: List[AbsTypeError]) extends SilentResult[Nothing] { + override def isEmpty = true def err: AbsTypeError = errors.head def reportableErrors = errors match { case (e1: AmbiguousImplicitTypeError) +: _ => @@ -87,7 +91,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def unapply(error: SilentTypeError): Option[AbsTypeError] = error.errors.headOption } - case class SilentResultValue[+T](value: T) extends SilentResult[T] { } + case class SilentResultValue[+T](value: T) extends SilentResult[T] { override def isEmpty = false } def newTyper(context: Context): Typer = new NormalTyper(context) @@ -1157,7 +1161,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case mt: MethodType if mode.typingExprNotFunNotLhs && mt.isImplicit => // (4.1) adaptToImplicitMethod(mt) - case mt: MethodType if mode.typingExprNotFunNotLhs && !hasUndetsInMonoMode && !treeInfo.isMacroApplicationOrBlock(tree) => + case mt: MethodType if mode.typingExprNotFunNotLhs && !hasUndetsInMonoMode => instantiateToMethodType(mt) case _ => vanillaAdapt(tree) @@ -3268,12 +3272,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper */ def tryNamesDefaults: Tree = { val lencmp = compareLengths(args, formals) - - def checkNotMacro() = { - if (treeInfo.isMacroApplication(fun)) - tryTupleApply orElse duplErrorTree(NamedAndDefaultArgumentsNotSupportedForMacros(tree, fun)) - } - if (mt.isErroneous) duplErrTree else if (mode.inPatternMode) { // #2064 @@ -3292,18 +3290,15 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper else if (allArgsArePositional(argPos) && !isNamedApplyBlock(fun)) { // if there's no re-ordering, and fun is not transformed, no need to transform // more than an optimization, e.g. important in "synchronized { x = update-x }" - checkNotMacro() doTypedApply(tree, fun, namelessArgs, mode, pt) } else { - checkNotMacro() - transformNamedApplication(Typer.this, mode, pt)( - treeCopy.Apply(tree, fun, namelessArgs), argPos) + unsuppressMacroExpansion(transformNamedApplication(Typer.this, mode, pt)( + treeCopy.Apply(tree, suppressMacroExpansion(fun), namelessArgs), argPos)) } } else { // defaults are needed. they are added to the argument list in named style as // calls to the default getters. Example: // foo[Int](a)() ==> foo[Int](a)(b = foo$qual.foo$default$2[Int](a)) - checkNotMacro() // SI-8111 transformNamedApplication eagerly shuffles around the application to preserve // evaluation order. During this process, it calls `changeOwner` on symbols that @@ -3326,7 +3321,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper symsOwnedByContextOwner foreach (_.owner = context.owner) } - val fun1 = transformNamedApplication(Typer.this, mode, pt)(fun, x => x) + val fun1 = transformNamedApplication(Typer.this, mode, pt)(suppressMacroExpansion(fun), x => x) if (fun1.isErroneous) duplErrTree else { assert(isNamedApplyBlock(fun1), fun1) @@ -3352,7 +3347,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // useful when a default doesn't match parameter type, e.g. def f[T](x:T="a"); f[Int]() val note = "Error occurred in an application involving default arguments." if (!(context.diagnostic contains note)) context.diagnostic = note :: context.diagnostic - doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt) + unsuppressMacroExpansion(doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt)) } else { rollbackNamesDefaultsOwnerChanges() tryTupleApply orElse duplErrorTree(NotEnoughArgsError(tree, fun, missing)) diff --git a/src/compiler/scala/tools/reflect/FormatInterpolator.scala b/src/compiler/scala/tools/reflect/FormatInterpolator.scala index d5e674ebae..0258002850 100644 --- a/src/compiler/scala/tools/reflect/FormatInterpolator.scala +++ b/src/compiler/scala/tools/reflect/FormatInterpolator.scala @@ -81,7 +81,56 @@ abstract class FormatInterpolator { case Literal(Constant(x: String)) => x case _ => throw new IllegalArgumentException("internal error: argument parts must be a list of string literals") } - val s = StringContext.treatEscapes(s0) + def escapeHatch: PartialFunction[Throwable, String] = { + // trailing backslash, octal escape, or other + case e: StringContext.InvalidEscapeException => + def errPoint = part.pos withPoint (part.pos.point + e.index) + def octalOf(c: Char) = Character.digit(c, 8) + def alt = { + def altOf(i: Int) = i match { + case '\b' => "\\b" + case '\t' => "\\t" + case '\n' => "\\n" + case '\f' => "\\f" + case '\r' => "\\r" + case '\"' => "\\u0022" // $" in future + case '\'' => "'" + case '\\' => """\\""" + case x => "\\u%04x" format x + } + val suggest = { + val r = "([0-7]{1,3}).*".r + (s0 drop e.index + 1) match { + case r(n) => altOf { (0 /: n) { case (a, o) => (8 * a) + (o - '0') } } + case _ => "" + } + } + val txt = + if ("" == suggest) "" + else s", use $suggest instead" + txt + } + def badOctal = { + def msg(what: String) = s"Octal escape literals are $what$alt." + if (settings.future) { + c.error(errPoint, msg("unsupported")) + s0 + } else { + c.enclosingUnit.deprecationWarning(errPoint, msg("deprecated")) + try StringContext.treatEscapes(s0) catch escapeHatch + } + } + if (e.index == s0.length - 1) { + c.error(errPoint, """Trailing '\' escapes nothing.""") + s0 + } else if (octalOf(s0(e.index + 1)) >= 0) { + badOctal + } else { + c.error(errPoint, e.getMessage) + s0 + } + } + val s = try StringContext.processEscapes(s0) catch escapeHatch val ms = fpat findAllMatchIn s def errorLeading(op: Conversion) = op.errorAt(Spec, s"conversions must follow a splice; ${Conversion.literalHelp}") diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 3a40b44c74..3b12086cc7 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -64,7 +64,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => lastSeenContext = null } - def verify(expr: Tree): Unit = { + def verify(expr: Tree): Tree = { // Previously toolboxes used to typecheck their inputs before compiling. // Actually, the initial demo by Martin first typechecked the reified tree, // then ran it, which typechecked it again, and only then launched the @@ -86,14 +86,8 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => |if you have troubles tracking free type variables, consider using -Xlog-free-types """.stripMargin.trim) } - } - - def wrapIntoTerm(tree: Tree): Tree = - if (!tree.isTerm) Block(List(tree), Literal(Constant(()))) else tree - def unwrapFromTerm(tree: Tree): Tree = tree match { - case Block(List(tree), Literal(Constant(()))) => tree - case tree => tree + expr } def extractFreeTerms(expr0: Tree, wrapFreeTermRefs: Boolean): (Tree, scala.collection.mutable.LinkedHashMap[FreeTermSymbol, TermName]) = { @@ -122,59 +116,58 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => (expr, freeTermNames) } - def transformDuringTyper(expr0: Tree, withImplicitViewsDisabled: Boolean, withMacrosDisabled: Boolean)(transform: (analyzer.Typer, Tree) => Tree): Tree = { - verify(expr0) - - // need to wrap the expr, because otherwise you won't be able to typecheck macros against something that contains free vars - val exprAndFreeTerms = extractFreeTerms(expr0, wrapFreeTermRefs = false) - var expr = exprAndFreeTerms._1 - val freeTerms = exprAndFreeTerms._2 - val dummies = freeTerms.map{ case (freeTerm, name) => ValDef(NoMods, name, TypeTree(freeTerm.info), Select(Ident(PredefModule), newTermName("$qmark$qmark$qmark"))) }.toList - expr = Block(dummies, wrapIntoTerm(expr)) - - // [Eugene] how can we implement that? - // !!! Why is this is in the empty package? If it's only to make - // it inaccessible then please put it somewhere designed for that - // rather than polluting the empty package with synthetics. - val ownerClass = rootMirror.EmptyPackageClass.newClassSymbol(newTypeName("<expression-owner>")) - build.setInfo(ownerClass, ClassInfoType(List(ObjectTpe), newScope, ownerClass)) - val owner = ownerClass.newLocalDummy(expr.pos) - val currentTyper = analyzer.newTyper(analyzer.rootContext(NoCompilationUnit, EmptyTree).make(expr, owner)) - val wrapper1 = if (!withImplicitViewsDisabled) (currentTyper.context.withImplicitsEnabled[Tree] _) else (currentTyper.context.withImplicitsDisabled[Tree] _) - val wrapper2 = if (!withMacrosDisabled) (currentTyper.context.withMacrosEnabled[Tree] _) else (currentTyper.context.withMacrosDisabled[Tree] _) - def wrapper (tree: => Tree) = wrapper1(wrapper2(tree)) - - val run = new Run - run.symSource(ownerClass) = NoAbstractFile // need to set file to something different from null, so that currentRun.defines works - phase = run.typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled - currentTyper.context.setReportErrors() // need to manually set context mode, otherwise typer.silent will throw exceptions - reporter.reset() - - val expr1 = wrapper(transform(currentTyper, expr)) - var (dummies1, unwrapped) = expr1 match { - case Block(dummies, unwrapped) => ((dummies, unwrapped)) - case unwrapped => ((Nil, unwrapped)) - } - val invertedIndex = freeTerms map (_.swap) - // todo. also fixup singleton types - unwrapped = new Transformer { - override def transform(tree: Tree): Tree = - tree match { - case Ident(name: TermName) if invertedIndex contains name => - Ident(invertedIndex(name)) setType tree.tpe - case _ => - super.transform(tree) - } - }.transform(unwrapped) - new TreeTypeSubstituter(dummies1 map (_.symbol), dummies1 map (dummy => SingleType(NoPrefix, invertedIndex(dummy.symbol.name.toTermName)))).traverse(unwrapped) - unwrapped = if (expr0.isTerm) unwrapped else unwrapFromTerm(unwrapped) - unwrapped + def transformDuringTyper(expr: Tree, mode: scala.reflect.internal.Mode, withImplicitViewsDisabled: Boolean, withMacrosDisabled: Boolean)(transform: (analyzer.Typer, Tree) => Tree): Tree = { + def withWrapping(tree: Tree)(op: Tree => Tree) = if (mode == TERMmode) wrappingIntoTerm(tree)(op) else op(tree) + withWrapping(verify(expr))(expr1 => { + // need to extract free terms, because otherwise you won't be able to typecheck macros against something that contains them + val exprAndFreeTerms = extractFreeTerms(expr1, wrapFreeTermRefs = false) + var expr2 = exprAndFreeTerms._1 + val freeTerms = exprAndFreeTerms._2 + val dummies = freeTerms.map{ case (freeTerm, name) => ValDef(NoMods, name, TypeTree(freeTerm.info), Select(Ident(PredefModule), newTermName("$qmark$qmark$qmark"))) }.toList + expr2 = Block(dummies, expr2) + + // !!! Why is this is in the empty package? If it's only to make + // it inaccessible then please put it somewhere designed for that + // rather than polluting the empty package with synthetics. + // [Eugene] how can we implement that? + val ownerClass = rootMirror.EmptyPackageClass.newClassSymbol(newTypeName("<expression-owner>")) + build.setInfo(ownerClass, ClassInfoType(List(ObjectTpe), newScope, ownerClass)) + val owner = ownerClass.newLocalDummy(expr2.pos) + val currentTyper = analyzer.newTyper(analyzer.rootContext(NoCompilationUnit, EmptyTree).make(expr2, owner)) + val withImplicitFlag = if (!withImplicitViewsDisabled) (currentTyper.context.withImplicitsEnabled[Tree] _) else (currentTyper.context.withImplicitsDisabled[Tree] _) + val withMacroFlag = if (!withMacrosDisabled) (currentTyper.context.withMacrosEnabled[Tree] _) else (currentTyper.context.withMacrosDisabled[Tree] _) + def withContext (tree: => Tree) = withImplicitFlag(withMacroFlag(tree)) + + val run = new Run + run.symSource(ownerClass) = NoAbstractFile // need to set file to something different from null, so that currentRun.defines works + phase = run.typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled + currentTyper.context.setReportErrors() // need to manually set context mode, otherwise typer.silent will throw exceptions + reporter.reset() + + val expr3 = withContext(transform(currentTyper, expr2)) + var (dummies1, result) = expr3 match { + case Block(dummies, result) => ((dummies, result)) + case result => ((Nil, result)) + } + val invertedIndex = freeTerms map (_.swap) + result = new Transformer { + override def transform(tree: Tree): Tree = + tree match { + case Ident(name: TermName) if invertedIndex contains name => + Ident(invertedIndex(name)) setType tree.tpe + case _ => + super.transform(tree) + } + }.transform(result) + new TreeTypeSubstituter(dummies1 map (_.symbol), dummies1 map (dummy => SingleType(NoPrefix, invertedIndex(dummy.symbol.name.toTermName)))).traverse(result) + result + }) } def typecheck(expr: Tree, pt: Type, mode: scala.reflect.internal.Mode, silent: Boolean, withImplicitViewsDisabled: Boolean, withMacrosDisabled: Boolean): Tree = - transformDuringTyper(expr, withImplicitViewsDisabled = withImplicitViewsDisabled, withMacrosDisabled = withMacrosDisabled)( + transformDuringTyper(expr, mode, withImplicitViewsDisabled = withImplicitViewsDisabled, withMacrosDisabled = withMacrosDisabled)( (currentTyper, expr) => { - trace("typing (implicit views = %s, macros = %s): ".format(!withImplicitViewsDisabled, !withMacrosDisabled))(showAttributed(expr, true, true, settings.Yshowsymkinds.value)) + trace("typing (implicit views = %s, macros = %s): ".format(!withImplicitViewsDisabled, !withMacrosDisabled))(showAttributed(expr, true, true, settings.Yshowsymowners.value, settings.Yshowsymkinds.value)) currentTyper.silent(_.typed(expr, mode, pt), reportAmbiguousErrors = false) match { case analyzer.SilentResultValue(result) => trace("success: ")(showAttributed(result, true, true, settings.Yshowsymkinds.value)) @@ -187,9 +180,9 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => }) def inferImplicit(tree: Tree, pt: Type, isView: Boolean, silent: Boolean, withMacrosDisabled: Boolean, pos: Position): Tree = - transformDuringTyper(tree, withImplicitViewsDisabled = false, withMacrosDisabled = withMacrosDisabled)( + transformDuringTyper(tree, TERMmode, withImplicitViewsDisabled = false, withMacrosDisabled = withMacrosDisabled)( (currentTyper, tree) => { - trace("inferring implicit %s (macros = %s): ".format(if (isView) "view" else "value", !withMacrosDisabled))(showAttributed(pt, true, true, settings.Yshowsymkinds.value)) + trace("inferring implicit %s (macros = %s): ".format(if (isView) "view" else "value", !withMacrosDisabled))(showAttributed(pt, true, true, settings.Yshowsymowners.value, settings.Yshowsymkinds.value)) analyzer.inferImplicit(tree, pt, isView, currentTyper.context, silent, withMacrosDisabled, pos, (pos, msg) => throw ToolBoxError(msg)) }) @@ -207,7 +200,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => } def compile(expr0: Tree): () => Any = { - val expr = wrapIntoTerm(expr0) + val expr = build.SyntacticBlock(expr0 :: Nil) val freeTerms = expr.freeTerms // need to calculate them here, because later on they will be erased val thunks = freeTerms map (fte => () => fte.value) // need to be lazy in order not to distort evaluation order @@ -247,10 +240,10 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => List(), List(methdef), NoPosition)) - trace("wrapped: ")(showAttributed(moduledef, true, true, settings.Yshowsymkinds.value)) + trace("wrapped: ")(showAttributed(moduledef, true, true, settings.Yshowsymowners.value, settings.Yshowsymkinds.value)) val cleanedUp = resetAttrs(moduledef) - trace("cleaned up: ")(showAttributed(cleanedUp, true, true, settings.Yshowsymkinds.value)) + trace("cleaned up: ")(showAttributed(cleanedUp, true, true, settings.Yshowsymowners.value, settings.Yshowsymkinds.value)) cleanedUp.asInstanceOf[ModuleDef] } @@ -298,19 +291,22 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => tree } - def showAttributed(artifact: Any, printTypes: Boolean = true, printIds: Boolean = true, printKinds: Boolean = false): String = { + def showAttributed(artifact: Any, printTypes: Boolean = true, printIds: Boolean = true, printOwners: Boolean = false, printKinds: Boolean = false): String = { val saved1 = settings.printtypes.value val saved2 = settings.uniqid.value - val saved3 = settings.Yshowsymkinds.value + val saved3 = settings.Yshowsymowners.value + val saved4 = settings.Yshowsymkinds.value try { settings.printtypes.value = printTypes settings.uniqid.value = printIds + settings.Yshowsymowners.value = printOwners settings.Yshowsymkinds.value = printKinds artifact.toString } finally { settings.printtypes.value = saved1 settings.uniqid.value = saved2 - settings.Yshowsymkinds.value = saved3 + settings.Yshowsymowners.value = saved3 + settings.Yshowsymkinds.value = saved4 } } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index 9f6807fe17..301e7051df 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -14,6 +14,7 @@ import scala.util.Try */ trait Parsers { self: Quasiquotes => import global.{Try => _, _} + import build.implodePatDefs abstract class Parser extends { val global: self.global.type = self.global @@ -61,12 +62,10 @@ trait Parsers { self: Quasiquotes => override implicit def fresh: FreshNameCreator = parser.fresh // q"(..$xs)" - override def makeTupleTerm(trees: List[Tree]): Tree = - Apply(Ident(nme.QUASIQUOTE_TUPLE), trees) + override def makeTupleTerm(trees: List[Tree]): Tree = TuplePlaceholder(trees) // tq"(..$xs)" - override def makeTupleType(trees: List[Tree]): Tree = - AppliedTypeTree(Ident(tpnme.QUASIQUOTE_TUPLE), trees) + override def makeTupleType(trees: List[Tree]): Tree = TupleTypePlaceholder(trees) // q"{ $x }" override def makeBlock(stats: List[Tree]): Tree = stats match { @@ -75,30 +74,32 @@ trait Parsers { self: Quasiquotes => } // tq"$a => $b" - override def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = - AppliedTypeTree(Ident(tpnme.QUASIQUOTE_FUNCTION), argtpes :+ restpe) + override def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = FunctionTypePlaceholder(argtpes, restpe) + + // make q"val (x: T) = rhs" be equivalent to q"val x: T = rhs" for sake of bug compatibility (SI-8211) + override def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree) = pat match { + case TuplePlaceholder(inParensPat :: Nil) => super.makePatDef(mods, inParensPat, rhs) + case _ => super.makePatDef(mods, pat, rhs) + } } import treeBuilder.{global => _, unit => _, _} - def quasiquoteParam(name: Name, flags: FlagSet = NoFlags) = - ValDef(Modifiers(flags), name.toTermName, Ident(tpnme.QUASIQUOTE_PARAM), EmptyTree) - // q"def foo($x)" override def param(owner: Name, implicitmod: Int, caseParam: Boolean): ValDef = if (isHole && lookingAhead { in.token == COMMA || in.token == RPAREN }) { - quasiquoteParam(ident(), implicitmod) + ParamPlaceholder(implicitmod, ident()) } else super.param(owner, implicitmod, caseParam) // q"($x) => ..." && q"class X { selfie => } override def convertToParam(tree: Tree): ValDef = tree match { - case Ident(name) if isHole(name) => quasiquoteParam(name) + case Ident(name) if isHole(name) => ParamPlaceholder(NoFlags, name) case _ => super.convertToParam(tree) } // q"foo match { case $x }" override def caseClause(): CaseDef = if (isHole && lookingAhead { in.token == CASE || in.token == RBRACE || in.token == SEMI }) { - val c = makeCaseDef(Apply(Ident(nme.QUASIQUOTE_CASE), List(Ident(ident()))), EmptyTree, EmptyTree) + val c = CasePlaceholder(ident()) while (in.token == SEMI) in.nextToken() c } else @@ -132,7 +133,7 @@ trait Parsers { self: Quasiquotes => in.nextToken() annot :: readAnnots(annot) case _ if isHole && lookingAhead { isAnnotation || isModifier || isDefIntro || isIdent || isStatSep || in.token == LPAREN } => - val ann = Apply(Select(New(Ident(tpnme.QUASIQUOTE_MODS)), nme.CONSTRUCTOR), List(Literal(Constant(in.name.toString)))) + val ann = ModsPlaceholder(in.name) in.nextToken() ann :: readAnnots(annot) case _ => @@ -141,13 +142,13 @@ trait Parsers { self: Quasiquotes => override def refineStat(): List[Tree] = if (isHole && !isDclIntro) { - val result = ValDef(NoMods, in.name, Ident(tpnme.QUASIQUOTE_REFINE_STAT), EmptyTree) :: Nil + val result = RefineStatPlaceholder(in.name) :: Nil in.nextToken() result } else super.refineStat() override def ensureEarlyDef(tree: Tree) = tree match { - case Ident(name: TermName) if isHole(name) => ValDef(NoMods | Flag.PRESUPER, name, Ident(tpnme.QUASIQUOTE_EARLY_DEF), EmptyTree) + case Ident(name: TermName) if isHole(name) => EarlyDefPlaceholder(name) case _ => super.ensureEarlyDef(tree) } @@ -158,14 +159,14 @@ trait Parsers { self: Quasiquotes => override def topStat = super.topStat.orElse { case _ if isHole => - val stats = ValDef(NoMods, in.name, Ident(tpnme.QUASIQUOTE_PACKAGE_STAT), EmptyTree) :: Nil + val stats = PackageStatPlaceholder(in.name) :: Nil in.nextToken() stats } override def enumerator(isFirst: Boolean, allowNestedIf: Boolean = true) = if (isHole && lookingAhead { in.token == EOF || in.token == RPAREN || isStatSep }) { - val res = build.SyntacticValFrom(Bind(in.name, Ident(nme.WILDCARD)), Ident(nme.QUASIQUOTE_FOR_ENUM)) :: Nil + val res = ForEnumPlaceholder(in.name) :: Nil in.nextToken() res } else super.enumerator(isFirst, allowNestedIf) @@ -182,7 +183,7 @@ trait Parsers { self: Quasiquotes => } object TermParser extends Parser { - def entryPoint = parser => Q(gen.mkTreeOrBlock(parser.templateOrTopStatSeq())) + def entryPoint = parser => Q(implodePatDefs(gen.mkTreeOrBlock(parser.templateOrTopStatSeq()))) } object TypeParser extends Parser { @@ -195,7 +196,7 @@ trait Parsers { self: Quasiquotes => } object CaseParser extends Parser { - def entryPoint = _.caseClause() + def entryPoint = parser => implodePatDefs(parser.caseClause()) } object PatternParser extends Parser { @@ -209,7 +210,7 @@ trait Parsers { self: Quasiquotes => def entryPoint = { parser => val enums = parser.enumerator(isFirst = false, allowNestedIf = false) assert(enums.length == 1) - enums.head + implodePatDefs(enums.head) } } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala index 29df0ae670..e7730f878f 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala @@ -100,6 +100,8 @@ trait Placeholders { self: Quasiquotes => } object ModsPlaceholder extends HolePlaceholder { + def apply(name: Name) = + Apply(Select(New(Ident(tpnme.QUASIQUOTE_MODS)), nme.CONSTRUCTOR), List(Literal(Constant(name.toString)))) def matching = { case Apply(Select(New(Ident(tpnme.QUASIQUOTE_MODS)), nme.CONSTRUCTOR), List(Literal(Constant(s: String)))) => TermName(s) } @@ -112,12 +114,16 @@ trait Placeholders { self: Quasiquotes => } object ParamPlaceholder extends HolePlaceholder { + def apply(flags: FlagSet, name: Name) = + ValDef(Modifiers(flags), nme.QUASIQUOTE_PARAM, Ident(name), EmptyTree) def matching = { - case ValDef(_, name, Ident(tpnme.QUASIQUOTE_PARAM), EmptyTree) => name + case ValDef(_, nme.QUASIQUOTE_PARAM, Ident(name), EmptyTree) => name } } object TuplePlaceholder { + def apply(args: List[Tree]) = + Apply(Ident(nme.QUASIQUOTE_TUPLE), args) def unapply(tree: Tree): Option[List[Tree]] = tree match { case Apply(Ident(nme.QUASIQUOTE_TUPLE), args) => Some(args) case _ => None @@ -125,6 +131,8 @@ trait Placeholders { self: Quasiquotes => } object TupleTypePlaceholder { + def apply(args: List[Tree]) = + AppliedTypeTree(Ident(tpnme.QUASIQUOTE_TUPLE), args) def unapply(tree: Tree): Option[List[Tree]] = tree match { case AppliedTypeTree(Ident(tpnme.QUASIQUOTE_TUPLE), args) => Some(args) case _ => None @@ -132,6 +140,8 @@ trait Placeholders { self: Quasiquotes => } object FunctionTypePlaceholder { + def apply(args: List[Tree], res: Tree) = + AppliedTypeTree(Ident(tpnme.QUASIQUOTE_FUNCTION), args :+ res) def unapply(tree: Tree): Option[(List[Tree], Tree)] = tree match { case AppliedTypeTree(Ident(tpnme.QUASIQUOTE_FUNCTION), args :+ res) => Some((args, res)) case _ => None @@ -146,6 +156,8 @@ trait Placeholders { self: Quasiquotes => } object CasePlaceholder { + def apply(name: Name) = + CaseDef(Apply(Ident(nme.QUASIQUOTE_CASE), Ident(name) :: Nil), EmptyTree, EmptyTree) def unapply(tree: Tree): Option[Hole] = tree match { case CaseDef(Apply(Ident(nme.QUASIQUOTE_CASE), List(Placeholder(hole))), EmptyTree, EmptyTree) => Some(hole) case _ => None @@ -153,27 +165,35 @@ trait Placeholders { self: Quasiquotes => } object RefineStatPlaceholder { + def apply(name: Name) = + ValDef(NoMods, nme.QUASIQUOTE_REFINE_STAT, Ident(name), EmptyTree) def unapply(tree: Tree): Option[Hole] = tree match { - case ValDef(_, Placeholder(hole), Ident(tpnme.QUASIQUOTE_REFINE_STAT), _) => Some(hole) + case ValDef(_, nme.QUASIQUOTE_REFINE_STAT, Ident(Placeholder(hole)), _) => Some(hole) case _ => None } } object EarlyDefPlaceholder { + def apply(name: Name) = + ValDef(Modifiers(Flag.PRESUPER), nme.QUASIQUOTE_EARLY_DEF, Ident(name), EmptyTree) def unapply(tree: Tree): Option[Hole] = tree match { - case ValDef(_, Placeholder(hole), Ident(tpnme.QUASIQUOTE_EARLY_DEF), _) => Some(hole) + case ValDef(_, nme.QUASIQUOTE_EARLY_DEF, Ident(Placeholder(hole)), _) => Some(hole) case _ => None } } object PackageStatPlaceholder { + def apply(name: Name) = + ValDef(NoMods, nme.QUASIQUOTE_PACKAGE_STAT, Ident(name), EmptyTree) def unapply(tree: Tree): Option[Hole] = tree match { - case ValDef(NoMods, Placeholder(hole), Ident(tpnme.QUASIQUOTE_PACKAGE_STAT), EmptyTree) => Some(hole) + case ValDef(NoMods, nme.QUASIQUOTE_PACKAGE_STAT, Ident(Placeholder(hole)), EmptyTree) => Some(hole) case _ => None } } object ForEnumPlaceholder { + def apply(name: Name) = + build.SyntacticValFrom(Bind(name, Ident(nme.WILDCARD)), Ident(nme.QUASIQUOTE_FOR_ENUM)) def unapply(tree: Tree): Option[Hole] = tree match { case build.SyntacticValFrom(Bind(Placeholder(hole), Ident(nme.WILDCARD)), Ident(nme.QUASIQUOTE_FOR_ENUM)) => Some(hole) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index af8b0be92c..339937adc3 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -194,8 +194,8 @@ trait Reifiers { self: Quasiquotes => reifyBuildCall(nme.SyntacticEmptyTypeTree) case SyntacticImport(expr, selectors) => reifyBuildCall(nme.SyntacticImport, expr, selectors) - case Q(Placeholder(Hole(tree, DotDot))) => - mirrorBuildCall(nme.SyntacticBlock, tree) + case Q(tree) if fillListHole.isDefinedAt(tree) => + mirrorBuildCall(nme.SyntacticBlock, fillListHole(tree)) case Q(other) => reifyTree(other) // Syntactic block always matches so we have to be careful @@ -311,11 +311,7 @@ trait Reifiers { self: Quasiquotes => */ def reifyMultiCardinalityList(xs: List[Any])(fill: PartialFunction[Any, Tree])(fallback: Any => Tree): Tree - /** Reifies arbitrary list filling ..$x and ...$y holeMap when they are put - * in the correct position. Fallbacks to regular reification for non-high cardinality - * elements. - */ - override def reifyList(xs: List[Any]): Tree = reifyMultiCardinalityList(xs) { + val fillListHole: PartialFunction[Any, Tree] = { case Placeholder(Hole(tree, DotDot)) => tree case CasePlaceholder(Hole(tree, DotDot)) => tree case RefineStatPlaceholder(h @ Hole(_, DotDot)) => reifyRefineStat(h) @@ -323,12 +319,23 @@ trait Reifiers { self: Quasiquotes => case PackageStatPlaceholder(h @ Hole(_, DotDot)) => reifyPackageStat(h) case ForEnumPlaceholder(Hole(tree, DotDot)) => tree case ParamPlaceholder(Hole(tree, DotDot)) => tree + case SyntacticPatDef(mods, pat, tpt, rhs) => + reifyBuildCall(nme.SyntacticPatDef, mods, pat, tpt, rhs) + case SyntacticValDef(mods, p @ Placeholder(h: ApplyHole), tpt, rhs) if h.tpe <:< treeType => + mirrorBuildCall(nme.SyntacticPatDef, reify(mods), h.tree, reify(tpt), reify(rhs)) + } + + val fillListOfListsHole: PartialFunction[Any, Tree] = { case List(ParamPlaceholder(Hole(tree, DotDotDot))) => tree case List(Placeholder(Hole(tree, DotDotDot))) => tree - } { - reify(_) } + /** Reifies arbitrary list filling ..$x and ...$y holeMap when they are put + * in the correct position. Fallbacks to regular reification for non-high cardinality + * elements. + */ + override def reifyList(xs: List[Any]): Tree = reifyMultiCardinalityList(xs)(fillListHole.orElse(fillListOfListsHole))(reify) + def reifyAnnotList(annots: List[Tree]): Tree = reifyMultiCardinalityList(annots) { case AnnotPlaceholder(h @ Hole(_, DotDot)) => reifyAnnotation(h) } { diff --git a/src/library/scala/StringContext.scala b/src/library/scala/StringContext.scala index 2d79452c5d..cd928a2b61 100644 --- a/src/library/scala/StringContext.scala +++ b/src/library/scala/StringContext.scala @@ -172,8 +172,8 @@ object StringContext { * @param str The offending string * @param idx The index of the offending backslash character in `str`. */ - class InvalidEscapeException(str: String, idx: Int) - extends IllegalArgumentException("invalid escape character at index "+idx+" in \""+str+"\"") + class InvalidEscapeException(str: String, @deprecatedName('idx) val index: Int) + extends IllegalArgumentException("invalid escape character at index "+index+" in \""+str+"\"") /** Expands standard Scala escape sequences in a string. * Escape sequences are: @@ -184,7 +184,11 @@ object StringContext { * @param str A string that may contain escape sequences * @return The string with all escape sequences expanded. */ - def treatEscapes(str: String): String = { + def treatEscapes(str: String): String = treatEscapes0(str, strict = false) + + def processEscapes(str: String): String = treatEscapes0(str, strict = true) + + private def treatEscapes0(str: String, strict: Boolean): String = { lazy val bldr = new java.lang.StringBuilder val len = str.length var start = 0 @@ -201,6 +205,7 @@ object StringContext { idx += 1 if (idx >= len) throw new InvalidEscapeException(str, cur) if ('0' <= str(idx) && str(idx) <= '7') { + if (strict) throw new InvalidEscapeException(str, cur) val leadch = str(idx) var oct = leadch - '0' idx += 1 diff --git a/src/library/scala/collection/immutable/HashMap.scala b/src/library/scala/collection/immutable/HashMap.scala index 8a24f721d7..3b3e65ea61 100644 --- a/src/library/scala/collection/immutable/HashMap.scala +++ b/src/library/scala/collection/immutable/HashMap.scala @@ -350,6 +350,8 @@ object HashMap extends ImmutableMapFactory[HashMap] with BitOperations.Int { Array.copy(elems, 0, elemsNew, 0, offset) Array.copy(elems, offset + 1, elemsNew, offset, elems.length - offset - 1) val sizeNew = size - sub.size + // if we have only one child, which is not a HashTrieSet but a self-contained set like + // HashSet1 or HashSetCollision1, return the child instead if (elemsNew.length == 1 && !elemsNew(0).isInstanceOf[HashTrieMap[_,_]]) elemsNew(0) else diff --git a/src/library/scala/collection/immutable/HashSet.scala b/src/library/scala/collection/immutable/HashSet.scala index 67e9d18da7..726937efd9 100644 --- a/src/library/scala/collection/immutable/HashSet.scala +++ b/src/library/scala/collection/immutable/HashSet.scala @@ -431,15 +431,12 @@ object HashSet extends ImmutableSetFactory[HashSet] { case 0 => // the empty set null - case size if size == ks.size => - // We do this check first since even if the result is of size 1 since - // it is preferable to return the existing set for better structural sharing - // we can not rely on ks.- returning the same instance if we subtract an element that is not in it - // so we need to do the size check - this case 1 => // create a new HashSet1 with the hash we already know new HashSet1(ks1.head, hash) + case size if size == ks.size => + // Should only have HSC1 if size > 1 + this case _ => // create a new HashSetCollison with the hash we already know and the new keys new HashSetCollision1(hash, ks1) @@ -861,6 +858,8 @@ object HashSet extends ImmutableSetFactory[HashSet] { new HashTrieSet(bitmapNew, elemsNew, sizeNew) } else null + } else if(elems.length == 1 && !subNew.isInstanceOf[HashTrieSet[_]]) { + subNew } else { val elemsNew = new Array[HashSet[A]](elems.length) Array.copy(elems, 0, elemsNew, 0, elems.length) diff --git a/src/library/scala/collection/parallel/mutable/ParFlatHashTable.scala b/src/library/scala/collection/parallel/mutable/ParFlatHashTable.scala index afc2d6e987..62165ae0d2 100644 --- a/src/library/scala/collection/parallel/mutable/ParFlatHashTable.scala +++ b/src/library/scala/collection/parallel/mutable/ParFlatHashTable.scala @@ -28,12 +28,12 @@ trait ParFlatHashTable[T] extends scala.collection.mutable.FlatHashTable[T] { extends IterableSplitter[T] with SizeMapUtils { import scala.collection.DebugUtils._ - private var traversed = 0 - private val itertable = table + private[this] var traversed = 0 + private[this] val itertable = table if (hasNext) scan() - private def scan() { + private[this] def scan() { while (itertable(idx) eq null) { idx += 1 } @@ -44,7 +44,7 @@ trait ParFlatHashTable[T] extends scala.collection.mutable.FlatHashTable[T] { def remaining = totalsize - traversed def hasNext = traversed < totalsize def next() = if (hasNext) { - val r = itertable(idx).asInstanceOf[T] + val r = entryToElem(itertable(idx)) traversed += 1 idx += 1 if (hasNext) scan() diff --git a/src/manual/scala/man1/scala.scala b/src/manual/scala/man1/scala.scala index 6b3be8b77f..92d9c59cca 100644 --- a/src/manual/scala/man1/scala.scala +++ b/src/manual/scala/man1/scala.scala @@ -215,7 +215,7 @@ object scala extends Command { "exec scala \"$0\" \"$@\"\n" + "!#\n" + "Console.println(\"Hello, world!\")\n" + - "argv.toList foreach Console.println"), + "args.toList foreach Console.println"), "Here is a complete Scala script for MS Windows: ", @@ -226,7 +226,7 @@ object scala extends Command { "goto :eof\n" + "::!#\n" + "Console.println(\"Hello, world!\")\n" + - "argv.toList foreach Console.println"), + "args.toList foreach Console.println"), "If you want to use the compilation cache to speed up multiple executions " + "of the script, then add " & Mono("-savecompiled") & " to the scala " + @@ -237,7 +237,7 @@ object scala extends Command { "exec scala -savecompiled \"$0\" \"$@\"\n" + "!#\n" + "Console.println(\"Hello, world!\")\n" + - "argv.toList foreach Console.println")) + "args.toList foreach Console.println")) val exitStatus = Section("EXIT STATUS", diff --git a/src/manual/scala/man1/scalac.scala b/src/manual/scala/man1/scalac.scala index 52e918595c..31d25d4801 100644 --- a/src/manual/scala/man1/scalac.scala +++ b/src/manual/scala/man1/scalac.scala @@ -309,6 +309,9 @@ object scalac extends Command { CmdOption("Xshow-phases"), "Print a synopsis of compiler phases."), Definition( + CmdOptionBound("Xsource:", Argument("version")), + "Treat compiler input as Scala source for the specified version, see SI-8126."), + Definition( CmdOption("Xsource-reader", Argument("classname")), "Specify a custom method for reading source files."), Definition( diff --git a/src/partest-extras/scala/tools/partest/ScriptTest.scala b/src/partest-extras/scala/tools/partest/ScriptTest.scala index 24a4121b54..3000d751e1 100644 --- a/src/partest-extras/scala/tools/partest/ScriptTest.scala +++ b/src/partest-extras/scala/tools/partest/ScriptTest.scala @@ -14,8 +14,9 @@ abstract class ScriptTest extends DirectTest { override def extraSettings = s"-usejavacp -Xscript $testmain" def scriptPath = testPath changeExtension "script" def code = scriptPath.toFile.slurp + def argv = Seq.empty[String] def show() = { compile() - ScalaClassLoader(getClass.getClassLoader).run(testmain, Seq.empty[String]) + ScalaClassLoader(getClass.getClassLoader).run(testmain, argv) } } diff --git a/src/reflect/scala/reflect/api/Internals.scala b/src/reflect/scala/reflect/api/Internals.scala index 2fd52d5cd8..37406385c8 100644 --- a/src/reflect/scala/reflect/api/Internals.scala +++ b/src/reflect/scala/reflect/api/Internals.scala @@ -698,6 +698,12 @@ trait Internals { self: Universe => def unapply(tree: Tree): Option[(Modifiers, TermName, Tree, Tree)] } + val SyntacticPatDef: SyntacticPatDefExtractor + + trait SyntacticPatDefExtractor { + def apply(mods: Modifiers, pat: Tree, tpt: Tree, rhs: Tree): List[ValDef] + } + val SyntacticAssign: SyntacticAssignExtractor trait SyntacticAssignExtractor { diff --git a/src/reflect/scala/reflect/api/Printers.scala b/src/reflect/scala/reflect/api/Printers.scala index 6f634c55fe..b262fdce68 100644 --- a/src/reflect/scala/reflect/api/Printers.scala +++ b/src/reflect/scala/reflect/api/Printers.scala @@ -142,6 +142,7 @@ trait Printers { self: Universe => def print(args: Any*) protected var printTypes = false protected var printIds = false + protected var printOwners = false protected var printKinds = false protected var printMirrors = false protected var printPositions = false @@ -149,6 +150,8 @@ trait Printers { self: Universe => def withoutTypes: this.type = { printTypes = false; this } def withIds: this.type = { printIds = true; this } def withoutIds: this.type = { printIds = false; this } + def withOwners: this.type = { printOwners = true; this } + def withoutOwners: this.type = { printOwners = false; this } def withKinds: this.type = { printKinds = true; this } def withoutKinds: this.type = { printKinds = false; this } def withMirrors: this.type = { printMirrors = true; this } @@ -169,12 +172,13 @@ trait Printers { self: Universe => } /** @group Printers */ - protected def render(what: Any, mkPrinter: PrintWriter => TreePrinter, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printKinds: BooleanFlag = None, printMirrors: BooleanFlag = None, printPositions: BooleanFlag = None): String = { + protected def render(what: Any, mkPrinter: PrintWriter => TreePrinter, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printOwners: BooleanFlag = None, printKinds: BooleanFlag = None, printMirrors: BooleanFlag = None, printPositions: BooleanFlag = None): String = { val buffer = new StringWriter() val writer = new PrintWriter(buffer) val printer = mkPrinter(writer) printTypes.value.map(printTypes => if (printTypes) printer.withTypes else printer.withoutTypes) printIds.value.map(printIds => if (printIds) printer.withIds else printer.withoutIds) + printOwners.value.map(printOwners => if (printOwners) printer.withOwners else printer.withoutOwners) printKinds.value.map(printKinds => if (printKinds) printer.withKinds else printer.withoutKinds) printMirrors.value.map(printMirrors => if (printMirrors) printer.withMirrors else printer.withoutMirrors) printPositions.value.map(printPositions => if (printPositions) printer.withPositions else printer.withoutPositions) @@ -193,8 +197,8 @@ trait Printers { self: Universe => * * @group Printers */ - def show(any: Any, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printKinds: BooleanFlag = None, printMirrors: BooleanFlag = None, printPositions: BooleanFlag = None): String = - render(any, newTreePrinter(_), printTypes, printIds, printKinds, printMirrors, printPositions) + def show(any: Any, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printOwners: BooleanFlag = None, printKinds: BooleanFlag = None, printMirrors: BooleanFlag = None, printPositions: BooleanFlag = None): String = + render(any, newTreePrinter(_), printTypes, printIds, printOwners, printKinds, printMirrors, printPositions) /** Hook to define what `show(...)` means. * @group Printers @@ -225,8 +229,8 @@ trait Printers { self: Universe => * * @group Printers */ - def showRaw(any: Any, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printKinds: BooleanFlag = None, printMirrors: BooleanFlag = None, printPositions: BooleanFlag = None): String = - render(any, newRawTreePrinter(_), printTypes, printIds, printKinds, printMirrors, printPositions) + def showRaw(any: Any, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printOwners: BooleanFlag = None, printKinds: BooleanFlag = None, printMirrors: BooleanFlag = None, printPositions: BooleanFlag = None): String = + render(any, newRawTreePrinter(_), printTypes, printIds, printOwners, printKinds, printMirrors, printPositions) /** Hook to define what `showRaw(...)` means. * @group Printers diff --git a/src/reflect/scala/reflect/api/StandardLiftables.scala b/src/reflect/scala/reflect/api/StandardLiftables.scala index 6fc00de3e5..af11de46ce 100644 --- a/src/reflect/scala/reflect/api/StandardLiftables.scala +++ b/src/reflect/scala/reflect/api/StandardLiftables.scala @@ -54,9 +54,6 @@ trait StandardLiftables { self: Universe => case right: Right[L, R] => lift(right) } - implicit def liftTuple1[T1](implicit liftT1: Liftable[T1]): Liftable[Tuple1[T1]] = Liftable { t => - SyntacticTuple(liftT1(t._1) :: Nil) - } implicit def liftTuple2[T1, T2](implicit liftT1: Liftable[T1], liftT2: Liftable[T2]): Liftable[Tuple2[T1, T2]] = Liftable { t => SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: Nil) } @@ -148,9 +145,6 @@ trait StandardLiftables { self: Universe => implicit def unliftType: Unliftable[Type] = Unliftable[Type] { case tt: TypeTree if tt.tpe != null => tt.tpe } implicit def unliftConstant: Unliftable[Constant] = Unliftable[Constant] { case Literal(const) => const } - implicit def unliftTuple1[T1](implicit UnliftT1: Unliftable[T1]): Unliftable[Tuple1[T1]] = Unliftable { - case SyntacticTuple(UnliftT1(v1) :: Nil) => Tuple1(v1) - } implicit def unliftTuple2[T1, T2](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2]): Unliftable[Tuple2[T1, T2]] = Unliftable { case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: Nil) => Tuple2(v1, v2) } diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala index 8ec4c98cd9..b1d76b6056 100644 --- a/src/reflect/scala/reflect/internal/Printers.scala +++ b/src/reflect/scala/reflect/internal/Printers.scala @@ -33,10 +33,10 @@ trait Printers extends api.Printers { self: SymbolTable => def qowner = quotedName(sym.owner.name.dropLocal, decoded) def qsymbol = quotedName(sym.nameString) - if (sym.name.toTermName == nme.ERROR) - s"<$qname: error>" - else if (sym == null || sym == NoSymbol) + if (sym == null || sym == NoSymbol) qname + else if (sym.isErroneous) + s"<$qname: error>" else if (sym.isMixinConstructor) s"/*$qowner*/$qsymbol" else @@ -65,6 +65,7 @@ trait Printers extends api.Printers { self: SymbolTable => printTypes = settings.printtypes.value printIds = settings.uniqid.value + printOwners = settings.Yshowsymowners.value printKinds = settings.Yshowsymkinds.value printMirrors = false // typically there's no point to print mirrors inside the compiler, as there is only one mirror there printPositions = settings.Xprintpos.value @@ -275,6 +276,7 @@ trait Printers extends api.Printers { self: SymbolTable => printValueParams print(" => ", body, ")") if (printIds && tree.symbol != null) print("#" + tree.symbol.id) + if (printOwners && tree.symbol != null) print("@" + tree.symbol.owner.id) } protected def printSuper(tree: Super, resultName: => String) = { @@ -1093,7 +1095,7 @@ trait Printers extends api.Printers { self: SymbolTable => case self.pendingSuperCall => print("pendingSuperCall") case tree: Tree => - val hasSymbolField = tree.hasSymbolField && tree.symbol != NoSymbol + def hasSymbolField = tree.hasSymbolField && tree.symbol != NoSymbol val isError = hasSymbolField && (tree.symbol.name string_== nme.ERROR) printProduct( tree, @@ -1139,6 +1141,7 @@ trait Printers extends api.Printers { self: SymbolTable => else if (sym.isStatic && (sym.isClass || sym.isModule)) print(sym.fullName) else print(sym.name) if (printIds) print("#", sym.id) + if (printOwners) print("@", sym.owner.id) if (printKinds) print("#", sym.abbreviatedKindString) if (printMirrors) print("%M", footnotes.put[scala.reflect.api.Mirror[_]](mirrorThatLoaded(sym))) case tag: TypeTag[_] => diff --git a/src/reflect/scala/reflect/internal/ReificationSupport.scala b/src/reflect/scala/reflect/internal/ReificationSupport.scala index 6a91109faf..087d4186be 100644 --- a/src/reflect/scala/reflect/internal/ReificationSupport.scala +++ b/src/reflect/scala/reflect/internal/ReificationSupport.scala @@ -226,9 +226,9 @@ trait ReificationSupport { self: SymbolTable => // recover constructor contents generated by gen.mkTemplate protected object UnCtor { def unapply(tree: Tree): Option[(Modifiers, List[List[ValDef]], List[Tree])] = tree match { - case DefDef(mods, nme.MIXIN_CONSTRUCTOR, _, _, _, Block(lvdefs, _)) => + case DefDef(mods, nme.MIXIN_CONSTRUCTOR, _, _, _, SyntacticBlock(lvdefs :+ _)) => Some((mods | Flag.TRAIT, Nil, lvdefs)) - case DefDef(mods, nme.CONSTRUCTOR, Nil, vparamss, _, Block(lvdefs :+ _, _)) => + case DefDef(mods, nme.CONSTRUCTOR, Nil, vparamss, _, SyntacticBlock(lvdefs :+ _ :+ _)) => Some((mods, vparamss, lvdefs)) case _ => None } @@ -383,7 +383,7 @@ trait ReificationSupport { self: SymbolTable => object SyntacticTuple extends SyntacticTupleExtractor { def apply(args: List[Tree]): Tree = { require(args.isEmpty || TupleClass(args.length).exists, s"Tuples with ${args.length} arity aren't supported") - gen.mkTuple(args, flattenUnary = false) + gen.mkTuple(args) } def unapply(tree: Tree): Option[List[Tree]] = tree match { @@ -393,6 +393,8 @@ trait ReificationSupport { self: SymbolTable => if sym == TupleClass(args.length).companionModule && (targs.isEmpty || targs.length == args.length) => Some(args) + case _ if tree.isTerm => + Some(tree :: Nil) case _ => None } @@ -401,7 +403,7 @@ trait ReificationSupport { self: SymbolTable => object SyntacticTupleType extends SyntacticTupleExtractor { def apply(args: List[Tree]): Tree = { require(args.isEmpty || TupleClass(args.length).exists, s"Tuples with ${args.length} arity aren't supported") - gen.mkTupleType(args, flattenUnary = false) + gen.mkTupleType(args) } def unapply(tree: Tree): Option[List[Tree]] = tree match { @@ -410,6 +412,8 @@ trait ReificationSupport { self: SymbolTable => case MaybeTypeTreeOriginal(AppliedTypeTree(TupleClassRef(sym), args)) if sym == TupleClass(args.length) => Some(args) + case _ if tree.isType => + Some(tree :: Nil) case _ => None } @@ -507,10 +511,9 @@ trait ReificationSupport { self: SymbolTable => } protected class SyntacticValDefBase(isMutable: Boolean) extends SyntacticValDefExtractor { - def apply(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree) = { - val mods1 = if (isMutable) mods | MUTABLE else mods - ValDef(mods1, name, tpt, rhs) - } + def modifiers(mods: Modifiers): Modifiers = if (isMutable) mods | MUTABLE else mods + + def apply(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree): ValDef = ValDef(modifiers(mods), name, tpt, rhs) def unapply(tree: Tree): Option[(Modifiers, TermName, Tree, Tree)] = tree match { case ValDef(mods, name, tpt, rhs) if mods.hasFlag(MUTABLE) == isMutable => @@ -580,25 +583,57 @@ trait ReificationSupport { self: SymbolTable => // match a sequence of desugared `val $pat = $value` protected object UnPatSeq { - def unapply(trees: List[Tree]): Option[List[(Tree, Tree)]] = trees match { - case Nil => Some(Nil) - // case q"$mods val ${_}: ${_} = ${MaybeUnchecked(value)} match { case $pat => (..$ids) }" :: tail - case ValDef(mods, _, _, Match(MaybeUnchecked(value), CaseDef(pat, EmptyTree, SyntacticTuple(ids)) :: Nil)) :: tail + def unapply(trees: List[Tree]): Option[List[(Tree, Tree)]] = { + val imploded = implodePatDefs(trees) + val patvalues = imploded.flatMap { + case SyntacticPatDef(_, pat, EmptyTree, rhs) => Some((pat, rhs)) + case ValDef(_, name, SyntacticEmptyTypeTree(), rhs) => Some((Bind(name, self.Ident(nme.WILDCARD)), rhs)) + case ValDef(_, name, tpt, rhs) => Some((Bind(name, Typed(self.Ident(nme.WILDCARD), tpt)), rhs)) + case _ => None + } + if (patvalues.length == imploded.length) Some(patvalues) else None + } + } + + // implode multiple-statement desugaring of pattern definitions + // into single-statement valdefs with nme.QUASIQUOTE_PAT_DEF name + object implodePatDefs extends Transformer { + override def transform(tree: Tree) = tree match { + case templ: Template => deriveTemplate(templ)(transformStats) + case block: Block => + val Block(init, last) = block + Block(transformStats(init), transform(last)).copyAttrs(block) + case ValDef(mods, name1, SyntacticEmptyTypeTree(), Match(MaybeTyped(MaybeUnchecked(value), tpt), CaseDef(pat, EmptyTree, Ident(name2)) :: Nil)) + if name1 == name2 => + ValDef(mods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), transform(value)) + case _ => + super.transform(tree) + } + def transformStats(trees: List[Tree]): List[Tree] = trees match { + case Nil => Nil + case ValDef(mods, _, SyntacticEmptyTypeTree(), Match(MaybeTyped(MaybeUnchecked(value), tpt), CaseDef(pat, EmptyTree, SyntacticTuple(ids)) :: Nil)) :: tail if mods.hasFlag(SYNTHETIC) && mods.hasFlag(ARTIFACT) => - tail.drop(ids.length) match { - case UnPatSeq(rest) => Some((pat, value) :: rest) - case _ => None + ids match { + case Nil => + ValDef(NoMods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), transform(value)) :: transformStats(tail) + case _ => + val mods = tail.take(1).head.asInstanceOf[ValDef].mods + ValDef(mods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), transform(value)) :: transformStats(tail.drop(ids.length)) } - // case q"${_} val $name1: ${_} = ${MaybeUnchecked(value)} match { case $pat => ${Ident(name2)} }" :: UnPatSeq(rest) - case ValDef(_, name1, _, Match(MaybeUnchecked(value), CaseDef(pat, EmptyTree, Ident(name2)) :: Nil)) :: UnPatSeq(rest) - if name1 == name2 => - Some((pat, value) :: rest) - // case q"${_} val $name: ${SyntacticEmptyTypeTree()} = $value" :: UnPatSeq(rest) => - case ValDef(_, name, SyntacticEmptyTypeTree(), value) :: UnPatSeq(rest) => - Some((Bind(name, self.Ident(nme.WILDCARD)), value) :: rest) - // case q"${_} val $name: $tpt = $value" :: UnPatSeq(rest) => - case ValDef(_, name, tpt, value) :: UnPatSeq(rest) => - Some((Bind(name, Typed(self.Ident(nme.WILDCARD), tpt)), value) :: rest) + case other :: tail => + transform(other) :: transformStats(tail) + } + def apply(tree: Tree) = transform(tree) + def apply(trees: List[Tree]) = transformStats(trees) + } + + object SyntacticPatDef extends SyntacticPatDefExtractor { + def apply(mods: Modifiers, pat: Tree, tpt: Tree, rhs: Tree): List[ValDef] = tpt match { + case SyntacticEmptyTypeTree() => gen.mkPatDef(mods, pat, rhs) + case _ => gen.mkPatDef(mods, Typed(pat, tpt), rhs) + } + def unapply(tree: Tree): Option[(Modifiers, Tree, Tree, Tree)] = tree match { + case ValDef(mods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), rhs) => Some((mods, pat, tpt, rhs)) case _ => None } } @@ -779,6 +814,13 @@ trait ReificationSupport { self: SymbolTable => } } + protected object MaybeTyped { + def unapply(tree: Tree): Some[(Tree, Tree)] = tree match { + case Typed(v, tpt) => Some((v, tpt)) + case v => Some((v, SyntacticEmptyTypeTree())) + } + } + protected def mkCases(cases: List[Tree]): List[CaseDef] = cases.map { case c: CaseDef => c case tree => throw new IllegalArgumentException("$tree is not valid representation of pattern match case") diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 1e133eced9..b95c83d6cb 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -253,12 +253,8 @@ trait StdNames { final val Quasiquote: NameType = "Quasiquote" // quasiquote-specific names - final val QUASIQUOTE_EARLY_DEF: NameType = "$quasiquote$early$def$" final val QUASIQUOTE_FUNCTION: NameType = "$quasiquote$function$" final val QUASIQUOTE_MODS: NameType = "$quasiquote$mods$" - final val QUASIQUOTE_PACKAGE_STAT: NameType = "$quasiquote$package$stat$" - final val QUASIQUOTE_PARAM: NameType = "$quasiquote$param$" - final val QUASIQUOTE_REFINE_STAT: NameType = "$quasiquote$refine$stat$" final val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$" // Annotation simple names, used in Namer @@ -312,6 +308,8 @@ trait StdNames { val WHILE_PREFIX = "while$" val FRESH_PREFIX = "fresh" val FRESH_SUFFIX = "macro$" // uses a keyword to avoid collisions with mangled names + val QUAL_PREFIX = "qual$" + val NAMEDARG_PREFIX = "x$" // Compiler internal names val ANYname: NameType = "<anyname>" @@ -331,12 +329,17 @@ trait StdNames { val REIFY_FREE_THIS_SUFFIX: NameType = "$this" val REIFY_FREE_VALUE_SUFFIX: NameType = "$value" val REIFY_SYMDEF_PREFIX: NameType = "symdef$" - val QUASIQUOTE_PREFIX: String = "qq$" - val QUASIQUOTE_NAME_PREFIX: String = "nn$" - val QUASIQUOTE_FILE: String = "<quasiquote>" - val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$" val QUASIQUOTE_CASE: NameType = "$quasiquote$case$" + val QUASIQUOTE_EARLY_DEF: NameType = "$quasiquote$early$def$" + val QUASIQUOTE_FILE: String = "<quasiquote>" val QUASIQUOTE_FOR_ENUM: NameType = "$quasiquote$for$enum$" + val QUASIQUOTE_NAME_PREFIX: String = "nn$" + val QUASIQUOTE_PACKAGE_STAT: NameType = "$quasiquote$package$stat$" + val QUASIQUOTE_PARAM: NameType = "$quasiquote$param$" + val QUASIQUOTE_PAT_DEF: NameType = "$quasiquote$pat$def$" + val QUASIQUOTE_PREFIX: String = "qq$" + val QUASIQUOTE_REFINE_STAT: NameType = "$quasiquote$refine$stat$" + val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$" val QUASIQUOTE_UNLIFT_HELPER: String = "$quasiquote$unlift$helper$" val MIXIN_CONSTRUCTOR: NameType = "$init$" val MODULE_INSTANCE_FIELD: NameType = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$" @@ -625,6 +628,7 @@ trait StdNames { val SyntacticNew: NameType = "SyntacticNew" val SyntacticObjectDef: NameType = "SyntacticObjectDef" val SyntacticPackageObjectDef: NameType = "SyntacticPackageObjectDef" + val SyntacticPatDef: NameType = "SyntacticPatDef" val SyntacticTraitDef: NameType = "SyntacticTraitDef" val SyntacticTry: NameType = "SyntacticTry" val SyntacticTuple: NameType = "SyntacticTuple" @@ -651,7 +655,6 @@ trait StdNames { val applyDynamicNamed: NameType = "applyDynamicNamed" val applyOrElse: NameType = "applyOrElse" val args : NameType = "args" - val argv : NameType = "argv" val arrayClass: NameType = "arrayClass" val array_apply : NameType = "array_apply" val array_clone : NameType = "array_clone" diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 5e81badfad..5fa4772d60 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -405,9 +405,14 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Create a new existential type skolem with this symbol its owner, * based on the given symbol and origin. */ - def newExistentialSkolem(basis: Symbol, origin: AnyRef): TypeSkolem = { - val skolem = newTypeSkolemSymbol(basis.name.toTypeName, origin, basis.pos, (basis.flags | EXISTENTIAL) & ~PARAM) - skolem setInfo (basis.info cloneInfo skolem) + def newExistentialSkolem(basis: Symbol, origin: AnyRef): TypeSkolem = + newExistentialSkolem(basis.name.toTypeName, basis.info, basis.flags, basis.pos, origin) + + /** Create a new existential type skolem with this symbol its owner, and the given other properties. + */ + def newExistentialSkolem(name: TypeName, info: Type, flags: Long, pos: Position, origin: AnyRef): TypeSkolem = { + val skolem = newTypeSkolemSymbol(name.toTypeName, origin, pos, (flags | EXISTENTIAL) & ~PARAM) + skolem setInfo (info cloneInfo skolem) } // don't test directly -- use isGADTSkolem @@ -577,6 +582,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isConstructor = false def isEarlyInitialized = false def isGetter = false + def isDefaultGetter = false def isLocalDummy = false def isMixinConstructor = false def isOverloaded = false @@ -2502,14 +2508,14 @@ trait Symbols extends api.Symbols { self: SymbolTable => * If !settings.debug translates expansions of operators back to operator symbol. * E.g. $eq => =. * If settings.uniqid, adds id. + * If settings.Yshowsymowners, adds owner's id * If settings.Yshowsymkinds, adds abbreviated symbol kind. */ def nameString: String = { val name_s = if (settings.debug.value) "" + unexpandedName else unexpandedName.dropLocal.decode - val id_s = if (settings.uniqid.value) "#" + id else "" val kind_s = if (settings.Yshowsymkinds.value) "#" + abbreviatedKindString else "" - name_s + id_s + kind_s + name_s + idString + kind_s } def fullNameString: String = { @@ -2523,7 +2529,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => } /** If settings.uniqid is set, the symbol's id, else "" */ - final def idString = if (settings.uniqid.value) "#"+id else "" + final def idString = { + val id_s = if (settings.uniqid.value) "#"+id else "" + val owner_s = if (settings.Yshowsymowners.value) "@"+owner.id else "" + id_s + owner_s + } /** String representation, including symbol's kind e.g., "class Foo", "method Bar". * If hasMeaninglessName is true, uses the owner's name to disambiguate identity. @@ -2675,6 +2685,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def isSetterParameter = isValueParameter && owner.isSetter override def isAccessor = this hasFlag ACCESSOR override def isGetter = isAccessor && !isSetter + override def isDefaultGetter = name containsName nme.DEFAULT_GETTER_STRING override def isSetter = isAccessor && nme.isSetterName(name) // todo: make independent of name, as this can be forged. override def isLocalDummy = nme.isLocalDummyName(name) override def isClassConstructor = name == nme.CONSTRUCTOR @@ -3459,6 +3470,21 @@ trait Symbols extends api.Symbols { self: SymbolTable => mapList(syms1)(_ substInfo (syms, syms1)) } + /** Derives a new list of symbols from the given list by mapping the given + * list of `syms` and `as` across the given function. + * Then fixes the info of all the new symbols + * by substituting the new symbols for the original symbols. + * + * @param syms the prototypical symbols + * @param as arguments to be passed to symFn together with symbols from syms (must be same length) + * @param symFn the function to create new symbols + * @return the new list of info-adjusted symbols + */ + def deriveSymbols2[A](syms: List[Symbol], as: List[A], symFn: (Symbol, A) => Symbol): List[Symbol] = { + val syms1 = map2(syms, as)(symFn) + mapList(syms1)(_ substInfo (syms, syms1)) + } + /** Derives a new Type by first deriving new symbols as in deriveSymbols, * then performing the same oldSyms => newSyms substitution on `tpe` as is * performed on the symbol infos in deriveSymbols. @@ -3472,6 +3498,22 @@ trait Symbols extends api.Symbols { self: SymbolTable => val syms1 = deriveSymbols(syms, symFn) tpe.substSym(syms, syms1) } + + /** Derives a new Type by first deriving new symbols as in deriveSymbols2, + * then performing the same oldSyms => newSyms substitution on `tpe` as is + * performed on the symbol infos in deriveSymbols. + * + * @param syms the prototypical symbols + * @param as arguments to be passed to symFn together with symbols from syms (must be same length) + * @param symFn the function to create new symbols based on `as` + * @param tpe the prototypical type + * @return the new symbol-subsituted type + */ + def deriveType2[A](syms: List[Symbol], as: List[A], symFn: (Symbol, A) => Symbol)(tpe: Type): Type = { + val syms1 = deriveSymbols2(syms, as, symFn) + tpe.substSym(syms, syms1) + } + /** Derives a new Type by instantiating the given list of symbols as * WildcardTypes. * diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index 7ed808a7fb..6011289baf 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -691,11 +691,11 @@ abstract class TreeGen { } /** Create tree for pattern definition <val pat0 = rhs> */ - def mkPatDef(pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[Tree] = + def mkPatDef(pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[ValDef] = mkPatDef(Modifiers(0), pat, rhs) /** Create tree for pattern definition <mods val pat0 = rhs> */ - def mkPatDef(mods: Modifiers, pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[Tree] = matchVarPattern(pat) match { + def mkPatDef(mods: Modifiers, pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[ValDef] = matchVarPattern(pat) match { case Some((name, tpt)) => List(atPos(pat.pos union rhs.pos) { ValDef(mods, name.toTermName, tpt, rhs) diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 8cad2497c1..0b42a8f9ac 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -281,6 +281,10 @@ abstract class TreeInfo { } } + def isDefaultGetter(tree: Tree) = { + tree.symbol != null && tree.symbol.isDefaultGetter + } + /** Is tree a self constructor call this(...)? I.e. a call to a constructor of the * same object? */ @@ -864,13 +868,8 @@ abstract class TreeInfo { case _ => false }) - def isMacroApplication(tree: Tree): Boolean = !tree.isDef && { - val sym = tree.symbol - sym != null && sym.isTermMacro && !sym.isErroneous - } - - def isMacroApplicationOrBlock(tree: Tree): Boolean = tree match { - case Block(_, expr) => isMacroApplicationOrBlock(expr) - case tree => isMacroApplication(tree) + def isMacroApplication(tree: Tree): Boolean = tree match { + case Block(_, expr) => isMacroApplication(expr) + case tree => !tree.isDef && tree.symbol != null && tree.symbol.isTermMacro && !tree.symbol.isErroneous } } diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 5b4b7fd2a4..7a6862a770 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -1683,6 +1683,15 @@ trait Trees extends api.Trees { def duplicateAndKeepPositions(tree: Tree) = new Duplicator(focusPositions = false) transform tree + // this is necessary to avoid crashes like https://github.com/scalamacros/paradise/issues/1 + // when someone tries to c.typecheck a naked MemberDef + def wrappingIntoTerm(tree: Tree)(op: Tree => Tree): Tree = { + op(build.SyntacticBlock(tree :: Nil)) match { + case build.SyntacticBlock(tree :: Nil) => tree + case tree => tree + } + } + // ------ copiers ------------------------------------------- def copyDefDef(tree: Tree)( diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 089684413d..3ede21d5e3 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -93,10 +93,11 @@ trait Types private final val traceTypeVars = sys.props contains "scalac.debug.tvar" private final val breakCycles = settings.breakCycles.value - /** In case anyone wants to turn off type parameter bounds being used + /** In case anyone wants to turn on type parameter bounds being used * to seed type constraints. */ private final val propagateParameterBoundsToTypeVars = sys.props contains "scalac.debug.prop-constraints" + private final val sharperSkolems = sys.props contains "scalac.experimental.sharper-skolems" protected val enableTypeVarExperimentals = settings.Xexperimental.value @@ -2574,56 +2575,58 @@ trait Types override def baseTypeSeq = underlying.baseTypeSeq map maybeRewrap override def isHigherKinded = false + // TODO: check invariant that all quantifiers have the same (existing) owner + private def quantifierOwner = quantified collectFirst { case q if q.owner.exists => q.owner } getOrElse NoSymbol + + // Is this existential of the form: T[Q1, ..., QN] forSome { type Q1 >: L1 <: U1, ..., QN >: LN <: UN} + private def isStraightApplication = (quantified corresponds underlying.typeArgs){ (q, a) => q.tpe =:= a } + /** [SI-6169, SI-8197 -- companion to SI-1786] * - * Approximation to improve the bounds of a Java-defined existential type, - * based on the bounds of the type parameters of the quantified type - * In Scala syntax, given a java-defined class C[T <: String], the existential type C[_] - * is improved to C[_ <: String] before skolemization, which captures (get it?) what Java does: - * enter the type paramers' bounds into the context when checking subtyping/type equality of existential types + * Approximation to improve the bounds of a Java-defined existential type, + * based on the bounds of the type parameters of the quantified type + * In Scala syntax, given a java-defined class C[T <: String], the existential type C[_] + * is improved to C[_ <: String] before skolemization, which captures (get it?) what Java does: + * enter the type paramers' bounds into the context when checking subtyping/type equality of existential types * - * (Also tried doing this once during class file parsing or when creating the existential type, - * but that causes cyclic errors because it happens too early.) + * Also tried doing this once during class file parsing or when creating the existential type, + * but that causes cyclic errors because it happens too early. + * + * NOTE: we're only modifying the skolems to avoid leaking the sharper bounds to `quantified` (SI-8283) * * TODO: figure out how to do this earlier without running into cycles, so this can subsume the fix for SI-1786 */ - private def sharpenQuantifierBounds(): Unit = { - /* Check that we're looking at rawToExistential's handiwork - * (`existentialAbstraction(eparams, typeRef(apply(pre), sym, eparams map (_.tpe)))`). - * We can't do this sharpening there because we'll run into cycles. - */ - def rawToExistentialCreatedMe = (quantified corresponds underlying.typeArgs){ (q, a) => q.tpe =:= a } - - if (underlying.typeSymbol.isJavaDefined && rawToExistentialCreatedMe) { - val tpars = underlying.typeSymbol.initialize.typeParams // TODO: is initialize needed? - debuglog(s"sharpen bounds: $this | ${underlying.typeArgs.map(_.typeSymbol)} <-- ${tpars.map(_.info)}") - - foreach2(quantified, tpars) { (quant, tparam) => - // TODO: check `tparam.info.substSym(tpars, quantified) <:< quant.info` instead (for some weird reason not working for test/t6169/ExistF) - // for now, crude approximation for the common case - if (quant.info.bounds.isEmptyBounds && !tparam.info.bounds.isEmptyBounds) { - // avoid creating cycles [pos/t2940] that consist of an existential quantifier's - // bounded by an existential type that unhygienically has that quantifier as its own quantifier - // (TODO: clone latter existential with fresh quantifiers -- not covering this case for now) - if ((existentialsInType(tparam.info) intersect quantified).isEmpty) - quant setInfo tparam.info.substSym(tpars, quantified) - } - } - } + override def skolemizeExistential(owner0: Symbol, origin: AnyRef) = { + val owner = owner0 orElse quantifierOwner - _sharpenQuantifierBounds = false - } - private[this] var _sharpenQuantifierBounds = true - - override def skolemizeExistential(owner: Symbol, origin: AnyRef) = { // do this here because it's quite close to what Java does: // when checking subtyping/type equality, enter constraints // derived from the existentially quantified type into the typing environment // (aka \Gamma, which tracks types for variables and constraints/kinds for types) // as a nice bonus, delaying this until we need it avoids cyclic errors - if (_sharpenQuantifierBounds) sharpenQuantifierBounds + def tpars = underlying.typeSymbol.initialize.typeParams + + def newSkolem(quant: Symbol) = owner.newExistentialSkolem(quant, origin) + def newSharpenedSkolem(quant: Symbol, tparam: Symbol): Symbol = { + def emptyBounds(sym: Symbol) = sym.info.bounds.isEmptyBounds + + // avoid creating cycles [pos/t2940] that consist of an existential quantifier's + // bounded by an existential type that unhygienically has that quantifier as its own quantifier + // (TODO: clone latter existential with fresh quantifiers -- not covering this case for now) + val canSharpen = ( + emptyBounds(quant) && !emptyBounds(tparam) + && (existentialsInType(tparam.info) intersect quantified).isEmpty + ) + + val skolemInfo = if (!canSharpen) quant.info else tparam.info.substSym(tpars, quantified) + + owner.newExistentialSkolem(quant.name.toTypeName, skolemInfo, quant.flags, quant.pos, origin) + } + + val canSharpenBounds = (underlying.typeSymbol.isJavaDefined || sharperSkolems) && isStraightApplication - deriveType(quantified, tparam => (owner orElse tparam.owner).newExistentialSkolem(tparam, origin))(underlying) + if (canSharpenBounds) deriveType2(quantified, tpars, newSharpenedSkolem)(underlying) + else deriveType(quantified, newSkolem)(underlying) } private def wildcardArgsString(qset: Set[Symbol], args: List[Type]): List[String] = args map { diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala index 816916787e..048fe9ef37 100644 --- a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala +++ b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala @@ -39,6 +39,7 @@ abstract class MutableSettings extends AbsSettings { def Xprintpos: BooleanSetting def Yposdebug: BooleanSetting def Yrangepos: BooleanSetting + def Yshowsymowners: BooleanSetting def Yshowsymkinds: BooleanSetting def breakCycles: BooleanSetting def debug: BooleanSetting diff --git a/src/reflect/scala/reflect/runtime/Settings.scala b/src/reflect/scala/reflect/runtime/Settings.scala index de5ba99900..d46846fc21 100644 --- a/src/reflect/scala/reflect/runtime/Settings.scala +++ b/src/reflect/scala/reflect/runtime/Settings.scala @@ -36,6 +36,7 @@ private[reflect] class Settings extends MutableSettings { val Xprintpos = new BooleanSetting(false) val Yposdebug = new BooleanSetting(false) val Yrangepos = new BooleanSetting(false) + val Yshowsymowners = new BooleanSetting(false) val Yshowsymkinds = new BooleanSetting(false) val breakCycles = new BooleanSetting(false) val debug = new BooleanSetting(false) diff --git a/test/files/neg/literate_existentials.check b/test/files/neg/literate_existentials.check new file mode 100644 index 0000000000..c98f976f79 --- /dev/null +++ b/test/files/neg/literate_existentials.check @@ -0,0 +1,4 @@ +literate_existentials.scala:189: error: Cannot prove that Int <:< M forSome { type M <: String }. + implicitly[Int <:< (M forSome { type M >: Nothing <: String })] // fails + ^ +one error found diff --git a/test/files/neg/literate_existentials.scala b/test/files/neg/literate_existentials.scala new file mode 100644 index 0000000000..8580347bf9 --- /dev/null +++ b/test/files/neg/literate_existentials.scala @@ -0,0 +1,224 @@ + +object LiterateExistentials { + +// Let's play with Scala's type system a bit. +// +// From adriaanm, we have the following substitution rule, which allows us to +// determine whether a type is a subtype of an existential in Scala: +// +// +// T <: subst(U) for all i: subst(Li) <: Vi /\ Vi <: subst(Hi) +// -------------------------------------------------------------- +// T <: U forSome {type X1 :> L1 <: H1; ...; type Xn :> Ln <: Hn} +// +// where subst(T) = T.subst(Xi, Vi) // Vi fresh type variables +// +// T is a subtype of some existential if all constraints of the existential hold +// after substituting Vi for the existentially quantified type variables Xi, +// and T is a subtype of the underlying type U with the same substitution applied. +// +// +// Since we are not a formal substitution system, we will actually be using +// this rule 'backward' in order to determine whether it allows us to +// truthfully make claims; In each example, we will start with the proposition +// that a type is a subtype of an existential. Then, we will fit the +// proposition into the form on the bottom rule by creating a set of bindings +// which allow one to be transformed into the other. Next, we will express the +// top of the substitution rule in terms of a series of constraints. We will +// simplify those constraints until simple inspection can determine whether +// they are consistent. From this, we can conclude whether the type system / +// environment admit the top of the substitution rule (and thus, the bottom). If +// they do, we can say that the proposition is true. + + +// In each case, we will also probe the compiler to see whether _it_ thinks that +// the proposition holds, using an uncommented implicitly[_ <:< _] line. + + + + +// Proposition: Nothing :< (A forSome { type A >: String <: Any }) +// +// +// Bindings: +// T := Nothing +// U := A +// X1 := A +// L1 := String +// H1 := Any +// +// We need: +// +// Nothing <: V1 // (U, which is "A", which V1 substituted for all instances of A) +// String <: V1 +// V1 <: Any +// +// Which simplify to: +// V1 >: String <: Any +// +// That's not inconsistent, so we can say that: +// T <: U forSome { type X1 >: L1 <: H1 } +// which means (under our mappings): +// Nothing <: A forSome { type A >: String <: Any } + +// Now to ask the compiler: + + implicitly[Nothing <:< (A forSome { type A >: String <: Any })] + + +// Let's try another: +// +// Proposition: Int :< (M forSome { type M >: String <: Any }) +// +// Bindings: +// T := Int +// U := M +// X1 := M +// L1 := String +// H1 := Any +// +// We need: +// +// Int <: V1 +// String <: V1 +// V1 <: Any +// +// Which simplify to: +// +// V1 >: lub(Int, String) <: Any +// +// V1 >: Any <: Any +// +// We have demonstrated consistency! We can say that: +// T :< (U forSome { type U >: L1 <: H1 }) +// Under our bindings, this is: +// Int :< (M forSome { type M >: String <: Any }) + + implicitly[Int <:< (M forSome { type M >: String <: Any })] + + + +// Now, let's do a more complicated one: +// +// Proposition: (Nothing, List[String]) <: ((A, B) forSome { type A >: String <: AnyRef; type B >: Null <: List[A] }) +// +// Bindings: +// T := (Nothing, List[String]) +// U := (A, B) +// X1 := A +// X2 := B +// L1 := String +// H1 := AnyRef +// L2 := Null +// H2 := List[A] +// +// We need: +// +// (Nothing, List[String]) <: (V1, V2) +// String <: V1 +// V1 <: AnyRef +// Null <: V2 +// V2 <: List[V1] +// +// Of course, we can split the first line to make: +// +// Nothing <: V1 +// List[String]) <: V2 +// String <: V1 +// V1 <: AnyRef +// Null <: V2 +// V2 <: List[V1] +// +// Which reorder to: +// +// Nothing <: V1 +// String <: V1 +// V1 <: AnyRef +// List[String]) <: V2 +// Null <: V2 +// V2 <: List[V1] +// +// Which simplify to: +// +// String <: V1 +// V1 <: AnyRef +// List[String]) <: V2 +// V2 <: List[V1] +// +// String <: V1 +// V1 <: AnyRef +// String <: V1 +// +// V1 >: String <: AnyRef +// +// Consistency demonstrated! We can say that: +// T <: U forSome {type X1 :> L1 <: H1; type X2 :> L2 <: H2} +// meaning: +// (Nothing, List[String]) <: ((A, B) forSome { type A >: String <: AnyRef; type B >: Null <: List[A] }) + + implicitly[ + (Nothing, List[String]) <:< ((A, B) forSome { type A >: String <: AnyRef; type B >: Null <: List[A] }) + ] + + + +// Now let's try one that isn't true: +// +// Proposition: Int :< (M forSome { type M >: Nothing <: String }) +// +// Bindings: +// T := Int +// U := M +// X1 := M +// L1 := Nothing +// H1 := String +// +// We need: +// +// Int <: V1 +// Nothing <: V1 +// V1 <: String +// +// V1 >: Int <: String +// +// Alas! These are inconsistent! There is no supertype of Int that is a +// subtype of String! Our substitution rule does not allow us to claim that our +// proposition is true. +// + + implicitly[Int <:< (M forSome { type M >: Nothing <: String })] // fails +// The preceeding line causes the compiler to generate an error message. + + + +// Let's look at one final example, courtesy of paulp. +// Proposition: String :< X forSome { type X >: Nothing <: String } +// +// Bindings: +// T := String +// U := X +// X1 := X +// L1 := Nothing +// H1 := String +// +// We need: +// +// String <: V1 +// Nothing <: V1 +// V1 <: String +// +// Which simplify to: +// +// String <: V1 +// V1 <: String +// +// V1 >: String <: String +// +// So, we can say: +// T <: U forSome { type X1 >: L1 <: H1 } +// which means: +// String :< X forSome { type X >: Nothing <: String } + + implicitly[String <:< (X forSome { type X >: Nothing <: String })] + +} diff --git a/test/files/neg/macro-argc-mismatch.check b/test/files/neg/macro-argc-mismatch.check new file mode 100644 index 0000000000..617daa890c --- /dev/null +++ b/test/files/neg/macro-argc-mismatch.check @@ -0,0 +1,49 @@ +Test_2.scala:4: error: missing arguments for macro method one in object Macros + one + ^ +Test_2.scala:5: error: not enough arguments for macro method one: (x: Int)Unit. +Unspecified value parameter x. + one() + ^ +Test_2.scala:6: error: too many arguments for macro method one: (x: Int)Unit + one(2, 3) + ^ +Test_2.scala:7: error: not enough arguments for macro method one: (x: Int)Unit. +Unspecified value parameter x. + one()() + ^ +Test_2.scala:8: error: Unit does not take parameters + one(1)() + ^ +Test_2.scala:10: error: missing arguments for macro method two in object Macros + two + ^ +Test_2.scala:11: error: not enough arguments for macro method two: (x: Int)(y: Int)Unit. +Unspecified value parameter x. + two() + ^ +Test_2.scala:12: error: too many arguments for macro method two: (x: Int)(y: Int)Unit + two(2, 3) + ^ +Test_2.scala:13: error: not enough arguments for macro method two: (x: Int)(y: Int)Unit. +Unspecified value parameter x. + two()() + ^ +Test_2.scala:14: error: missing arguments for macro method two in object Macros + two(1) + ^ +Test_2.scala:15: error: not enough arguments for macro method two: (y: Int)Unit. +Unspecified value parameter y. + two(1)() + ^ +Test_2.scala:16: error: too many arguments for macro method two: (y: Int)Unit + two(1)(2, 3) + ^ +Test_2.scala:17: error: not enough arguments for macro method two: (y: Int)Unit. +Unspecified value parameter y. + two(1)()() + ^ +Test_2.scala:18: error: Unit does not take parameters + two(1)(1)() + ^ +14 errors found diff --git a/test/files/neg/macro-argc-mismatch/Macros_1.scala b/test/files/neg/macro-argc-mismatch/Macros_1.scala new file mode 100644 index 0000000000..4dca644172 --- /dev/null +++ b/test/files/neg/macro-argc-mismatch/Macros_1.scala @@ -0,0 +1,16 @@ +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context + +object Macros { + def one(x: Int): Unit = macro oneImpl + def oneImpl(c: Context)(x: c.Tree) = { + import c.universe._ + q"()" + } + + def two(x: Int)(y: Int): Unit = macro twoImpl + def twoImpl(c: Context)(x: c.Tree)(y: c.Tree) = { + import c.universe._ + q"()" + } +} diff --git a/test/files/neg/macro-argc-mismatch/Test_2.scala b/test/files/neg/macro-argc-mismatch/Test_2.scala new file mode 100644 index 0000000000..28f9c35654 --- /dev/null +++ b/test/files/neg/macro-argc-mismatch/Test_2.scala @@ -0,0 +1,19 @@ +import Macros._ + +object Test extends App { + one + one() + one(2, 3) + one()() + one(1)() + + two + two() + two(2, 3) + two()() + two(1) + two(1)() + two(1)(2, 3) + two(1)()() + two(1)(1)() +}
\ No newline at end of file diff --git a/test/files/neg/macro-bundle-ambiguous.check b/test/files/neg/macro-bundle-ambiguous.check new file mode 100644 index 0000000000..8430496455 --- /dev/null +++ b/test/files/neg/macro-bundle-ambiguous.check @@ -0,0 +1,5 @@ +macro-bundle-ambiguous.scala:13: error: macro implementation reference is ambiguous: makes sense both as +a macro bundle method reference and a vanilla object method reference + def foo: Unit = macro Macros.impl + ^ +one error found diff --git a/test/files/neg/macro-bundle-ambiguous.scala b/test/files/neg/macro-bundle-ambiguous.scala new file mode 100644 index 0000000000..92c359d9a9 --- /dev/null +++ b/test/files/neg/macro-bundle-ambiguous.scala @@ -0,0 +1,14 @@ +import scala.reflect.macros.whitebox._ +import scala.language.experimental.macros + +class Macros(val c: Context) { + def impl = ??? +} + +object Macros { + def impl(c: Context) = ??? +} + +object Test extends App { + def foo: Unit = macro Macros.impl +}
\ No newline at end of file diff --git a/test/files/neg/macro-bundle-priority-bundle.check b/test/files/neg/macro-bundle-priority-bundle.check new file mode 100644 index 0000000000..c6cea72ba6 --- /dev/null +++ b/test/files/neg/macro-bundle-priority-bundle.check @@ -0,0 +1,8 @@ +macro-bundle-priority-bundle.scala:13: error: bundle implementation has incompatible shape: + required: : Macros.this.c.Expr[Unit] + or : : Macros.this.c.Tree + found : (x: Macros.this.c.Tree): Nothing +number of parameter sections differ + def foo: Unit = macro Macros.impl + ^ +one error found diff --git a/test/files/neg/macro-bundle-priority-bundle.scala b/test/files/neg/macro-bundle-priority-bundle.scala new file mode 100644 index 0000000000..ce831a7121 --- /dev/null +++ b/test/files/neg/macro-bundle-priority-bundle.scala @@ -0,0 +1,14 @@ +import scala.reflect.macros.whitebox._ +import scala.language.experimental.macros + +class Macros(val c: Context) { + def impl(x: c.Tree) = ??? +} + +object Macros { + def impl(c: Context)(x: c.Tree) = ??? +} + +object Test extends App { + def foo: Unit = macro Macros.impl +}
\ No newline at end of file diff --git a/test/files/neg/macro-bundle-priority-nonbundle.check b/test/files/neg/macro-bundle-priority-nonbundle.check new file mode 100644 index 0000000000..0d03b5074b --- /dev/null +++ b/test/files/neg/macro-bundle-priority-nonbundle.check @@ -0,0 +1,8 @@ +macro-bundle-priority-nonbundle.scala:13: error: macro implementation has incompatible shape: + required: (c: scala.reflect.macros.whitebox.Context): c.Expr[Unit] + or : (c: scala.reflect.macros.whitebox.Context): c.Tree + found : (c: scala.reflect.macros.whitebox.Context)(x: c.Tree): Nothing +number of parameter sections differ + def foo: Unit = macro Macros.impl + ^ +one error found diff --git a/test/files/neg/macro-bundle-priority-nonbundle.scala b/test/files/neg/macro-bundle-priority-nonbundle.scala new file mode 100644 index 0000000000..8dc00f6dd3 --- /dev/null +++ b/test/files/neg/macro-bundle-priority-nonbundle.scala @@ -0,0 +1,14 @@ +import scala.reflect.macros.whitebox._ +import scala.language.experimental.macros + +class Macros(val c: scala.reflect.api.Universe) { + def impl(x: c.Tree) = ??? +} + +object Macros { + def impl(c: Context)(x: c.Tree) = ??? +} + +object Test extends App { + def foo: Unit = macro Macros.impl +}
\ No newline at end of file diff --git a/test/files/neg/macro-invalidusage-badargs.check b/test/files/neg/macro-invalidusage-badargs.check index 4c1115418b..3fd3c53691 100644 --- a/test/files/neg/macro-invalidusage-badargs.check +++ b/test/files/neg/macro-invalidusage-badargs.check @@ -3,13 +3,14 @@ Macros_Test_2.scala:5: error: type mismatch; required: Int foo("42") ^ -Macros_Test_2.scala:6: error: too few argument lists for macro invocation +Macros_Test_2.scala:6: error: missing arguments for macro method foo in object Macros foo ^ Macros_Test_2.scala:7: error: Int does not take parameters foo(4)(2) ^ -Macros_Test_2.scala:8: error: macro applications do not support named and/or default arguments +Macros_Test_2.scala:8: error: not enough arguments for macro method foo: (x: Int)Int. +Unspecified value parameter x. foo() ^ Macros_Test_2.scala:9: error: too many arguments for macro method foo: (x: Int)Int diff --git a/test/files/neg/macro-qmarkqmarkqmark.check b/test/files/neg/macro-qmarkqmarkqmark.check index bc3e25edaf..b4f8ea905f 100644 --- a/test/files/neg/macro-qmarkqmarkqmark.check +++ b/test/files/neg/macro-qmarkqmarkqmark.check @@ -1,7 +1,7 @@ macro-qmarkqmarkqmark.scala:5: error: macro implementation is missing foo1 ^ -macro-qmarkqmarkqmark.scala:8: error: too few argument lists for macro invocation +macro-qmarkqmarkqmark.scala:8: error: missing arguments for macro method foo2 in object Macros foo2 ^ macro-qmarkqmarkqmark.scala:9: error: macro implementation is missing diff --git a/test/files/neg/macro-quasiquotes.check b/test/files/neg/macro-quasiquotes.check index c690b61fe1..a985aee156 100644 --- a/test/files/neg/macro-quasiquotes.check +++ b/test/files/neg/macro-quasiquotes.check @@ -1,4 +1,4 @@ -Macros_1.scala:14: error: macro implementation has incompatible shape: +Macros_1.scala:14: error: bundle implementation has incompatible shape: required: (x: Impls.this.c.Expr[Int]): Impls.this.c.Expr[Unit] or : (x: Impls.this.c.Tree): Impls.this.c.Tree found : (x: Impls.this.c.universe.Block): Impls.this.c.Tree diff --git a/test/files/neg/t4749.check b/test/files/neg/t4749.check index 63d5c21532..3539140954 100644 --- a/test/files/neg/t4749.check +++ b/test/files/neg/t4749.check @@ -25,6 +25,10 @@ t4749.scala:26: warning: Fail6 has a main method with parameter type Array[Strin object Fail6 { ^ +t4749.scala:42: warning: Win3 has a main method with parameter type Array[String], but bippy.Win3 will not be a runnable program. + Reason: main method must have exact signature (Array[String])Unit + object Win3 extends WinBippy[Unit] { } + ^ error: No warnings can be incurred under -Xfatal-warnings. -6 warnings found +7 warnings found one error found diff --git a/test/files/neg/t7157.check b/test/files/neg/t7157.check index c6a7af9a23..3988460d4b 100644 --- a/test/files/neg/t7157.check +++ b/test/files/neg/t7157.check @@ -7,7 +7,8 @@ Test_2.scala:6: error: too many arguments for macro method m1_0_0: ()Unit Test_2.scala:7: error: too many arguments for macro method m1_0_0: ()Unit m1_0_0(1, 2, 3) ^ -Test_2.scala:9: error: macro applications do not support named and/or default arguments +Test_2.scala:9: error: not enough arguments for macro method m1_1_1: (x: Int)Unit. +Unspecified value parameter x. m1_1_1() ^ Test_2.scala:11: error: too many arguments for macro method m1_1_1: (x: Int)Unit @@ -16,22 +17,27 @@ Test_2.scala:11: error: too many arguments for macro method m1_1_1: (x: Int)Unit Test_2.scala:12: error: too many arguments for macro method m1_1_1: (x: Int)Unit m1_1_1(1, 2, 3) ^ -Test_2.scala:14: error: macro applications do not support named and/or default arguments +Test_2.scala:14: error: not enough arguments for macro method m1_2_2: (x: Int, y: Int)Unit. +Unspecified value parameters x, y. m1_2_2() ^ -Test_2.scala:15: error: macro applications do not support named and/or default arguments +Test_2.scala:15: error: not enough arguments for macro method m1_2_2: (x: Int, y: Int)Unit. +Unspecified value parameter y. m1_2_2(1) ^ Test_2.scala:17: error: too many arguments for macro method m1_2_2: (x: Int, y: Int)Unit m1_2_2(1, 2, 3) ^ -Test_2.scala:24: error: macro applications do not support named and/or default arguments +Test_2.scala:24: error: not enough arguments for macro method m1_1_inf: (x: Int, y: Int*)Unit. +Unspecified value parameters x, y. m1_1_inf() ^ -Test_2.scala:29: error: macro applications do not support named and/or default arguments +Test_2.scala:29: error: not enough arguments for macro method m1_2_inf: (x: Int, y: Int, z: Int*)Unit. +Unspecified value parameters x, y, z. m1_2_inf() ^ -Test_2.scala:30: error: macro applications do not support named and/or default arguments +Test_2.scala:30: error: not enough arguments for macro method m1_2_inf: (x: Int, y: Int, z: Int*)Unit. +Unspecified value parameters y, z. m1_2_inf(1) ^ Test_2.scala:35: error: too many arguments for macro method m2_0_0: ()Unit @@ -43,7 +49,8 @@ Test_2.scala:36: error: too many arguments for macro method m2_0_0: ()Unit Test_2.scala:37: error: too many arguments for macro method m2_0_0: ()Unit m2_0_0()(1, 2, 3) ^ -Test_2.scala:39: error: macro applications do not support named and/or default arguments +Test_2.scala:39: error: not enough arguments for macro method m2_1_1: (x: Int)Unit. +Unspecified value parameter x. m2_1_1()() ^ Test_2.scala:41: error: too many arguments for macro method m2_1_1: (x: Int)Unit @@ -52,22 +59,27 @@ Test_2.scala:41: error: too many arguments for macro method m2_1_1: (x: Int)Unit Test_2.scala:42: error: too many arguments for macro method m2_1_1: (x: Int)Unit m2_1_1()(1, 2, 3) ^ -Test_2.scala:44: error: macro applications do not support named and/or default arguments +Test_2.scala:44: error: not enough arguments for macro method m2_2_2: (x: Int, y: Int)Unit. +Unspecified value parameters x, y. m2_2_2()() ^ -Test_2.scala:45: error: macro applications do not support named and/or default arguments +Test_2.scala:45: error: not enough arguments for macro method m2_2_2: (x: Int, y: Int)Unit. +Unspecified value parameter y. m2_2_2()(1) ^ Test_2.scala:47: error: too many arguments for macro method m2_2_2: (x: Int, y: Int)Unit m2_2_2()(1, 2, 3) ^ -Test_2.scala:54: error: macro applications do not support named and/or default arguments +Test_2.scala:54: error: not enough arguments for macro method m2_1_inf: (x: Int, y: Int*)Unit. +Unspecified value parameters x, y. m2_1_inf()() ^ -Test_2.scala:59: error: macro applications do not support named and/or default arguments +Test_2.scala:59: error: not enough arguments for macro method m2_2_inf: (x: Int, y: Int, z: Int*)Unit. +Unspecified value parameters x, y, z. m2_2_inf()() ^ -Test_2.scala:60: error: macro applications do not support named and/or default arguments +Test_2.scala:60: error: not enough arguments for macro method m2_2_inf: (x: Int, y: Int, z: Int*)Unit. +Unspecified value parameters y, z. m2_2_inf()(1) ^ 24 errors found diff --git a/test/files/neg/t8266-invalid-interp.check b/test/files/neg/t8266-invalid-interp.check new file mode 100644 index 0000000000..70dd4081b0 --- /dev/null +++ b/test/files/neg/t8266-invalid-interp.check @@ -0,0 +1,10 @@ +t8266-invalid-interp.scala:4: error: Trailing '\' escapes nothing. + f"a\", + ^ +t8266-invalid-interp.scala:5: error: invalid escape character at index 1 in "a\xc" + f"a\xc", + ^ +t8266-invalid-interp.scala:7: error: invalid escape character at index 1 in "a\vc" + f"a\vc" + ^ +three errors found diff --git a/test/files/neg/t8266-invalid-interp.scala b/test/files/neg/t8266-invalid-interp.scala new file mode 100644 index 0000000000..4b26546880 --- /dev/null +++ b/test/files/neg/t8266-invalid-interp.scala @@ -0,0 +1,9 @@ + +trait X { + def f = Seq( + f"a\", + f"a\xc", + // following could suggest \u000b for vertical tab, similar for \a alert + f"a\vc" + ) +} diff --git a/test/files/pos/existential-java-case-class/Client.scala b/test/files/pos/existential-java-case-class/Client.scala new file mode 100644 index 0000000000..368899820f --- /dev/null +++ b/test/files/pos/existential-java-case-class/Client.scala @@ -0,0 +1,3 @@ +case class CC(x: J[_]) + +case class CC1(x: Any => J[_]) diff --git a/test/files/pos/existential-java-case-class/J.java b/test/files/pos/existential-java-case-class/J.java new file mode 100644 index 0000000000..7fd7848286 --- /dev/null +++ b/test/files/pos/existential-java-case-class/J.java @@ -0,0 +1 @@ +public class J<T extends String> {} diff --git a/test/files/pos/macro-bundle-disambiguate-bundle.check b/test/files/pos/macro-bundle-disambiguate-bundle.check new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/files/pos/macro-bundle-disambiguate-bundle.check diff --git a/test/files/pos/macro-bundle-disambiguate-bundle.scala b/test/files/pos/macro-bundle-disambiguate-bundle.scala new file mode 100644 index 0000000000..04809317e1 --- /dev/null +++ b/test/files/pos/macro-bundle-disambiguate-bundle.scala @@ -0,0 +1,14 @@ +import scala.reflect.macros.whitebox._ +import scala.language.experimental.macros + +class Macros(val c: Context) { + def impl = ??? +} + +object Macros { + def impl(c: Context)(x: c.Tree) = ??? +} + +object Test extends App { + def foo: Unit = macro Macros.impl +}
\ No newline at end of file diff --git a/test/files/pos/macro-bundle-disambiguate-nonbundle.check b/test/files/pos/macro-bundle-disambiguate-nonbundle.check new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/files/pos/macro-bundle-disambiguate-nonbundle.check diff --git a/test/files/pos/macro-bundle-disambiguate-nonbundle.scala b/test/files/pos/macro-bundle-disambiguate-nonbundle.scala new file mode 100644 index 0000000000..cb66f28a0b --- /dev/null +++ b/test/files/pos/macro-bundle-disambiguate-nonbundle.scala @@ -0,0 +1,14 @@ +import scala.reflect.macros.whitebox._ +import scala.language.experimental.macros + +class Macros(val c: Context) { + def impl(x: c.Tree) = ??? +} + +object Macros { + def impl(c: Context) = ??? +} + +object Test extends App { + def foo: Unit = macro Macros.impl +}
\ No newline at end of file diff --git a/test/files/pos/t3452f.scala b/test/files/pos/t3452f.scala new file mode 100644 index 0000000000..efe25a62fc --- /dev/null +++ b/test/files/pos/t3452f.scala @@ -0,0 +1,10 @@ +class Base[Coll] { + trait Transformed[S] { + lazy val underlying: Coll = ??? + } +} + +class Derived extends Base[String] { + class C extends Transformed[Any] +} + diff --git a/test/files/run/macro-expand-default-named.check b/test/files/run/macro-expand-default-named.check new file mode 100644 index 0000000000..2d75772572 --- /dev/null +++ b/test/files/run/macro-expand-default-named.check @@ -0,0 +1,56 @@ +Test.this.one(2, -40) = 42 +Test.this.one(y = -40, x = 2) = 42 +Test.this.one(2, -40) = 42 +Test.this.one(100) = 140 +Test.this.one(y = 100) = -98 +Test.this.one(100) = 140 +Test.this.one() = 42 +Test.this.qualone.one(2, -40) = 42 +Test.this.qualone.one(y = -40, x = 2) = 42 +Test.this.qualone.one(2, -40) = 42 +Test.this.qualone.one(x = 100) = 140 +Test.this.qualone.one(y = 100) = -98 +Test.this.qualone.one(x = 100) = 140 +Test.this.qualone.one() = 42 +Test.this.onezero(2, -40)(1, 2) = 41 +Test.this.onezero(y = -40, x = 2)(z = 3, w = 4) = 41 +Test.this.onezero(2, -40)(5, 6) = 41 +Test.this.onezero(100)(7, 8) = 139 +Test.this.onezero(y = 100)(z = 9, w = 10) = -99 +Test.this.onezero(100)(11, 12) = 139 +Test.this.onezero()(13, 14) = 41 +Test.this.qualonezero.onezero(2, -40)(15, 16) = 41 +Test.this.qualonezero.onezero(y = -40, x = 2)(z = 17, w = 18) = 41 +Test.this.qualonezero.onezero(2, -40)(19, 20) = 41 +Test.this.qualonezero.onezero(x = 100)(z = 21, w = 22) = 139 +Test.this.qualonezero.onezero(y = 100)(z = 23, w = 24) = -99 +Test.this.qualonezero.onezero(x = 100)(z = 25, w = 26) = 139 +Test.this.qualonezero.onezero()(z = 27, w = 28) = 41 +Test.this.zeroone(1, 2)(2, -40) = 41 +Test.this.zeroone(x = 3, y = 4)(w = -40, z = 2) = 41 +Test.this.zeroone(5, 6)(2, -40) = 41 +Test.this.zeroone(x = 7, y = 8)(z = 100) = 139 +Test.this.zeroone(x = 9, y = 10)(w = 100) = -99 +Test.this.zeroone(x = 11, y = 12)(z = 100) = 139 +Test.this.zeroone(x = 13, y = 14)() = 41 +Test.this.qualzeroone.zeroone(15, 16)(2, -40) = 41 +Test.this.qualzeroone.zeroone(x = 17, y = 18)(w = -40, z = 2) = 41 +Test.this.qualzeroone.zeroone(19, 20)(2, -40) = 41 +Test.this.qualzeroone.zeroone(x = 21, y = 22)(z = 100) = 139 +Test.this.qualzeroone.zeroone(x = 23, y = 24)(w = 100) = -99 +Test.this.qualzeroone.zeroone(x = 25, y = 26)(z = 100) = 139 +Test.this.qualzeroone.zeroone(x = 27, y = 28)() = 41 +Test.this.oneone(2, -40)(2, -40) = 84 +Test.this.oneone(y = -40, x = 2)(w = -40, z = 2) = 84 +Test.this.oneone(2, -40)(2, -40) = 84 +Test.this.oneone(x = 100)(z = 100) = 280 +Test.this.oneone(y = 100)(w = 100) = -196 +Test.this.oneone(x = 100)(z = 100) = 280 +Test.this.oneone()() = 84 +Test.this.qualoneone.oneone(2, -40)(2, -40) = 84 +Test.this.qualoneone.oneone(y = -40, x = 2)(w = -40, z = 2) = 84 +Test.this.qualoneone.oneone(2, -40)(2, -40) = 84 +Test.this.qualoneone.oneone(x = 100)(z = 100) = 280 +Test.this.qualoneone.oneone(y = 100)(w = 100) = -196 +Test.this.qualoneone.oneone(x = 100)(z = 100) = 280 +Test.this.qualoneone.oneone()() = 84 diff --git a/test/files/run/macro-expand-default-named/Impls_1.scala b/test/files/run/macro-expand-default-named/Impls_1.scala new file mode 100644 index 0000000000..73774cd56a --- /dev/null +++ b/test/files/run/macro-expand-default-named/Impls_1.scala @@ -0,0 +1,37 @@ +import scala.reflect.macros.blackbox.Context + +object Impls { + def one(c: Context)(x: c.Tree, y: c.Tree) = { + import c.universe._ + val x1 = x orElse q"2" + val y1 = y orElse q"-40" + q"println(${c.macroApplication.toString + " = "} + ($x1 - $y1))" + } + + def onezero(c: Context)(x: c.Tree, y: c.Tree)(z: c.Tree, w: c.Tree) = { + import c.universe._ + val x1 = x orElse q"2" + val y1 = y orElse q"-40" + val z1 = z + val w1 = w + q"println(${c.macroApplication.toString + " = "} + ($x1 - $y1 + $z1 - $w1))" + } + + def zeroone(c: Context)(x: c.Tree, y: c.Tree)(z: c.Tree, w: c.Tree) = { + import c.universe._ + val x1 = x + val y1 = y + val z1 = z orElse q"2" + val w1 = w orElse q"-40" + q"println(${c.macroApplication.toString + " = "} + ($x1 - $y1 + $z1 - $w1))" + } + + def oneone(c: Context)(x: c.Tree, y: c.Tree)(z: c.Tree, w: c.Tree) = { + import c.universe._ + val x1 = x orElse q"2" + val y1 = y orElse q"-40" + val z1 = z orElse q"2" + val w1 = w orElse q"-40" + q"println(${c.macroApplication.toString + " = "} + ($x1 - $y1 + $z1 - $w1))" + } +}
\ No newline at end of file diff --git a/test/files/run/macro-expand-default-named/Macros_Test_2.scala b/test/files/run/macro-expand-default-named/Macros_Test_2.scala new file mode 100644 index 0000000000..e58eddd9a3 --- /dev/null +++ b/test/files/run/macro-expand-default-named/Macros_Test_2.scala @@ -0,0 +1,71 @@ +import scala.language.experimental.macros + +object Test extends App { + def one(x: Int = 2, y: Int = -40): Unit = macro Impls.one + one(2, -40) + one(y = -40, x = 2) + one(x = 2, y = -40) + one(x = 100) + one(y = 100) + one(100) + one() + var qualone = this + qualone.one(2, -40) + qualone.one(y = -40, x = 2) + qualone.one(x = 2, y = -40) + qualone.one(x = 100) + qualone.one(y = 100) + qualone.one(100) + qualone.one() + + def onezero(x: Int = 2, y: Int = -40)(z: Int, w: Int): Unit = macro Impls.onezero + onezero(2, -40)(1, 2) + onezero(y = -40, x = 2)(3, 4) + onezero(x = 2, y = -40)(5, 6) + onezero(x = 100)(7, 8) + onezero(y = 100)(9, 10) + onezero(100)(11, 12) + onezero()(13, 14) + var qualonezero = this + qualonezero.onezero(2, -40)(15, 16) + qualonezero.onezero(y = -40, x = 2)(17, 18) + qualonezero.onezero(x = 2, y = -40)(19, 20) + qualonezero.onezero(x = 100)(21, 22) + qualonezero.onezero(y = 100)(23, 24) + qualonezero.onezero(100)(25, 26) + qualonezero.onezero()(27, 28) + + def zeroone(x: Int, y: Int)(z: Int = 2, w: Int = -40): Unit = macro Impls.zeroone + zeroone(1, 2)(2, -40) + zeroone(3, 4)(w = -40, z = 2) + zeroone(5, 6)(z = 2, w = -40) + zeroone(7, 8)(z = 100) + zeroone(9, 10)(w = 100) + zeroone(11, 12)(100) + zeroone(13, 14)() + var qualzeroone = this + qualzeroone.zeroone(15, 16)(2, -40) + qualzeroone.zeroone(17, 18)(w = -40, z = 2) + qualzeroone.zeroone(19, 20)(z = 2, w = -40) + qualzeroone.zeroone(21, 22)(z = 100) + qualzeroone.zeroone(23, 24)(w = 100) + qualzeroone.zeroone(25, 26)(100) + qualzeroone.zeroone(27, 28)() + + def oneone(x: Int = 2, y: Int = -40)(z: Int = 2, w: Int = -40): Unit = macro Impls.oneone + oneone(2, -40)(2, -40) + oneone(y = -40, x = 2)(w = -40, z = 2) + oneone(x = 2, y = -40)(z = 2, w = -40) + oneone(x = 100)(z = 100) + oneone(y = 100)(w = 100) + oneone(100)(100) + oneone()() + var qualoneone = this + qualoneone.oneone(2, -40)(2, -40) + qualoneone.oneone(y = -40, x = 2)(w = -40, z = 2) + qualoneone.oneone(x = 2, y = -40)(z = 2, w = -40) + qualoneone.oneone(x = 100)(z = 100) + qualoneone.oneone(y = 100)(w = 100) + qualoneone.oneone(100)(100) + qualoneone.oneone()() +}
\ No newline at end of file diff --git a/test/files/run/macro-expand-ownerchain-a.check b/test/files/run/macro-expand-ownerchain-a.check new file mode 100644 index 0000000000..51993f072d --- /dev/null +++ b/test/files/run/macro-expand-ownerchain-a.check @@ -0,0 +1,2 @@ +2 +2 diff --git a/test/files/run/macro-expand-ownerchain-a/Macros_1.scala b/test/files/run/macro-expand-ownerchain-a/Macros_1.scala new file mode 100644 index 0000000000..0d11c24ad1 --- /dev/null +++ b/test/files/run/macro-expand-ownerchain-a/Macros_1.scala @@ -0,0 +1,11 @@ +import scala.reflect.macros.whitebox._ +import scala.language.experimental.macros + +object Macros { + def impl(c: Context)(x: c.Tree, y: c.Tree) = { + import c.universe._ + q"println($x)" + } + + def foo(x: Int, y: Int): Unit = macro impl +}
\ No newline at end of file diff --git a/test/files/run/macro-expand-ownerchain-a/Test_2.scala b/test/files/run/macro-expand-ownerchain-a/Test_2.scala new file mode 100644 index 0000000000..738afc75df --- /dev/null +++ b/test/files/run/macro-expand-ownerchain-a/Test_2.scala @@ -0,0 +1,4 @@ +object Test extends App { + Macros.foo(y = 1, x = ((x: Int) => x)(2)) + Macros.foo(y = 1, x = {val x = 2; x}) +}
\ No newline at end of file diff --git a/test/files/run/macro-invalidusage-partialapplication-with-tparams.check b/test/files/run/macro-invalidusage-partialapplication-with-tparams.check index 6cbcb9e5af..f1061e00f7 100644 --- a/test/files/run/macro-invalidusage-partialapplication-with-tparams.check +++ b/test/files/run/macro-invalidusage-partialapplication-with-tparams.check @@ -1,3 +1,3 @@ reflective compilation has failed: -too few argument lists for macro invocation +missing arguments for macro method foo in object Macros diff --git a/test/files/run/macro-invalidusage-partialapplication.check b/test/files/run/macro-invalidusage-partialapplication.check index 6cbcb9e5af..f1061e00f7 100644 --- a/test/files/run/macro-invalidusage-partialapplication.check +++ b/test/files/run/macro-invalidusage-partialapplication.check @@ -1,3 +1,3 @@ reflective compilation has failed: -too few argument lists for macro invocation +missing arguments for macro method foo in object Macros diff --git a/test/files/run/mixin-signatures.check b/test/files/run/mixin-signatures.check new file mode 100644 index 0000000000..3031fe75af --- /dev/null +++ b/test/files/run/mixin-signatures.check @@ -0,0 +1,59 @@ +class Test$bar1$ { + public java.lang.String Test$bar1$.f(java.lang.Object) + public java.lang.Object Test$bar1$.f(java.lang.Object) <bridge> <synthetic> + public java.lang.String Test$bar1$.g(java.lang.String) + public java.lang.Object Test$bar1$.g(java.lang.Object) <bridge> <synthetic> + public java.lang.String Test$bar1$.g(java.lang.Object) <bridge> <synthetic> + public java.lang.Object Test$bar1$.h(java.lang.Object) +} + +class Test$bar2$ { + public java.lang.Object Test$bar2$.f(java.lang.String) + public java.lang.Object Test$bar2$.f(java.lang.Object) <bridge> <synthetic> + public java.lang.String Test$bar2$.g(java.lang.String) + public java.lang.Object Test$bar2$.g(java.lang.Object) <bridge> <synthetic> + public java.lang.Object Test$bar2$.g(java.lang.String) <bridge> <synthetic> + public java.lang.Object Test$bar2$.h(java.lang.Object) +} + +class Test$bar3$ { + public java.lang.String Foo3.f(java.lang.Object) + generic: public java.lang.String Foo3.f(T) + public java.lang.Object Foo3.f(java.lang.Object) <bridge> <synthetic> + public java.lang.String Test$bar3$.g(java.lang.String) + public java.lang.Object Test$bar3$.g(java.lang.Object) <bridge> <synthetic> + public java.lang.String Test$bar3$.g(java.lang.Object) <bridge> <synthetic> + public java.lang.Object Foo3.h(java.lang.Object) +} + +class Test$bar4$ { + public java.lang.Object Foo4.f(java.lang.String) + generic: public R Foo4.f(java.lang.String) + public java.lang.Object Foo4.f(java.lang.Object) <bridge> <synthetic> + public java.lang.String Test$bar4$.g(java.lang.String) + public java.lang.Object Test$bar4$.g(java.lang.Object) <bridge> <synthetic> + public java.lang.Object Test$bar4$.g(java.lang.String) <bridge> <synthetic> + public java.lang.Object Foo4.h(java.lang.Object) +} + +class Test$bar5$ { + public java.lang.String Test$bar5$.f(java.lang.String) + public java.lang.Object Test$bar5$.f(java.lang.Object) <bridge> <synthetic> + public java.lang.Object Test$bar5$.f(java.lang.String) <bridge> <synthetic> + public java.lang.String Test$bar5$.f(java.lang.Object) <bridge> <synthetic> + public java.lang.String Test$bar5$.g(java.lang.String) + public java.lang.Object Test$bar5$.g(java.lang.Object) <bridge> <synthetic> + public java.lang.Object Test$bar5$.g(java.lang.String) <bridge> <synthetic> + public java.lang.String Test$bar5$.g(java.lang.Object) <bridge> <synthetic> + public java.lang.Object Test$bar5$.h(java.lang.Object) +} + +class Foo1$class { + public static java.lang.String Foo1$class.f(Foo1,java.lang.Object) +} + +class Foo2$class { + public static java.lang.Object Foo2$class.f(Foo2,java.lang.String) +} + +000000000000000000000000000000000000 diff --git a/test/files/run/mixin-signatures.scala b/test/files/run/mixin-signatures.scala new file mode 100644 index 0000000000..afd3fad877 --- /dev/null +++ b/test/files/run/mixin-signatures.scala @@ -0,0 +1,105 @@ +trait Base[T, R] { + def f(x: T): R + def g(x: T): R + def h(x: T): R = null.asInstanceOf[R] +} + +trait Foo1[T] extends Base[T, String] { + def f(x: T): String = null + def g(x: T): String +} +trait Foo2[R] extends Base[String, R] { + def f(x: String): R = { print(x.length) ; null.asInstanceOf[R] } + def g(x: String): R +} +abstract class Foo3[T] extends Base[T, String] { + def f(x: T): String = "" + def g(x: T): String +} +abstract class Foo4[R] extends Base[String, R] { + def f(x: String): R = { print(x.length) ; null.asInstanceOf[R] } + def g(x: String): R +} + +object Test { + object bar1 extends Foo1[String] { def g(x: String): String = { print(x.length) ; "" } } + object bar2 extends Foo2[String] { def g(x: String): String = { print(x.length) ; "" } } + object bar3 extends Foo3[String] { def g(x: String): String = { print(x.length) ; "" } } + object bar4 extends Foo4[String] { def g(x: String): String = { print(x.length) ; "" } } + + // Notice that in bar5, f and g require THREE bridges, because the final + // implementation is (String)String, but: + // + // inherited abstract signatures: T(R), (T)String, and (String)R + // which erase to: (Object)Object, (Object)String, and (String)Object + // + // each of which must be bridged to the actual (String)String implementation. + // + // public java.lang.String Test$bar5$.g(java.lang.String) + // public java.lang.Object Test$bar5$.g(java.lang.String) <bridge> <synthetic> + // public java.lang.Object Test$bar5$.g(java.lang.Object) <bridge> <synthetic> + // public java.lang.String Test$bar5$.g(java.lang.Object) <bridge> <synthetic> + object bar5 extends Foo1[String] with Foo2[String] { + override def f(x: String): String = { print(x.length) ; x } + def g(x: String): String = { print(x.length) ; x } + } + + final def m1[T, R](x: Base[T, R], y: T) = { x.f(y) ; x.g(y) ; x.h(y) } + final def m2[T](x: Base[T, String], y: T) = { x.f(y) ; x.g(y) ; x.h(y) } + final def m3[R](x: Base[String, R]) = { x.f("") ; x.g("") ; x.h("") } + final def m4(x: Base[String, String]) = { x.f("") ; x.g("") ; x.h("") } + + final def m11[T](x: Foo1[T], y: T) = { x.f(y) ; x.g(y) ; x.h(y) } + final def m12(x: Foo1[String]) = { x.f("") ; x.g("") ; x.h("") } + final def m21[T](x: Foo2[T], y: T) = { x.f("") ; x.g("") ; x.h("") } + final def m22(x: Foo2[String]) = { x.f("") ; x.g("") ; x.h("") } + final def m31[T](x: Foo3[T], y: T) = { x.f(y) ; x.g(y) ; x.h(y) } + final def m32(x: Foo3[String]) = { x.f("") ; x.g("") ; x.h("") } + final def m41[T](x: Foo4[T], y: T) = { x.f("") ; x.g("") ; x.h("") } + final def m42(x: Foo4[String]) = { x.f("") ; x.g("") ; x.h("") } + + def go = { + m1(bar1, "") ; m2(bar1, "") ; m3(bar1) ; m4(bar1) + m1(bar2, "") ; m2(bar2, "") ; m3(bar2) ; m4(bar2) + m1(bar3, "") ; m2(bar3, "") ; m3(bar3) ; m4(bar3) + m1(bar4, "") ; m2(bar4, "") ; m3(bar4) ; m4(bar4) + + m11(bar1, "") ; m12(bar1) + m21(bar2, "") ; m22(bar2) + m31(bar3, "") ; m32(bar3) + m41(bar4, "") ; m42(bar4) + "" + } + + def flagsString(m: java.lang.reflect.Method) = { + val str = List( + if (m.isBridge) "<bridge>" else "", + if (m.isSynthetic) "<synthetic>" else "" + ) filterNot (_ == "") mkString " " + + if (str == "") "" else " " + str + // + // val flags = scala.reflect.internal.ClassfileConstants.toScalaMethodFlags(m.getModifiers()) + // scala.tools.nsc.symtab.Flags.flagsToString(flags) + } + + def show(clazz: Class[_]) { + print(clazz + " {") + clazz.getMethods.sortBy(x => (x.getName, x.isBridge, x.toString)) filter (_.getName.length == 1) foreach { m => + print("\n " + m + flagsString(m)) + if ("" + m != "" + m.toGenericString) { + print("\n generic: " + m.toGenericString) + } + } + println("\n}") + println("") + } + def show(x: AnyRef) { show(x.getClass) } + def show(x: String) { show(Class.forName(x)) } + + def main(args: Array[String]): Unit = { + List(bar1, bar2, bar3, bar4, bar5) foreach show + List("Foo1$class", "Foo2$class") foreach show + println(go) + } +}
\ No newline at end of file diff --git a/test/files/run/reify-repl-fail-gracefully.check b/test/files/run/reify-repl-fail-gracefully.check index 29ccee3cc6..c78d95dbed 100644 --- a/test/files/run/reify-repl-fail-gracefully.check +++ b/test/files/run/reify-repl-fail-gracefully.check @@ -10,7 +10,7 @@ import scala.reflect.runtime.universe._ scala> scala> reify -<console>:12: error: too few argument lists for macro invocation +<console>:12: error: missing arguments for macro method reify in class Universe reify ^ diff --git a/test/files/run/t3452.check b/test/files/run/t3452.check new file mode 100644 index 0000000000..b8626c4cff --- /dev/null +++ b/test/files/run/t3452.check @@ -0,0 +1 @@ +4 diff --git a/test/files/run/t3452.scala b/test/files/run/t3452.scala new file mode 100644 index 0000000000..253fc93cfa --- /dev/null +++ b/test/files/run/t3452.scala @@ -0,0 +1,21 @@ +trait IStringPair[T] { + def a : String + def b : String + def build(a : String, b : String) : T + def cat(that : IStringPair[T]) = build(this.a + that.a, this.b + that.b) + override def toString = a + b +} + +class StringPair(val a : String, val b : String) extends IStringPair[StringPair] { + def build(a : String, b : String) = new StringPair(a, b) + def len = a.length + b.length +} + +object Test { + def main(args: Array[String]): Unit = { + val a = new StringPair("A", "B") + val b = new StringPair("1", "2") + val c = a cat b + println(c.len) + } +} diff --git a/test/files/run/t3452a.check b/test/files/run/t3452a.check new file mode 100644 index 0000000000..9ff787eb86 --- /dev/null +++ b/test/files/run/t3452a.check @@ -0,0 +1 @@ +BulkSearch.searchFor called. diff --git a/test/files/run/t3452a/J_2.java b/test/files/run/t3452a/J_2.java new file mode 100644 index 0000000000..62057ffe61 --- /dev/null +++ b/test/files/run/t3452a/J_2.java @@ -0,0 +1,5 @@ +public class J_2 { + public static void main(String[] args) { + BulkSearchInstance.searchFor(new UpRelation()); + } +} diff --git a/test/files/run/t3452a/S_1.scala b/test/files/run/t3452a/S_1.scala new file mode 100644 index 0000000000..791faf42fa --- /dev/null +++ b/test/files/run/t3452a/S_1.scala @@ -0,0 +1,24 @@ +abstract class BulkSearch { + type R <: Row + type Rel <: Relation [R] + type Corr <: Correspondence[R] + + def searchFor(input: Rel): Mapping[Corr] = { println("BulkSearch.searchFor called.") ; null } +} + +object BulkSearchInstance extends BulkSearch { + type R = UpRow + type Rel = UpRelation + type Corr = UpCorrespondence +} + +class Row +class UpRow extends Row + +class Relation [R <: Row] +class UpRelation extends Relation [UpRow] + +class Correspondence [R <: Row] +class UpCorrespondence extends Correspondence [UpRow] + +class Mapping[MC <: Correspondence[_]] diff --git a/test/files/run/t3452a/S_3.scala b/test/files/run/t3452a/S_3.scala new file mode 100644 index 0000000000..aaa898dcde --- /dev/null +++ b/test/files/run/t3452a/S_3.scala @@ -0,0 +1,5 @@ +object Test { + def main(args: Array[String]): Unit = { + J_2.main(args) + } +} diff --git a/test/files/run/t3452b-bcode.check b/test/files/run/t3452b-bcode.check new file mode 100644 index 0000000000..204c3d0437 --- /dev/null +++ b/test/files/run/t3452b-bcode.check @@ -0,0 +1,2 @@ +Search received: test +SearchC received: test diff --git a/test/files/run/t3452b-bcode.flags b/test/files/run/t3452b-bcode.flags new file mode 100644 index 0000000000..c30091d3de --- /dev/null +++ b/test/files/run/t3452b-bcode.flags @@ -0,0 +1 @@ +-Ybackend:GenBCode diff --git a/test/files/run/t3452b-bcode/J_2.java b/test/files/run/t3452b-bcode/J_2.java new file mode 100644 index 0000000000..839f334508 --- /dev/null +++ b/test/files/run/t3452b-bcode/J_2.java @@ -0,0 +1,6 @@ +public class J_2 { + public static void j() { + StringSearch.search("test"); + StringSearch.searchC("test"); + } +} diff --git a/test/files/run/t3452b-bcode/S_1.scala b/test/files/run/t3452b-bcode/S_1.scala new file mode 100644 index 0000000000..a209f12035 --- /dev/null +++ b/test/files/run/t3452b-bcode/S_1.scala @@ -0,0 +1,17 @@ +trait Search[M] { + def search(input: M): C[Int] = { + println("Search received: " + input) + null + } +} + +class SearchC[M] { + def searchC(input: M): C[Int] = { + println("SearchC received: " + input) + null + } +} + +object StringSearch extends SearchC[String] with Search[String] + +trait C[T] diff --git a/test/files/run/t3452b-bcode/S_3.scala b/test/files/run/t3452b-bcode/S_3.scala new file mode 100644 index 0000000000..102b433f47 --- /dev/null +++ b/test/files/run/t3452b-bcode/S_3.scala @@ -0,0 +1,5 @@ +object Test { + def main(args: Array[String]): Unit = { + J_2.j() + } +} diff --git a/test/files/run/t3452b.check b/test/files/run/t3452b.check new file mode 100644 index 0000000000..204c3d0437 --- /dev/null +++ b/test/files/run/t3452b.check @@ -0,0 +1,2 @@ +Search received: test +SearchC received: test diff --git a/test/files/run/t3452b/J_2.java b/test/files/run/t3452b/J_2.java new file mode 100644 index 0000000000..839f334508 --- /dev/null +++ b/test/files/run/t3452b/J_2.java @@ -0,0 +1,6 @@ +public class J_2 { + public static void j() { + StringSearch.search("test"); + StringSearch.searchC("test"); + } +} diff --git a/test/files/run/t3452b/S_1.scala b/test/files/run/t3452b/S_1.scala new file mode 100644 index 0000000000..a209f12035 --- /dev/null +++ b/test/files/run/t3452b/S_1.scala @@ -0,0 +1,17 @@ +trait Search[M] { + def search(input: M): C[Int] = { + println("Search received: " + input) + null + } +} + +class SearchC[M] { + def searchC(input: M): C[Int] = { + println("SearchC received: " + input) + null + } +} + +object StringSearch extends SearchC[String] with Search[String] + +trait C[T] diff --git a/test/files/run/t3452b/S_3.scala b/test/files/run/t3452b/S_3.scala new file mode 100644 index 0000000000..102b433f47 --- /dev/null +++ b/test/files/run/t3452b/S_3.scala @@ -0,0 +1,5 @@ +object Test { + def main(args: Array[String]): Unit = { + J_2.j() + } +} diff --git a/test/files/run/t3452c.check b/test/files/run/t3452c.check new file mode 100644 index 0000000000..ab47181198 --- /dev/null +++ b/test/files/run/t3452c.check @@ -0,0 +1,8 @@ +3 +3 +3 +3 +3 +3 +3 +3 diff --git a/test/files/run/t3452c.scala b/test/files/run/t3452c.scala new file mode 100644 index 0000000000..2c55767abc --- /dev/null +++ b/test/files/run/t3452c.scala @@ -0,0 +1,113 @@ +trait Base[A, B, C] { + def f(x: A, y: B, z: C): Unit + def g(x: A, y: B, z: C) = f(x, y, z) + def h(x: A, y: B, z: C) = g(x, y, z) +} + +trait D1[B, C] extends Base[String, B, C] +trait D2[A, B] extends Base[A, B, String] +trait D3[A, C] extends Base[A, String, C] +trait D4[A] extends Base[A, String, String] +trait D5[B] extends Base[String, B, String] +trait D6[C] extends Base[String, String, C] +trait D7 extends Base[String, String, String] + +trait E1[B, C] extends Base[String, B, C] { def f(x: String, y: B, z: C): Unit ; override def h(x: String, y: B, z: C) = g(x, y, z) } +trait E2[A, B] extends Base[A, B, String] { def f(x: A, y: B, z: String): Unit ; override def h(x: A, y: B, z: String) = g(x, y, z) } +trait E3[A, C] extends Base[A, String, C] { def f(x: A, y: String, z: C): Unit ; override def h(x: A, y: String, z: C) = g(x, y, z) } +trait E4[A] extends Base[A, String, String] { def f(x: A, y: String, z: String): Unit ; override def h(x: A, y: String, z: String) = g(x, y, z) } +trait E5[B] extends Base[String, B, String] { def f(x: String, y: B, z: String): Unit ; override def h(x: String, y: B, z: String) = g(x, y, z) } +trait E6[C] extends Base[String, String, C] { def f(x: String, y: String, z: C): Unit ; override def h(x: String, y: String, z: C) = g(x, y, z) } +trait E7 extends Base[String, String, String] { def f(x: String, y: String, z: String): Unit ; override def h(x: String, y: String, z: String) = g(x, y, z) } + +trait F1[B, C] extends Base[String, B, C] { def f(x: String, y: B, z: C): Unit = println(x.length) } +trait F2[A, B] extends Base[A, B, String] { def f(x: A, y: B, z: String): Unit = println(z.length) } +trait F3[A, C] extends Base[A, String, C] { def f(x: A, y: String, z: C): Unit = println(y.length) } +trait F4[A] extends Base[A, String, String] { def f(x: A, y: String, z: String): Unit = println(y.length) } +trait F5[B] extends Base[String, B, String] { def f(x: String, y: B, z: String): Unit = println(x.length) } +trait F6[C] extends Base[String, String, C] { def f(x: String, y: String, z: C): Unit = println(x.length) } +trait F7 extends Base[String, String, String] { def f(x: String, y: String, z: String): Unit = println(x.length) } + +abstract class DBag extends D1[String, String] with D2[String, String] with D3[String, String] with D4[String] with D5[String] with D6[String] with D7 { + def f(x: String, y: String, z: String) = println(x.length + y.length + z.length) +} +abstract class EBag extends E1[String, String] with E2[String, String] with E3[String, String] with E4[String] with E5[String] with E6[String] with E7 { + def f(x: String, y: String, z: String) = println(x.length + y.length + z.length) +} +abstract class FBag extends F1[String, String] with F2[String, String] with F3[String, String] with F4[String] with F5[String] with F6[String] with F7 { + override def f(x: String, y: String, z: String) = println(x.length + y.length + z.length) +} + +abstract class GBag1[A, B] extends Base[A, B, String] with D2[A, B] { + def f(x: A, y: B, z: String) = println(z.length) +} +abstract class GBag2[A] extends GBag1[A, String] with D4[A] { + override def f(x: A, y: String, z: String) = println(z.length) +} +abstract class GBag3 extends GBag2[String] with D7 { + override def f(x: String, y: String, z: String) = println(z.length) +} +class GBag extends GBag3 with D2[String, String] with D3[String, String] with D4[String] with D5[String] with D6[String] with D7 { +} + +object Test { + def f0(x: Base[String, String, String]) = x.f("a", "b", "c") + def f1(x: D1[String, String]) = x.f("a", "b", "c") + def f2(x: D2[String, String]) = x.f("a", "b", "c") + def f3(x: D3[String, String]) = x.f("a", "b", "c") + def f4(x: D4[String]) = x.f("a", "b", "c") + def f5(x: D5[String]) = x.f("a", "b", "c") + def f6(x: D6[String]) = x.f("a", "b", "c") + def f7(x: D7) = x.f("a", "b", "c") + + def main(args: Array[String]): Unit = { + val x = new DBag { } + f0(x) + f1(x) + f2(x) + f3(x) + f4(x) + f5(x) + f6(x) + f7(x) + } +} + +object TestE { + def f0(x: Base[String, String, String]) = { x.f("a", "b", "c") ; x.g("a", "b", "c") ; x.h("a", "b", "c") } + def f1(x: E1[String, String]) = { x.f("a", "b", "c") ; x.g("a", "b", "c") ; x.h("a", "b", "c") } + def f2(x: E2[String, String]) = { x.f("a", "b", "c") ; x.g("a", "b", "c") ; x.h("a", "b", "c") } + def f3(x: E3[String, String]) = { x.f("a", "b", "c") ; x.g("a", "b", "c") ; x.h("a", "b", "c") } + def f4(x: E4[String]) = { x.f("a", "b", "c") ; x.g("a", "b", "c") ; x.h("a", "b", "c") } + def f5(x: E5[String]) = { x.f("a", "b", "c") ; x.g("a", "b", "c") ; x.h("a", "b", "c") } + def f6(x: E6[String]) = { x.f("a", "b", "c") ; x.g("a", "b", "c") ; x.h("a", "b", "c") } + def f7(x: E7) = { x.f("a", "b", "c") ; x.g("a", "b", "c") ; x.h("a", "b", "c") } + + def main(args: Array[String]): Unit = { + val x = new EBag { } + f0(x) + f1(x) + f2(x) + f3(x) + f4(x) + f5(x) + f6(x) + f7(x) + } +} + + +object TestG { + def f0(x: Base[String, String, String]) = { x.f("a", "b", "c") ; x.g("a", "b", "c") ; x.h("a", "b", "c") } + def f1(x: GBag1[String, String]) = { x.f("a", "b", "c") ; x.g("a", "b", "c") ; x.h("a", "b", "c") } + def f2(x: GBag2[String]) = { x.f("a", "b", "c") ; x.g("a", "b", "c") ; x.h("a", "b", "c") } + def f3(x: GBag3) = { x.f("a", "b", "c") ; x.g("a", "b", "c") ; x.h("a", "b", "c") } + + def main(args: Array[String]): Unit = { + val x = new GBag { } + f0(x) + f1(x) + f2(x) + f3(x) + } +} diff --git a/test/files/run/t3452d/A.scala b/test/files/run/t3452d/A.scala new file mode 100644 index 0000000000..67a2080d27 --- /dev/null +++ b/test/files/run/t3452d/A.scala @@ -0,0 +1,7 @@ +trait TraversableLike[A, Repr] { + def tail: Repr = null.asInstanceOf[Repr] +} + +abstract class AbstractTrav[A] extends TraversableLike[A, Traversable[A]] + +class C[A] extends AbstractTrav[A] diff --git a/test/files/run/t3452d/Test.java b/test/files/run/t3452d/Test.java new file mode 100644 index 0000000000..875be6176c --- /dev/null +++ b/test/files/run/t3452d/Test.java @@ -0,0 +1,12 @@ +import scala.collection.immutable.Nil; +import scala.collection.immutable.List; +import scala.collection.Traversable; + +public class Test { + public static void main(String[] args) { + C<String> c = new C<String>(); + // TODO add a bridge during mixin so we can expose + // sharper generic signature for `tail`. + /*Traversable<String>*/ Object ls = c.tail(); + } +} diff --git a/test/files/run/t3452e/A.scala b/test/files/run/t3452e/A.scala new file mode 100644 index 0000000000..939172f401 --- /dev/null +++ b/test/files/run/t3452e/A.scala @@ -0,0 +1,4 @@ +trait F1[T, R] { + def andThen[A](g: R => A): Int = 0 +} +class C1[TT, RR] extends F1[TT, RR] diff --git a/test/files/run/t3452e/B.java b/test/files/run/t3452e/B.java new file mode 100644 index 0000000000..0268af9987 --- /dev/null +++ b/test/files/run/t3452e/B.java @@ -0,0 +1,2 @@ +class B extends C1<String, String> { +} diff --git a/test/files/run/t3452e/Test.scala b/test/files/run/t3452e/Test.scala new file mode 100644 index 0000000000..fc175bf94a --- /dev/null +++ b/test/files/run/t3452e/Test.scala @@ -0,0 +1,3 @@ +object Test extends App { + new B +} diff --git a/test/files/run/t3452f.scala b/test/files/run/t3452f.scala new file mode 100644 index 0000000000..af64f5c042 --- /dev/null +++ b/test/files/run/t3452f.scala @@ -0,0 +1,19 @@ +import language.higherKinds + +trait GenSet[A] + +trait GenSetTemplate[A, +CC[X] <: GenSet[X]] { + def empty: CC[A] = ??? +} + +trait SetLike[A, +This <: SetLike[A, This] with Set[A]] { + def empty: This +} + +abstract class Set[A] extends GenSet[A] with SetLike[A,Set[A]] with GenSetTemplate[A,Set] + +object Test { + def main(args: Array[String]): Unit = { + locally(classOf[Set[_]]) // trigger classloading to verify class + } +} diff --git a/test/files/run/t3452g/A.scala b/test/files/run/t3452g/A.scala new file mode 100644 index 0000000000..a3f74c1e1e --- /dev/null +++ b/test/files/run/t3452g/A.scala @@ -0,0 +1,9 @@ +trait TraversableLike[A, Repr] { + def tail: Repr = null.asInstanceOf[Repr] +} + +abstract class AbstractTrav[A] extends TraversableLike[A, AbstractTrav[A]] + +object O extends AbstractTrav[String] + +class C[A] extends AbstractTrav[A] diff --git a/test/files/run/t3452g/Test.java b/test/files/run/t3452g/Test.java new file mode 100644 index 0000000000..c3b4222d16 --- /dev/null +++ b/test/files/run/t3452g/Test.java @@ -0,0 +1,14 @@ + +public class Test { + public static void main(String[] args) { + // To get better types here, we would need to + // add bridge during mixin so we can expose + // a generic return type of Traversable<A>, because the erasure + // of this (Traversable) differs from the erasure of the mixed + // method (erasure(Repr) = Object) + + Object lsSharp = O.tail(); + + Object lsSharp2 = new C<String>().tail(); + } +} diff --git a/test/files/run/t3452h.scala b/test/files/run/t3452h.scala new file mode 100644 index 0000000000..6237d3ea64 --- /dev/null +++ b/test/files/run/t3452h.scala @@ -0,0 +1,8 @@ +class Mix___eFoo_I_wBar__f extends Foo_I_ with Bar__f { f; } +trait T +abstract class Foo_I_ { class I extends T ; def f: I ; f; } +trait Bar__f { type I>:Null<:T; def f: I = {null}; f; def gobble: I = {null}} + +object Test extends App { + new Mix___eFoo_I_wBar__f +} diff --git a/test/files/run/t6554.check b/test/files/run/t6554.check new file mode 100644 index 0000000000..6e0af7b474 --- /dev/null +++ b/test/files/run/t6554.check @@ -0,0 +1 @@ +public java.lang.Object Bar.minBy(java.lang.Object) / public java.lang.Object Bar.minBy(java.lang.Object) diff --git a/test/files/run/t6554.scala b/test/files/run/t6554.scala new file mode 100644 index 0000000000..5d29d16666 --- /dev/null +++ b/test/files/run/t6554.scala @@ -0,0 +1,11 @@ +trait Foo[A] { + def minBy[B](b: B): A = ??? +} + +class Bar extends Foo[Int] + +object Test extends App { + val sigs = classOf[Bar].getDeclaredMethods.map(m => s"${m.toString} / ${m.toGenericString}").sorted + println(sigs.mkString("\n")) +} +// Was public java.lang.Object Bar.minBy(java.lang.Object) / public <B> int Bar.minBy(B) diff --git a/test/files/run/t6908.scala b/test/files/run/t6908.scala new file mode 100644 index 0000000000..a641de96b9 --- /dev/null +++ b/test/files/run/t6908.scala @@ -0,0 +1,6 @@ +object Test { + def main(args: Array[String]) { + val set = collection.mutable.Set("1", null, "3").par + assert( set exists (_ eq null) ) + } +} diff --git a/test/files/run/t7185.check b/test/files/run/t7185.check index ebf85b731f..2b4adf36b4 100644 --- a/test/files/run/t7185.check +++ b/test/files/run/t7185.check @@ -24,9 +24,7 @@ tree: reflect.runtime.universe.Apply = scala> {val tb = reflect.runtime.currentMirror.mkToolBox(); tb.typecheck(tree): Any} res0: Any = { - { - $read.O.apply() - } + $read.O.apply() } scala> diff --git a/test/files/run/t7374.check b/test/files/run/t7374.check new file mode 100644 index 0000000000..4efa6f7af3 --- /dev/null +++ b/test/files/run/t7374.check @@ -0,0 +1,3 @@ +List(2, 3) +ParVector(1, 2, 3) +List(1, 2) diff --git a/test/files/run/t7374/Some.scala b/test/files/run/t7374/Some.scala new file mode 100644 index 0000000000..3266a5642e --- /dev/null +++ b/test/files/run/t7374/Some.scala @@ -0,0 +1,3 @@ +object SomeScala { + def list = List(1, 2, 3) +} diff --git a/test/files/run/t7374/Test.java b/test/files/run/t7374/Test.java new file mode 100644 index 0000000000..02f86146ca --- /dev/null +++ b/test/files/run/t7374/Test.java @@ -0,0 +1,7 @@ +public class Test { + public static void main(String[] args) { + System.out.println(SomeScala.list().tail()); + System.out.println(SomeScala.list().par()); + System.out.println(SomeScala.list().init()); + } +} diff --git a/test/files/run/t7711-script-args.check b/test/files/run/t7711-script-args.check new file mode 100644 index 0000000000..d107590a8a --- /dev/null +++ b/test/files/run/t7711-script-args.check @@ -0,0 +1,2 @@ +Hello, scripted test! +What good news have you for me today? diff --git a/test/files/run/t7711-script-args.scala b/test/files/run/t7711-script-args.scala new file mode 100644 index 0000000000..02535aa954 --- /dev/null +++ b/test/files/run/t7711-script-args.scala @@ -0,0 +1,7 @@ + +import scala.tools.partest.ScriptTest + +object Test extends ScriptTest { + override def extraSettings = s"${super.extraSettings} -Xlint" + override def argv = Seq("good", "news") +} diff --git a/test/files/run/t7711-script-args.script b/test/files/run/t7711-script-args.script new file mode 100644 index 0000000000..19b7a74924 --- /dev/null +++ b/test/files/run/t7711-script-args.script @@ -0,0 +1,12 @@ +#!/bin/bash +exec ${SCALA_HOME}/bin/scala "$0" "$@" 2>&1 +!# + +Console println s"Hello, scripted test!" +Console println s"What ${args mkString " "} have you for me today?" + +//def unused = 88 +//newSource1.scala:8: warning: private method in <$anon: AnyRef> is never used +//Console println s"Hello, $argv, are you still here?" +//newSource1.scala:9: error: not found: value argv + diff --git a/test/files/run/t8266-octal-interp.check b/test/files/run/t8266-octal-interp.check new file mode 100644 index 0000000000..6e9454119b --- /dev/null +++ b/test/files/run/t8266-octal-interp.check @@ -0,0 +1,30 @@ +t8266-octal-interp.scala:4: warning: Octal escape literals are deprecated, use \b instead. + f"a\10c", + ^ +t8266-octal-interp.scala:5: warning: Octal escape literals are deprecated, use \t instead. + f"a\11c", + ^ +t8266-octal-interp.scala:6: warning: Octal escape literals are deprecated, use \n instead. + f"a\12c", + ^ +t8266-octal-interp.scala:7: warning: Octal escape literals are deprecated, use \r instead. + f"a\15c", + ^ +t8266-octal-interp.scala:8: warning: Octal escape literals are deprecated, use \u0022 instead. + f"a\42c", + ^ +t8266-octal-interp.scala:9: warning: Octal escape literals are deprecated, use \\ instead. + f"a\134c", + ^ +t8266-octal-interp.scala:10: warning: Octal escape literals are deprecated, use \u0069 instead. + f"a\15151515c" + ^ +ac +a c +a +c +a +c +a"c +a\c +ai51515c diff --git a/test/files/run/t8266-octal-interp.flags b/test/files/run/t8266-octal-interp.flags new file mode 100644 index 0000000000..dcc59ebe32 --- /dev/null +++ b/test/files/run/t8266-octal-interp.flags @@ -0,0 +1 @@ +-deprecation diff --git a/test/files/run/t8266-octal-interp.scala b/test/files/run/t8266-octal-interp.scala new file mode 100644 index 0000000000..f85ae0367d --- /dev/null +++ b/test/files/run/t8266-octal-interp.scala @@ -0,0 +1,16 @@ + +trait X { + def f = Seq( + f"a\10c", + f"a\11c", + f"a\12c", + f"a\15c", + f"a\42c", + f"a\134c", + f"a\15151515c" + ) +} + +object Test extends App with X { + f foreach println +} diff --git a/test/files/run/typecheck.check b/test/files/run/typecheck.check new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/files/run/typecheck.check diff --git a/test/files/run/typecheck/Macros_1.scala b/test/files/run/typecheck/Macros_1.scala new file mode 100644 index 0000000000..ee1c8da763 --- /dev/null +++ b/test/files/run/typecheck/Macros_1.scala @@ -0,0 +1,12 @@ +import scala.reflect.macros.whitebox._ +import scala.language.experimental.macros + +object Macros { + def impl(c: Context) = { + import c.universe._ + c.typecheck(q"class C") + q"()" + } + + def foo: Any = macro impl +}
\ No newline at end of file diff --git a/test/files/run/typecheck/Test_2.scala b/test/files/run/typecheck/Test_2.scala new file mode 100644 index 0000000000..01bf5198cc --- /dev/null +++ b/test/files/run/typecheck/Test_2.scala @@ -0,0 +1,10 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} +import scala.tools.reflect.ToolBox + +object Test extends App { + Macros.foo + + val tb = cm.mkToolBox() + tb.typecheck(q"class C") +}
\ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala index 0e0e70fd62..7e846bfb24 100644 --- a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala @@ -7,21 +7,22 @@ object DefinitionConstructionProps with TraitConstruction with TypeDefConstruction with ValDefConstruction + with PatDefConstruction with DefConstruction - with PackageConstruction + with PackageConstruction with ImportConstruction { - property("SI-6842") = test { - val x: Tree = q"val x: Int" - assertEqAst(q"def f($x) = 0", "def f(x: Int) = 0") - assertEqAst(q"class C($x)", "class C(val x: Int)") - assertEqAst(q"class C { $x => }", "class C { x: Int => }") - assertEqAst(q"trait B { $x => }", "trait B { x: Int => }") - assertEqAst(q"object A { $x => }", "object A { x: Int => }") - val t: Tree = q"type T" - assertEqAst(q"def f[$t] = 0", "def f[T] = 0") - assertEqAst(q"class C[$t]", "class C[T]") - assertEqAst(q"trait B[$t]", "trait B[T]") - } + + val x: Tree = q"val x: Int" + property("SI-6842 a1") = test { assertEqAst(q"def f($x) = 0", "def f(x: Int) = 0") } + property("SI-6842 a2") = test { assertEqAst(q"class C($x)", "class C(val x: Int)") } + property("SI-6842 a3") = test { assertEqAst(q"class C { $x => }", "class C { x: Int => }") } + property("SI-6842 a4") = test { assertEqAst(q"trait B { $x => }", "trait B { x: Int => }") } + property("SI-6842 a5") = test { assertEqAst(q"object A { $x => }", "object A { x: Int => }") } + + val t: Tree = q"type T" + property("SI-6842 b1") = test { assertEqAst(q"def f[$t] = 0", "def f[T] = 0") } + property("SI-6842 b2") = test { assertEqAst(q"class C[$t]", "class C[T]") } + property("SI-6842 b3") = test { assertEqAst(q"trait B[$t]", "trait B[T]") } } trait ClassConstruction { self: QuasiquoteProperties => @@ -200,13 +201,54 @@ trait TypeDefConstruction { self: QuasiquoteProperties => } trait ValDefConstruction { self: QuasiquoteProperties => - property("splice term name into val") = forAll { (name: TermName, tpt: Tree, rhs: Tree) => + property("splice into val") = forAll { (name: TermName, tpt: Tree, rhs: Tree) => q"val $name: $tpt = $rhs" ≈ ValDef(Modifiers(), name, tpt, rhs) } - property("splice term name into var") = forAll { (name: TermName, tpt: Tree, rhs: Tree) => + property("splice into var") = forAll { (name: TermName, tpt: Tree, rhs: Tree) => q"var $name: $tpt = $rhs" ≈ ValDef(Modifiers(MUTABLE), name, tpt, rhs) } + + // left tree is not a pattern due to Si-8211 + property("SI-8202") = test { + assertEqAst(q"val (x: Int) = 1", "val x: Int = 1") + } +} + +trait PatDefConstruction { self: QuasiquoteProperties => + property("splice pattern into pat def") = test { + val pat = pq"(a, b)" + assertEqAst(q"val $pat = (1, 2)", "val (a, b) = (1, 2)") + val tpt = tq"(Int, Int)" + assertEqAst(q"val $pat: $tpt = (1, 2)", "val (a, b): (Int, Int) = (1, 2)") + } + + property("splice pattern into pat def within other pattern (1)") = test { + val pat = pq"(a, b)" + assertEqAst(q"val Foo($pat) = Foo((1, 2))", "val Foo((a, b)) = Foo((1, 2))") + val tpt = tq"Foo" + assertEqAst(q"val Foo($pat): $tpt = Foo((1, 2))", "val Foo((a, b)): Foo = Foo((1, 2))") + } + + property("splice patterns into pat def within other pattern (2)") = test { + val pat1 = pq"(a, b)"; val pat2 = pq"(c, d)" + assertEqAst(q"val ($pat1, $pat2) = ((1, 2), (3, 4))", "val ((a, b), (c, d)) = ((1, 2), (3, 4))") + val tpt = tq"((Int, Int), (Int, Int))" + assertEqAst(q"val ($pat1, $pat2): $tpt = ((1, 2), (3, 4))", "val ((a, b), (c, d)): ((Int, Int), (Int, Int)) = ((1, 2), (3, 4))") + } + + property("splice pattern without free vars into pat def") = test { + val pat = pq"((1, 2), 3)" + assertEqAst(q"val $pat = ((1, 2), 3)", "{ val ((1, 2), 3) = ((1, 2), 3) }") + val tpt = tq"((Int, Int), Int)" + assertEqAst(q"val $pat: $tpt = ((1, 2), 3)","{ val ((1, 2), 3): ((Int, Int), Int) = ((1, 2), 3) }") + } + + // won't result into pattern match due to SI-8211 + property("splice typed pat into pat def") = test { + val pat = pq"x: Int" + assertEqAst(q"val $pat = 2", "{ val x: Int = 2 }") + } } trait MethodConstruction { self: QuasiquoteProperties => diff --git a/test/files/scalacheck/quasiquotes/LiftableProps.scala b/test/files/scalacheck/quasiquotes/LiftableProps.scala index bd631b8734..20cfcbe139 100644 --- a/test/files/scalacheck/quasiquotes/LiftableProps.scala +++ b/test/files/scalacheck/quasiquotes/LiftableProps.scala @@ -99,7 +99,6 @@ object LiftableProps extends QuasiquoteProperties("liftable") { } property("lift tuple") = test { - assert(q"${Tuple1(1)}" ≈ q"scala.Tuple1(1)") assert(q"${(1, 2)}" ≈ q"(1, 2)") assert(q"${(1, 2, 3)}" ≈ q"(1, 2, 3)") assert(q"${(1, 2, 3, 4)}" ≈ q"(1, 2, 3, 4)") diff --git a/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala b/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala index 2600b0c120..6132244227 100644 --- a/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala +++ b/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala @@ -28,7 +28,7 @@ trait Helpers { override def transform(tree: Tree): Tree = tree match { case Ident(SimplifiedName(name)) => Ident(name) - case ValDef(mods, SimplifiedName(name), tpt, rhs) => ValDef(mods, name, tpt, rhs) + case ValDef(mods, SimplifiedName(name), tpt, rhs) => ValDef(mods, name, transform(tpt), transform(rhs)) case Bind(SimplifiedName(name), rhs) => Bind(name, rhs) case _ => super.transform(tree) diff --git a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala index 400e1ac9fd..4dbf746cfe 100644 --- a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala @@ -139,6 +139,11 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { assert(q"(..$empty)" ≈ q"()") } + property("splice single element list into tuple") = test { + val xs = q"x" :: Nil + assert(q"(..$xs)" ≈ xs.head) + } + property("function param flags are the same") = test { val xy = q"val x: A" :: q"val y: B" :: Nil assertEqAst(q"(..$xy) => x + y", "(x: A, y: B) => x + y") diff --git a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala index 148bb383b0..7c9b5ead20 100644 --- a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala @@ -82,6 +82,11 @@ object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction assert(last ≈ q"d") } + property("deconstruct expr as tuple") = test { + val q"(..$elems)" = q"foo" + assert(elems ≈ List(q"foo")) + } + property("deconstruct cases") = test { val q"$x match { case ..$cases }" = q"x match { case 1 => case 2 => }" assert(x ≈ q"x") diff --git a/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala index ea9f734a0b..07875af326 100644 --- a/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala @@ -18,6 +18,11 @@ object TypeConstructionProps extends QuasiquoteProperties("type construction") assert(tq"(t0, ..$ts)" ≈ tq"scala.Tuple3[t0, t1, t2]") } + property("single-element tuple type") = test { + val ts = q"T" :: Nil + assert(tq"(..$ts)" ≈ ts.head) + } + property("refined type") = test { val stats = q"def foo" :: q"val x: Int" :: q"type Y = String" :: Nil assert(tq"T { ..$stats }" ≈ tq"T { def foo; val x: Int; type Y = String }") diff --git a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala index 0fdcc19052..8ec1779353 100644 --- a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala @@ -35,6 +35,11 @@ object TypeDeconstructionProps extends QuasiquoteProperties("type deconstruction assert(last ≈ tq"t2") } + property("tuple type (5)") = test { + val tq"(..$ts)" = tq"T" + assert(ts ≈ List(tq"T")) + } + property("refined type") = test { val tq"T { ..$stats }" = tq"T { def foo; val x: Int; type Y = String }" assert(stats ≈ List(q"def foo", q"val x: Int", q"type Y = String")) diff --git a/test/files/scalacheck/quasiquotes/UnliftableProps.scala b/test/files/scalacheck/quasiquotes/UnliftableProps.scala index 8b827c98ff..4e996c90d7 100644 --- a/test/files/scalacheck/quasiquotes/UnliftableProps.scala +++ b/test/files/scalacheck/quasiquotes/UnliftableProps.scala @@ -111,8 +111,6 @@ object UnliftableProps extends QuasiquoteProperties("unliftable") { } property("unlift tuple") = test { - // fails due to SI-8045 - // val q"${t1: Tuple1[Int]}" = q"_root_.scala.Tuple1(1)" val q"${t2: (Int, Int)}" = q"(1, 2)" val q"${t3: (Int, Int, Int)}" = q"(1, 2, 3)" val q"${t4: (Int, Int, Int, Int)}" = q"(1, 2, 3, 4)" diff --git a/test/junit/scala/collection/SetMapConsistencyTest.scala b/test/junit/scala/collection/SetMapConsistencyTest.scala index 0d6f43db06..eed6007eef 100644 --- a/test/junit/scala/collection/SetMapConsistencyTest.scala +++ b/test/junit/scala/collection/SetMapConsistencyTest.scala @@ -507,4 +507,11 @@ class SetMapConsistencyTest { case _ => true }) } + + @Test + def testSI8264() { + val hs = Set(-2147483648, 1, -45023380, -1, 1971207058, -54312241, -234243394) - -1 + assert( hs.toList.toSet == hs ) + assert( hs == hs.toList.toSet ) + } } diff --git a/test/pending/run/macro-expand-default.flags b/test/pending/run/macro-expand-default.flags deleted file mode 100644 index cd66464f2f..0000000000 --- a/test/pending/run/macro-expand-default.flags +++ /dev/null @@ -1 +0,0 @@ --language:experimental.macros
\ No newline at end of file diff --git a/test/pending/run/macro-expand-default/Impls_1.scala b/test/pending/run/macro-expand-default/Impls_1.scala deleted file mode 100644 index fd5d8d7f18..0000000000 --- a/test/pending/run/macro-expand-default/Impls_1.scala +++ /dev/null @@ -1,10 +0,0 @@ -import scala.reflect.macros.blackbox.Context - -object Impls { - def foo(c: Context)(x: c.Expr[Int], y: c.Expr[Int]) = { - import c.universe._ - val sum = Apply(Select(x.tree, TermName("$minus")), List(y.tree)) - val body = Apply(Select(Ident(definitions.PredefModule), TermName("println")), List(sum)) - Expr[Unit](body) - } -}
\ No newline at end of file diff --git a/test/pending/run/macro-expand-default/Macros_Test_2.scala b/test/pending/run/macro-expand-default/Macros_Test_2.scala deleted file mode 100644 index 92fe84d04a..0000000000 --- a/test/pending/run/macro-expand-default/Macros_Test_2.scala +++ /dev/null @@ -1,8 +0,0 @@ -object Test extends App { - def foo(x: Int = 2, y: Int = -40) = macro Impls.foo - foo(y = -40, x = 2) - foo(x = 2, y = -40) - foo(x = 100) - foo(y = 100) - foo() -}
\ No newline at end of file diff --git a/test/pending/run/macro-expand-named.flags b/test/pending/run/macro-expand-named.flags deleted file mode 100644 index cd66464f2f..0000000000 --- a/test/pending/run/macro-expand-named.flags +++ /dev/null @@ -1 +0,0 @@ --language:experimental.macros
\ No newline at end of file diff --git a/test/pending/run/macro-expand-named/Impls_1.scala b/test/pending/run/macro-expand-named/Impls_1.scala deleted file mode 100644 index fd5d8d7f18..0000000000 --- a/test/pending/run/macro-expand-named/Impls_1.scala +++ /dev/null @@ -1,10 +0,0 @@ -import scala.reflect.macros.blackbox.Context - -object Impls { - def foo(c: Context)(x: c.Expr[Int], y: c.Expr[Int]) = { - import c.universe._ - val sum = Apply(Select(x.tree, TermName("$minus")), List(y.tree)) - val body = Apply(Select(Ident(definitions.PredefModule), TermName("println")), List(sum)) - Expr[Unit](body) - } -}
\ No newline at end of file diff --git a/test/pending/run/macro-expand-named/Macros_Test_2.scala b/test/pending/run/macro-expand-named/Macros_Test_2.scala deleted file mode 100644 index abebcf8448..0000000000 --- a/test/pending/run/macro-expand-named/Macros_Test_2.scala +++ /dev/null @@ -1,5 +0,0 @@ -object Test extends App { - def foo(x: Int, y: Int) = macro Impls.foo - foo(y = -40, x = 2) - foo(x = 2, y = -40) -}
\ No newline at end of file diff --git a/test/support/java-tests.txt b/test/support/java-tests.txt new file mode 100644 index 0000000000..e0a3fddab3 --- /dev/null +++ b/test/support/java-tests.txt @@ -0,0 +1,97 @@ +test/files/buildmanager/t2280 +test/files/buildmanager/t3045 +test/files/buildmanager/t3133 +test/files/jvm/deprecation +test/files/jvm/t1143-2 +test/files/jvm/t1342 +test/files/jvm/t1464 +test/files/jvm/t2470 +test/files/jvm/t2570 +test/files/jvm/t2585 +test/files/jvm/t3003 +test/files/jvm/t3415 +test/files/jvm/ticket2163 +test/files/jvm/ticket4283 +test/files/jvm/varargs +test/files/neg/abstract-class-error +test/files/neg/java-access-neg +test/files/neg/primitive-sigs-1 +test/files/neg/protected-static-fail +test/files/neg/t0673 +test/files/neg/t1548 +test/files/neg/t3663 +test/files/neg/t3757 +test/files/neg/t4851 +test/files/pos/chang +test/files/pos/ilya +test/files/pos/ilya2 +test/files/pos/java-access-pos +test/files/pos/javaReadsSigs +test/files/pos/protected-static +test/files/pos/raw-map +test/files/pos/signatures +test/files/pos/super +test/files/pos/t0288 +test/files/pos/t0695 +test/files/pos/t1101 +test/files/pos/t1102 +test/files/pos/t1150 +test/files/pos/t1152 +test/files/pos/t1176 +test/files/pos/t1186 +test/files/pos/t1196 +test/files/pos/t1197 +test/files/pos/t1203 +test/files/pos/t1230 +test/files/pos/t1231 +test/files/pos/t1232 +test/files/pos/t1235 +test/files/pos/t1254 +test/files/pos/t1263 +test/files/pos/t1409 +test/files/pos/t1459 +test/files/pos/t1642 +test/files/pos/t1711 +test/files/pos/t1745 +test/files/pos/t1751 +test/files/pos/t1782 +test/files/pos/t1836 +test/files/pos/t1840 +test/files/pos/t1937 +test/files/pos/t2377 +test/files/pos/t2409 +test/files/pos/t2413 +test/files/pos/t2433 +test/files/pos/t2464 +test/files/pos/t2569 +test/files/pos/t2868 +test/files/pos/t294 +test/files/pos/t2940 +test/files/pos/t2956 +test/files/pos/t3249 +test/files/pos/t3349 +test/files/pos/t3404 +test/files/pos/t3429 +test/files/pos/t3486 +test/files/pos/t3521 +test/files/pos/t3567 +test/files/pos/t3622 +test/files/pos/t3642 +test/files/pos/t3938 +test/files/pos/t3946 +test/files/pos/t4402 +test/files/pos/t4603 +test/files/pos/t4737 +test/files/pos/t5644 +test/files/pos/t5703 +test/files/run/inner-parse +test/files/run/t1430 +test/files/run/t2296a +test/files/run/t2296b +test/files/run/t3452a +test/files/run/t3452b +test/files/run/t3897 +test/files/run/t4119 +test/files/run/t4238 +test/files/run/t4317 +test/files/run/t4891 diff --git a/tools/compare-java-sigs b/tools/compare-java-sigs new file mode 100644 index 0000000000..99ab775437 --- /dev/null +++ b/tools/compare-java-sigs @@ -0,0 +1,56 @@ +#!/bin/sh +# +# Compare javac -Xprint (i.e. see signatures from java point of view) +# for the given classes. +# +# Sample: +# +# % SCALA_HOME=/scala/inst/29 SCALA_BUILD=/scala/inst/3 tools/compare-java-sigs 'scala.Predef$' +# +# Comparing javac -Xprint for scala.Predef$ based on '/scala/inst/29' and '/scala/inst/3' +# 3c3 +# < public final class Predef$ extends scala.LowPriorityImplicits implements scala.ScalaObject { +# --- +# > public final class Predef$ extends scala.LowPriorityImplicits { +# 7d6 +# < private final scala.SpecializableCompanion AnyRef; +# 21,22d19 +# < public scala.SpecializableCompanion AnyRef(); +# < +# 68a66,67 +# > public scala.runtime.Nothing$ $qmark$qmark$qmark(); +# > +# 225c224,226 +# < public scala.collection.immutable.StringOps augmentString(java.lang.String x); +# --- +# > public scala.runtime.StringFormat any2stringfmt(java.lang.Object x); +# > +# > public java.lang.String augmentString(java.lang.String x); +# 227c228 +# < public java.lang.String unaugmentString(scala.collection.immutable.StringOps x); +# --- +# > public java.lang.String unaugmentString(java.lang.String x); +# + +set -e + +[[ $# -gt 0 ]] || { + echo "Usage: $(basename $0) <class> <class> ..." + echo "" + echo "# Example usage" + echo "SCALA_HOME=/scala/inst/29 SCALA_BUILD=/scala/inst/3 \\" + echo " $(basename $0) scala.Function1 scala.runtime.AbstractFunction1" + exit 0 +} + +home1=$(cd ${SCALA_HOME:-/scala/inst/3} && pwd) +home2=$(cd ${SCALA_BUILD:-$(dirname $BASH_SOURCE)/../build/pack} && pwd) + +echo "Comparing javac -Xprint for $@ based on '$home1' and '$home2'" +tmpdir=$(mktemp -dt $(basename $BASH_SOURCE)) + +cd $tmpdir +javac -Xprint -cp $home1:$home1/lib/'*' "$@" > before.txt +javac -Xprint -cp $home2:$home2/lib/'*' "$@" > after.txt + +diff before.txt after.txt && echo "No differences in javac -Xprint output." |