diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 8236d9cb7b0..89b110ab2ec 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1127,7 +1127,7 @@ trait Namers extends MethodSynthesis { case _ => defnTyper.computeType(tree.rhs, pt) } tree.tpt.defineType { - if (currentRun.isScala3 && !pt.isWildcard && pt != NoType && !pt.isErroneous) pt + if (currentRun.isScala3 && !pt.isWildcard && pt != NoType && !pt.isErroneous && openMacros.isEmpty) pt else dropIllegalStarTypes(widenIfNecessary(tree.symbol, rhsTpe, pt)) }.setPos(tree.pos.focus) tree.tpt.tpe @@ -1319,7 +1319,6 @@ trait Namers extends MethodSynthesis { val tparamSyms = typer.reenterTypeParams(tparams) val tparamSkolems = tparams.map(_.symbol) - /* * Creates a method type using tparamSyms and vparamsSymss as argument symbols and `respte` as result type. * All typeRefs to type skolems are replaced by references to the corresponding non-skolem type parameter, @@ -1331,7 +1330,6 @@ trait Namers extends MethodSynthesis { def deskolemizedPolySig(vparamSymss: List[List[Symbol]], restpe: Type) = GenPolyType(tparamSyms, methodTypeFor(meth, vparamSymss, restpe).substSym(tparamSkolems, tparamSyms)) - if (tpt.isEmpty && meth.name == nme.CONSTRUCTOR) { tpt defineType context.enclClass.owner.tpe_* tpt setPos meth.pos.focus @@ -1351,7 +1349,6 @@ trait Namers extends MethodSynthesis { tptTyped.tpe } - // ignore missing types unless we can look to overridden method to recover the missing information val canOverride = methOwner.isClass && !meth.isConstructor val inferResTp = canOverride && tpt.isEmpty diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index e1eed2b8834..ccc0126ee1b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -14,7 +14,7 @@ package scala package tools.nsc package typechecker -import scala.annotation.tailrec +import scala.annotation.{tailrec, unused} import scala.collection.mutable import scala.reflect.internal.{Chars, TypesStats} import scala.reflect.internal.util.{FreshNameCreator, ListOfNil, Statistics} @@ -6272,27 +6272,25 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper tpe } - def computeMacroDefType(ddef: DefDef, pt: Type): Type = { + // called from `methodSig`. Macro defs must have an explicit type. + // The supplied expected type is ignored. + def computeMacroDefType(ddef: DefDef, @unused pt: Type): Type = { assert(context.owner.isMacro, context.owner) assert(ddef.symbol.isMacro, ddef.symbol) - // macro defs are typechecked in `methodSig` (by calling this method) in order to establish their link to macro implementation asap - // if a macro def doesn't have explicitly specified return type, this method will be called again by `assignTypeToTree` - // here we guard against this case val rhs1 = - if (transformed contains ddef.rhs) { + if (transformed contains ddef.rhs) transformed(ddef.rhs) - } else { + else { val rhs1 = typedMacroBody(this, ddef) transformed(ddef.rhs) = rhs1 rhs1 } - - val isMacroBodyOkay = !ddef.symbol.isErroneous && !(rhs1 exists (_.isErroneous)) && rhs1 != EmptyTree + val isMacroBodyOkay = !ddef.symbol.isErroneous && !rhs1.exists(_.isErroneous) && rhs1 != EmptyTree val shouldInheritMacroImplReturnType = ddef.tpt.isEmpty if (isMacroBodyOkay && shouldInheritMacroImplReturnType) { - val commonMessage = "macro defs must have explicitly specified return types" def reportFailure() = { + val commonMessage = "macro defs must have explicitly specified return types" ddef.symbol.setFlag(IS_ERROR) context.error(ddef.pos, commonMessage) } diff --git a/test/files/pos/t12645/Macro_1.scala b/test/files/pos/t12645/Macro_1.scala new file mode 100644 index 00000000000..827ba992fe7 --- /dev/null +++ b/test/files/pos/t12645/Macro_1.scala @@ -0,0 +1,20 @@ + +// scalac: -Xsource:3 + +import language.experimental.macros +import scala.reflect.macros.whitebox.Context + +trait Greeter { + def greeting: Option[Any] = Some("Take me to your leader, Greeter.") +} + +class Welcomer { + def greeter: Greeter = macro Macros.impl +} + +object Macros { + def impl(c: Context) = { + import c.universe._ + q"""new Greeter { override def greeting = Some("hello, quoted world") }""" + } +} diff --git a/test/files/pos/t12645/Test_2.scala b/test/files/pos/t12645/Test_2.scala new file mode 100644 index 00000000000..520bd7de7dd --- /dev/null +++ b/test/files/pos/t12645/Test_2.scala @@ -0,0 +1,8 @@ + +// scalac: -Xsource:3 + +object Test extends App { + def f(s: String) = println(s) + val welcomer = new Welcomer + welcomer.greeter.greeting.foreach(f) +} diff --git a/test/files/pos/t12645b/Test_2.scala b/test/files/pos/t12645b/Test_2.scala new file mode 100644 index 00000000000..eb9942baf84 --- /dev/null +++ b/test/files/pos/t12645b/Test_2.scala @@ -0,0 +1,6 @@ +// scalac: -Xsource:3 + +object Test extends App { + Foo.ctx.quote(42).ast.id +} +// was: Test_2.scala:4: error: value id is not a member of Ast diff --git a/test/files/pos/t12645b/macro_1.scala b/test/files/pos/t12645b/macro_1.scala new file mode 100644 index 00000000000..732ae1fd7d2 --- /dev/null +++ b/test/files/pos/t12645b/macro_1.scala @@ -0,0 +1,31 @@ + +// scalac: -Xsource:3 + +import scala.language.experimental.macros +import scala.reflect.macros.whitebox.Context + +trait Ast +case class Foo(id: Int) extends Ast +object Foo { + val ctx = new Ctx {} +} + +trait Ctx { + trait Quoted[+A] { + def ast: Ast + } + final def quote[A](a: A): Quoted[A] = macro QuoteMacro.quoteImpl[A] +} + +class QuoteMacro(val c: Context) { + import c.universe.* + + def quoteImpl[A](a: c.Expr[A])(implicit t: WeakTypeTag[A]) = + c.untypecheck { + q""" + new ${c.prefix}.Quoted[$t] { + def ast = Foo(42) + } + """ + } +}