From 488444b327f222fa4ef317b5d96fb7cdf732d4d2 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Mon, 3 Jun 2013 21:51:39 +0200 Subject: cleans up 82f0925 Updates comments, implements accidentally forgotten IMPLPARAM_TREE, creates a test to ensure that nothing else is overseen. --- .../tools/nsc/typechecker/ContextErrors.scala | 2 +- .../scala/tools/nsc/typechecker/Macros.scala | 27 ++++++++++++++-------- test/files/run/macro-impl-relaxed.check | 4 ++++ test/files/run/macro-impl-relaxed/Macros_1.scala | 14 +++++++++++ test/files/run/macro-impl-relaxed/Test_2.scala | 6 +++++ 5 files changed, 42 insertions(+), 11 deletions(-) create mode 100644 test/files/run/macro-impl-relaxed.check create mode 100644 test/files/run/macro-impl-relaxed/Macros_1.scala create mode 100644 test/files/run/macro-impl-relaxed/Test_2.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index fe1607c631..7fa199afaf 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -782,7 +782,7 @@ trait ContextErrors { } def MacroExpansionHasInvalidTypeError(expandee: Tree, expanded: Any) = { - val expected = "expr" + val expected = "expr or tree" val isPathMismatch = expanded != null && expanded.isInstanceOf[scala.reflect.api.Exprs#Expr[_]] macroExpansionError(expandee, s"macro must return a compiler-specific $expected; returned value is " + ( diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 6c4d1e20aa..86ba3d2164 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -92,11 +92,12 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { methName: String, // flattens the macro impl's parameter lists having symbols replaced with their fingerprints // currently fingerprints are calculated solely from types of the symbols: - // * c.Expr[T] => IMPLPARAM_EXPR - // * c.WeakTypeTag[T] => index of the type parameter corresponding to that type tag - // * everything else (e.g. scala.reflect.macros.Context) => IMPLPARAM_OTHER + // * c.Expr[T] => LiftedTyped + // * c.Tree => LiftedUntyped + // * c.WeakTypeTag[T] => Tagged(index of the type parameter corresponding to that type tag) + // * everything else (e.g. scala.reflect.macros.Context) => Other // f.ex. for: def impl[T: WeakTypeTag, U, V: WeakTypeTag](c: Context)(x: c.Expr[T], y: c.Tree): (U, V) = ??? - // `signature` will be equal to List(List(Other), List(Lifted, Other), List(Tagged(0), Tagged(2))) + // `signature` will be equal to List(List(Other), List(LiftedTyped, LiftedUntyped), List(Tagged(0), Tagged(2))) signature: List[List[Fingerprint]], // type arguments part of a macro impl ref (the right-hand side of a macro definition) // these trees don't refer to a macro impl, so we can pickle them as is @@ -124,7 +125,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { * "className" = "Macros$")) */ object MacroImplBinding { - val versionFormat = 4.0 + val versionFormat = 5.0 def pickleAtom(obj: Any): Tree = obj match { @@ -164,7 +165,8 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { def signature: List[List[Fingerprint]] = { def fingerprint(tpe: Type): Fingerprint = tpe.dealiasWiden match { case TypeRef(_, RepeatedParamClass, underlying :: Nil) => fingerprint(underlying) - case ExprClassOf(_) => Lifted + case ExprClassOf(_) => LiftedTyped + case TreeType() => LiftedUntyped case _ => Other } @@ -388,7 +390,8 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { val wrappedArgs = mapWithIndex(args)((arg, j) => { val fingerprint = implParams(min(j, implParams.length - 1)) fingerprint match { - case Lifted => context.Expr[Nothing](arg)(TypeTag.Nothing) // TODO: SI-5752 + case LiftedTyped => context.Expr[Nothing](arg)(TypeTag.Nothing) // TODO: SI-5752 + case LiftedUntyped => arg case _ => abort(s"unexpected fingerprint $fingerprint in $binding with paramss being $paramss " + s"corresponding to arg $arg in $argss") } @@ -690,6 +693,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { } expanded match { case expanded: Expr[_] if expandee.symbol.isTermMacro => validateResultingTree(expanded.tree) + case expanded: Tree if expandee.symbol.isTermMacro => validateResultingTree(expanded) case _ => MacroExpansionHasInvalidTypeError(expandee, expanded) } } catch { @@ -804,10 +808,12 @@ class Fingerprint(val value: Int) extends AnyVal { def paramPos = { assert(isTag, this); value } def isTag = value >= 0 def isOther = this == Other - def isExpr = this == Lifted + def isExpr = this == LiftedTyped + def isTree = this == LiftedUntyped override def toString = this match { case Other => "Other" - case Lifted => "Expr" + case LiftedTyped => "Expr" + case LiftedUntyped => "Tree" case _ => s"Tag($value)" } } @@ -815,5 +821,6 @@ class Fingerprint(val value: Int) extends AnyVal { object Fingerprint { def Tagged(tparamPos: Int) = new Fingerprint(tparamPos) val Other = new Fingerprint(-1) - val Lifted = new Fingerprint(-2) + val LiftedTyped = new Fingerprint(-2) + val LiftedUntyped = new Fingerprint(-3) } diff --git a/test/files/run/macro-impl-relaxed.check b/test/files/run/macro-impl-relaxed.check new file mode 100644 index 0000000000..487b116534 --- /dev/null +++ b/test/files/run/macro-impl-relaxed.check @@ -0,0 +1,4 @@ +2 +2 +2 +2 diff --git a/test/files/run/macro-impl-relaxed/Macros_1.scala b/test/files/run/macro-impl-relaxed/Macros_1.scala new file mode 100644 index 0000000000..af62646b4e --- /dev/null +++ b/test/files/run/macro-impl-relaxed/Macros_1.scala @@ -0,0 +1,14 @@ +import language.experimental.macros +import scala.reflect.macros.Context + +object Macros { + def implUU(c: Context)(x: c.Tree): c.Tree = x + def implTU(c: Context)(x: c.Expr[Int]): c.Tree = x.tree + def implUT(c: Context)(x: c.Tree): c.Expr[Int] = c.Expr[Int](x) + def implTT(c: Context)(x: c.Expr[Int]): c.Expr[Int] = x + + def fooUU(x: Int): Int = macro implUU + def fooTU(x: Int): Int = macro implTU + def fooUT(x: Int): Int = macro implUT + def fooTT(x: Int): Int = macro implTT +} \ No newline at end of file diff --git a/test/files/run/macro-impl-relaxed/Test_2.scala b/test/files/run/macro-impl-relaxed/Test_2.scala new file mode 100644 index 0000000000..2eaeef0fd0 --- /dev/null +++ b/test/files/run/macro-impl-relaxed/Test_2.scala @@ -0,0 +1,6 @@ +object Test extends App { + println(Macros.fooUU(2)) + println(Macros.fooTU(2)) + println(Macros.fooUT(2)) + println(Macros.fooTT(2)) +} \ No newline at end of file -- cgit v1.2.3 From ee646e9c84290e721c4ee9fe6247d4b95840e871 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Wed, 5 Jun 2013 16:51:06 +0200 Subject: fixes a crash on a degenerate macro definition Previous version of the MacroImplReference extractor didn't take into the account the fact that RefTree.qualifier.symbol can be null (and it can be null if RefTree is an Ident, because then qualifier is an EmptyTree). This led to NPEs for really weird macro defs that refer to local methods as their corresponding macro impls. Now I check for this corner case, and the stuff now longer crashes. This was wrong; this is how I fixed it; the world is now a better place. --- src/reflect/scala/reflect/internal/TreeInfo.scala | 7 ++++--- test/files/neg/macro-invalidshape.check | 7 ++++++- test/files/neg/macro-invalidshape/Macros_Test_2.scala | 5 +++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index d1e8a04553..3a8d3fd460 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -837,11 +837,12 @@ abstract class TreeInfo { def unapply(tree: Tree) = refPart(tree) match { case ref: RefTree => { - val isBundle = definitions.isMacroBundleType(ref.qualifier.tpe) + val qual = ref.qualifier + val isBundle = definitions.isMacroBundleType(qual.tpe) val owner = - if (isBundle) ref.qualifier.tpe.typeSymbol + if (isBundle) qual.tpe.typeSymbol else { - val sym = ref.qualifier.symbol + val sym = if (qual.hasSymbolField) qual.symbol else NoSymbol if (sym.isModule) sym.moduleClass else sym } Some((isBundle, owner, ref.symbol, dissectApplied(tree).targs)) diff --git a/test/files/neg/macro-invalidshape.check b/test/files/neg/macro-invalidshape.check index 40a2952569..1938f5ae47 100644 --- a/test/files/neg/macro-invalidshape.check +++ b/test/files/neg/macro-invalidshape.check @@ -12,4 +12,9 @@ Macros_Test_2.scala:4: error: missing arguments for method foo in object Impls; follow this method with `_' if you want to treat it as a partially applied function def foo3(x: Any) = macro {2; Impls.foo} ^ -three errors found +Macros_Test_2.scala:7: error: macro implementation reference has wrong shape. required: +macro [].[[]] or +macro [].[[]] + def foo = macro impl + ^ +four errors found diff --git a/test/files/neg/macro-invalidshape/Macros_Test_2.scala b/test/files/neg/macro-invalidshape/Macros_Test_2.scala index f39ad20c5d..cf37e14d8e 100644 --- a/test/files/neg/macro-invalidshape/Macros_Test_2.scala +++ b/test/files/neg/macro-invalidshape/Macros_Test_2.scala @@ -2,6 +2,11 @@ object Macros { def foo1(x: Any) = macro 2 def foo2(x: Any) = macro Impls.foo(null)(null) def foo3(x: Any) = macro {2; Impls.foo} + { + def impl(c: scala.reflect.macros.Context) = c.literalUnit + def foo = macro impl + foo + } } object Test extends App { -- cgit v1.2.3 From a45d3e5658a1ccb57d3b420eb36d84f7170404b5 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 28 May 2013 09:04:09 +0200 Subject: unifies handling of T's in various analyses of Foo[T]'s --- .../scala/reflect/macros/util/Helpers.scala | 6 +-- .../scala/reflect/internal/Definitions.scala | 48 ++++------------------ src/reflect/scala/reflect/internal/Types.scala | 38 +++++++++++++++++ 3 files changed, 50 insertions(+), 42 deletions(-) diff --git a/src/compiler/scala/reflect/macros/util/Helpers.scala b/src/compiler/scala/reflect/macros/util/Helpers.scala index ada8efa833..9b7680717e 100644 --- a/src/compiler/scala/reflect/macros/util/Helpers.scala +++ b/src/compiler/scala/reflect/macros/util/Helpers.scala @@ -54,7 +54,7 @@ trait Helpers { * * @see Metalevels.scala for more information and examples about metalevels */ - def increaseMetalevel(pre: Type, tp: Type): Type = dealiasAndRewrap(tp) { + def increaseMetalevel(pre: Type, tp: Type): Type = transparentShallowTransform(RepeatedParamClass, tp) { case tp => typeRef(pre, MacroContextExprClass, List(tp)) } @@ -64,8 +64,8 @@ trait Helpers { * * @see Metalevels.scala for more information and examples about metalevels */ - def decreaseMetalevel(tp: Type): Type = dealiasAndRewrap(tp) { + def decreaseMetalevel(tp: Type): Type = transparentShallowTransform(RepeatedParamClass, tp) { case ExprClassOf(runtimeType) => runtimeType - case _ => AnyClass.tpe // so that macro impls with rhs = ??? don't screw up our inference + case _ => AnyTpe // so that macro impls with rhs = ??? don't screw up our inference } } \ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 1b46d9e00b..5892053b5e 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -407,10 +407,6 @@ trait Definitions extends api.StandardDefinitions { lazy val JavaRepeatedParamClass = specialPolyClass(tpnme.JAVA_REPEATED_PARAM_CLASS_NAME, COVARIANT)(tparam => arrayType(tparam.tpe)) lazy val RepeatedParamClass = specialPolyClass(tpnme.REPEATED_PARAM_CLASS_NAME, COVARIANT)(tparam => seqType(tparam.tpe)) - def dropByName(tp: Type): Type = tp match { - case TypeRef(_, ByNameParamClass, arg :: Nil) => arg - case _ => tp - } def isByNameParamType(tp: Type) = tp.typeSymbol == ByNameParamClass def isScalaRepeatedParamType(tp: Type) = tp.typeSymbol == RepeatedParamClass def isJavaRepeatedParamType(tp: Type) = tp.typeSymbol == JavaRepeatedParamClass @@ -431,29 +427,15 @@ trait Definitions extends api.StandardDefinitions { case _ => false } - def repeatedToSingle(tp: Type): Type = tp match { - case TypeRef(_, RepeatedParamClass, arg :: Nil) => arg - case _ => tp - } - - def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match { - case TypeRef(_, RepeatedParamClass, arg :: Nil) => seqType(arg) - case _ => tp - } - - def seqToRepeated(tp: Type): Type = (tp baseType SeqClass) match { - case TypeRef(_, SeqClass, arg :: Nil) => scalaRepeatedType(arg) - case _ => tp - } - - def isReferenceArray(tp: Type) = tp match { - case TypeRef(_, ArrayClass, arg :: Nil) => arg <:< AnyRefTpe - case _ => false - } - def isArrayOfSymbol(tp: Type, elem: Symbol) = tp match { - case TypeRef(_, ArrayClass, arg :: Nil) => arg.typeSymbol == elem - case _ => false - } + // wrapping and unwrapping + def dropByName(tp: Type): Type = elementExtract(ByNameParamClass, tp) orElse tp + def repeatedToSingle(tp: Type): Type = elementExtract(RepeatedParamClass, tp) orElse tp + def repeatedToSeq(tp: Type): Type = elementTransform(RepeatedParamClass, tp)(seqType) orElse tp + def seqToRepeated(tp: Type): Type = elementTransform(SeqClass, tp)(scalaRepeatedType) orElse tp + def isReferenceArray(tp: Type) = elementTest(ArrayClass, tp)(_ <:< AnyRefTpe) + def isArrayOfSymbol(tp: Type, elem: Symbol) = elementTest(ArrayClass, tp)(_.typeSymbol == elem) + def elementType(container: Symbol, tp: Type): Type = elementExtract(container, tp) + object ExprClassOf { def unapply(tp: Type): Option[Type] = elementExtractOption(ExprClass, tp) } // collections classes lazy val ConsClass = requiredClass[scala.collection.immutable.::[_]] @@ -512,13 +494,6 @@ trait Definitions extends api.StandardDefinitions { lazy val ExprClass = ExprsClass.map(sym => getMemberClass(sym, tpnme.Expr)) def ExprSplice = ExprClass.map(sym => getMemberMethod(sym, nme.splice)) def ExprValue = ExprClass.map(sym => getMemberMethod(sym, nme.value)) - object ExprClassOf { - def unapply(tpe: Type): Option[Type] = tpe.dealias match { - case ExistentialType(_, underlying) => unapply(underlying) - case TypeRef(_, ExprClass, t :: Nil) => Some(t) - case _ => None - } - } lazy val ClassTagModule = requiredModule[scala.reflect.ClassTag[_]] lazy val ClassTagClass = requiredClass[scala.reflect.ClassTag[_]] @@ -707,11 +682,6 @@ trait Definitions extends api.StandardDefinitions { (sym eq PartialFunctionClass) || (sym eq AbstractPartialFunctionClass) } - def elementType(container: Symbol, tp: Type): Type = tp match { - case TypeRef(_, `container`, arg :: Nil) => arg - case _ => NoType - } - def arrayType(arg: Type) = appliedType(ArrayClass, arg) def byNameType(arg: Type) = appliedType(ByNameParamClass, arg) def iteratorOfType(tp: Type) = appliedType(IteratorClass, tp) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 00a929003e..f27e6c979a 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -2436,6 +2436,7 @@ trait Types else super.prefixString ) + def copy(pre: Type = this.pre, sym: Symbol = this.sym, args: List[Type] = this.args) = TypeRef(pre, sym, args) override def kind = "TypeRef" } @@ -3700,6 +3701,43 @@ trait Types object unwrapToStableClass extends ClassUnwrapper(existential = false) { } object unwrapWrapperTypes extends TypeUnwrapper(true, true, true, true) { } + def elementExtract(container: Symbol, tp: Type): Type = { + assert(!container.isAliasType, container) + unwrapWrapperTypes(tp baseType container).dealiasWiden match { + case TypeRef(_, `container`, arg :: Nil) => arg + case _ => NoType + } + } + def elementExtractOption(container: Symbol, tp: Type): Option[Type] = { + elementExtract(container, tp) match { + case NoType => None + case tp => Some(tp) + } + } + def elementTest(container: Symbol, tp: Type)(f: Type => Boolean): Boolean = { + elementExtract(container, tp) match { + case NoType => false + case tp => f(tp) + } + } + def elementTransform(container: Symbol, tp: Type)(f: Type => Type): Type = { + elementExtract(container, tp) match { + case NoType => NoType + case tp => f(tp) + } + } + + def transparentShallowTransform(container: Symbol, tp: Type)(f: Type => Type): Type = { + def loop(tp: Type): Type = tp match { + case tp @ AnnotatedType(_, underlying, _) => tp.copy(underlying = loop(underlying)) + case tp @ ExistentialType(_, underlying) => tp.copy(underlying = loop(underlying)) + case tp @ PolyType(_, resultType) => tp.copy(resultType = loop(resultType)) + case tp @ NullaryMethodType(resultType) => tp.copy(resultType = loop(resultType)) + case tp => elementTransform(container, tp)(el => appliedType(container, f(el))).orElse(f(tp)) + } + loop(tp) + } + /** Repack existential types, otherwise they sometimes get unpacked in the * wrong location (type inference comes up with an unexpected skolem) */ -- cgit v1.2.3