diff options
-rw-r--r-- | bincompat-backward.whitelist.conf | 12 | ||||
-rw-r--r-- | bincompat-forward.whitelist.conf | 24 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 9 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/StdAttachments.scala | 31 | ||||
-rw-r--r-- | test/files/run/t7617a.check | 2 | ||||
-rw-r--r-- | test/files/run/t7617a/Macros_1.scala | 22 | ||||
-rw-r--r-- | test/files/run/t7617a/Test_2.scala | 5 | ||||
-rw-r--r-- | test/files/run/t7617b.check | 1 | ||||
-rw-r--r-- | test/files/run/t7617b/Macros_1.scala | 8 | ||||
-rw-r--r-- | test/files/run/t7617b/Test_2.scala | 11 |
10 files changed, 122 insertions, 3 deletions
diff --git a/bincompat-backward.whitelist.conf b/bincompat-backward.whitelist.conf index 2ef4ae1cc1..267631f908 100644 --- a/bincompat-backward.whitelist.conf +++ b/bincompat-backward.whitelist.conf @@ -227,6 +227,18 @@ filter { { matchName="scala.reflect.internal.Definitions#DefinitionsClass.primitiveGetClassMethods" problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.StdAttachments.unsuppressMacroExpansion" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.StdAttachments.suppressMacroExpansion" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.StdAttachments.isMacroExpansionSuppressed" + problemName=MissingMethodProblem } ] } diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf index 284e0809a8..ec7e2fb6ba 100644 --- a/bincompat-forward.whitelist.conf +++ b/bincompat-forward.whitelist.conf @@ -479,6 +479,30 @@ filter { { matchName="scala.reflect.internal.Definitions#DefinitionsClass.primitiveGetClassMethods" problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.StdAttachments.unsuppressMacroExpansion" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.StdAttachments.suppressMacroExpansion" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.StdAttachments.isMacroExpansionSuppressed" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.SymbolTable.unsuppressMacroExpansion" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.SymbolTable.suppressMacroExpansion" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.SymbolTable.isMacroExpansionSuppressed" + problemName=MissingMethodProblem } ] } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ed2963fb0f..1a2e498bca 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1147,7 +1147,7 @@ trait Typers extends Modes with Adaptations with Tags { else if ( inExprModeButNot(mode, FUNmode) && !tree.isDef && // typechecking application tree.symbol != null && tree.symbol.isTermMacro && // of a macro - !tree.attachments.get[SuppressMacroExpansionAttachment.type].isDefined) + !isMacroExpansionSuppressed(tree)) macroExpand(this, tree, mode, pt) else if ((mode & (PATTERNmode | FUNmode)) == (PATTERNmode | FUNmode)) adaptConstrPattern() @@ -4057,6 +4057,7 @@ trait Typers extends Modes with Adaptations with Tags { findSelection(cxTree) match { case Some((opName, treeInfo.Applied(_, targs, _))) => val fun = gen.mkTypeApply(Select(qual, opName), targs) + if (opName == nme.updateDynamic) suppressMacroExpansion(fun) // SI-7617 atPos(qual.pos)(Apply(fun, Literal(Constant(name.decode)) :: Nil)) case _ => setError(tree) @@ -4229,7 +4230,9 @@ trait Typers extends Modes with Adaptations with Tags { } def typedAssign(lhs: Tree, rhs: Tree): Tree = { - val lhs1 = typed(lhs, EXPRmode | LHSmode, WildcardType) + // see SI-7617 for an explanation of why macro expansion is suppressed + def typedLhs(lhs: Tree) = typed(lhs, EXPRmode | LHSmode, WildcardType) + val lhs1 = unsuppressMacroExpansion(typedLhs(suppressMacroExpansion(lhs))) val varsym = lhs1.symbol // see #2494 for double error message example @@ -5352,7 +5355,7 @@ trait Typers extends Modes with Adaptations with Tags { // that typecheck must not trigger macro expansions, so we explicitly prohibit them // however we cannot do `context.withMacrosDisabled` // because `expr` might contain nested macro calls (see SI-6673) - val exprTyped = typed1(expr updateAttachment SuppressMacroExpansionAttachment, mode, pt) + val exprTyped = typed1(suppressMacroExpansion(expr), mode, pt) exprTyped match { case macroDef if macroDef.symbol != null && macroDef.symbol.isTermMacro && !macroDef.symbol.isErroneous => MacroEtaError(exprTyped) diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index b782353ed3..539d19140c 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -42,4 +42,35 @@ trait StdAttachments { * (but think thrice before using that API - see the discussion at https://github.com/scala/scala/pull/1639). */ case object SuppressMacroExpansionAttachment + + /** Suppresses macro expansion of the tree by putting SuppressMacroExpansionAttachment on it. + */ + def suppressMacroExpansion(tree: Tree) = tree.updateAttachment(SuppressMacroExpansionAttachment) + + /** Unsuppresses macro expansion of the tree by removing SuppressMacroExpansionAttachment from it and its children. + */ + def unsuppressMacroExpansion(tree: Tree): Tree = { + tree.removeAttachment[SuppressMacroExpansionAttachment.type] + tree match { + // see the comment to `isMacroExpansionSuppressed` to learn why we need + // a special traversal strategy here + case Apply(fn, _) => unsuppressMacroExpansion(fn) + case TypeApply(fn, _) => unsuppressMacroExpansion(fn) + case _ => // do nothing + } + tree + } + + /** Determines whether a tree should not be expanded, because someone has put SuppressMacroExpansionAttachment on it or one of its children. + */ + def isMacroExpansionSuppressed(tree: Tree): Boolean = + if (tree.attachments.get[SuppressMacroExpansionAttachment.type].isDefined) true + else tree match { + // 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 + case Apply(fn, _) => isMacroExpansionSuppressed(fn) + case TypeApply(fn, _) => isMacroExpansionSuppressed(fn) + case _ => false + } } diff --git a/test/files/run/t7617a.check b/test/files/run/t7617a.check new file mode 100644 index 0000000000..94954abda4 --- /dev/null +++ b/test/files/run/t7617a.check @@ -0,0 +1,2 @@ +hello +world diff --git a/test/files/run/t7617a/Macros_1.scala b/test/files/run/t7617a/Macros_1.scala new file mode 100644 index 0000000000..f9772c83c0 --- /dev/null +++ b/test/files/run/t7617a/Macros_1.scala @@ -0,0 +1,22 @@ +import scala.reflect.macros.Context +import language.experimental.macros + +object Macros { + def getValueImpl[T](c: Context): c.Expr[T] = { + import c.universe._ + c.Expr[T](Apply(Select(c.prefix.tree, newTermName("getVal")), Nil)) + } + def setValueImpl[T](c: Context)(value: c.Expr[T]): c.Expr[Unit] = { + import c.universe._ + c.Expr[Unit](Apply(Select(c.prefix.tree, newTermName("setVal")), List(value.tree))) + } +} + +object Module { + private var _val: String = "hello" + def setVal(value: String): Unit = this._val = value + def getVal(): String = this._val + + def value: String = macro Macros.getValueImpl[String] + def value_=(value: String): Unit = macro Macros.setValueImpl[String] +} diff --git a/test/files/run/t7617a/Test_2.scala b/test/files/run/t7617a/Test_2.scala new file mode 100644 index 0000000000..da6e34e09d --- /dev/null +++ b/test/files/run/t7617a/Test_2.scala @@ -0,0 +1,5 @@ +object Test extends App { + println(Module.value) + Module.value = "world" + println(Module.value) +}
\ No newline at end of file diff --git a/test/files/run/t7617b.check b/test/files/run/t7617b.check new file mode 100644 index 0000000000..81ec7e8b74 --- /dev/null +++ b/test/files/run/t7617b.check @@ -0,0 +1 @@ +foo = 2 diff --git a/test/files/run/t7617b/Macros_1.scala b/test/files/run/t7617b/Macros_1.scala new file mode 100644 index 0000000000..bc919935c9 --- /dev/null +++ b/test/files/run/t7617b/Macros_1.scala @@ -0,0 +1,8 @@ +import scala.reflect.macros.Context + +object Macros { + def impl(c: Context)(name: c.Expr[String])(value: c.Expr[Any]) = { + import c.universe._ + reify(println(s"${name.splice} = ${value.splice}")) + } +}
\ No newline at end of file diff --git a/test/files/run/t7617b/Test_2.scala b/test/files/run/t7617b/Test_2.scala new file mode 100644 index 0000000000..e27f650e80 --- /dev/null +++ b/test/files/run/t7617b/Test_2.scala @@ -0,0 +1,11 @@ +import scala.language.dynamics +import language.experimental.macros + +class C extends Dynamic { + def updateDynamic(name: String)(value: Any) = macro Macros.impl +} + +object Test extends App { + val c = new C + c.foo = 2 +}
\ No newline at end of file |