Skip to content

Fix #6263: Register needsStaging when creating a synthetic quoted type #6342

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,13 @@ class ReifyQuotes extends MacroTransform {
capturers(body.symbol)(body)
case _=>
val (body1, splices) = nested(isQuote = true).splitQuote(body)(quoteContext)
if (level == 0 && !ctx.inInlineMethod) {
if (level == 0) {
val body2 =
if (body1.isType) body1
else Inlined(Inliner.inlineCallTrace(ctx.owner, quote.sourcePos), Nil, body1)
pickledQuote(body2, splices, body.tpe, isType).withSpan(quote.span)
}
else {
// In top-level splice in an inline def. Keep the tree as it is, it will be transformed at inline site.
body
}
}
Expand Down
29 changes: 3 additions & 26 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -217,29 +217,6 @@ object Applications {
class ExtMethodApply(app: Tree)(implicit @constructorOnly src: SourceFile)
extends IntegratedTypeArgs(app)

/** 1. If we are in an inline method but not in a nested quote, mark the inline method
* as a macro.
*
* 2. If selection is a quote or splice node, record that fact in the current compilation unit.
*/
def handleMeta(tree: Tree)(implicit ctx: Context): tree.type = {
import transform.SymUtils._

def markAsMacro(c: Context): Unit =
if (c.owner eq c.outer.owner) markAsMacro(c.outer)
else if (c.owner.isInlineMethod) c.owner.setFlag(Macro)
else if (!c.outer.owner.is(Package)) markAsMacro(c.outer)
val sym = tree.symbol
if (sym.isSplice) {
if (StagingContext.level == 0)
markAsMacro(ctx)
ctx.compilationUnit.needsStaging = true
} else if (sym.isQuote) {
ctx.compilationUnit.needsStaging = true
}

tree
}
}

trait Applications extends Compatibility { self: Typer with Dynamic =>
Expand Down Expand Up @@ -820,7 +797,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
* or, if application is an operator assignment, also an `Assign` or
* Block node.
*/
def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = handleMeta {
def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {

def realApply(implicit ctx: Context): Tree = track("realApply") {
val originalProto = new FunProto(tree.args, IgnoredProto(pt))(this, tree.isContextual)(argCtx(tree))
Expand Down Expand Up @@ -964,7 +941,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
val isNamed = hasNamedArg(tree.args)
val typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_))
record("typedTypeApply")
handleMeta(typedFunPart(tree.fun, PolyProto(typedArgs, pt)) match {
typedFunPart(tree.fun, PolyProto(typedArgs, pt)) match {
case IntegratedTypeArgs(app) =>
app
case _: TypeApply if !ctx.isAfterTyper =>
Expand All @@ -986,7 +963,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
}
if (typedFn.tpe eq TryDynamicCallType) tryDynamicTypeApply()
else assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs)
})
}
}

/** Rewrite `new Array[T](....)` if T is an unbounded generic to calls to newGenericArray.
Expand Down
51 changes: 29 additions & 22 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -667,29 +667,36 @@ trait Implicits { self: Typer =>
EmptyTree
}

def synthesizedTypeTag(formal: Type): Tree = formal.argInfos match {
case arg :: Nil if !arg.typeSymbol.is(Param) =>
object bindFreeVars extends TypeMap {
var ok = true
def apply(t: Type) = t match {
case t @ TypeRef(NoPrefix, _) =>
inferImplicit(defn.QuotedTypeType.appliedTo(t), EmptyTree, span) match {
case SearchSuccess(tag, _, _) if tag.tpe.isStable =>
tag.tpe.select(defn.QuotedType_splice)
case _ =>
ok = false
t
}
case _ => t
def synthesizedTypeTag(formal: Type): Tree = {
def quotedType(t: Type) = {
if (StagingContext.level == 0)
ctx.compilationUnit.needsStaging = true // We will need to run ReifyQuotes
ref(defn.InternalQuoted_typeQuote).appliedToType(t)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if implicit search is the best place to put the logic --- what about put the logic in typer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We detect quotes and splices when we type. The issue here is that we will do not type this tree because we create it already typed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The concern I have here is about modularity. It seems the added code does not belong here. Is it due to concern about performance? Modularity could be sacrificed for performance.

}
formal.argInfos match {
case arg :: Nil if !arg.typeSymbol.is(Param) =>
object bindFreeVars extends TypeMap {
var ok = true
def apply(t: Type) = t match {
case t @ TypeRef(NoPrefix, _) =>
inferImplicit(defn.QuotedTypeType.appliedTo(t), EmptyTree, span) match {
case SearchSuccess(tag, _, _) if tag.tpe.isStable =>
tag.tpe.select(defn.QuotedType_splice)
case _ =>
ok = false
t
}
case _ => t
}
}
}
val tag = bindFreeVars(arg)
if (bindFreeVars.ok) ref(defn.InternalQuoted_typeQuote).appliedToType(tag)
else EmptyTree
case arg :: Nil if ctx.inInlineMethod =>
ref(defn.InternalQuoted_typeQuote).appliedToType(arg)
case _ =>
EmptyTree
val tag = bindFreeVars(arg)
if (bindFreeVars.ok) quotedType(tag)
else EmptyTree
case arg :: Nil if ctx.inInlineMethod =>
quotedType(arg)
case _ =>
EmptyTree
}
}

def synthesizedTastyContext(formal: Type): Tree =
Expand Down
18 changes: 15 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -449,8 +449,7 @@ class Typer extends Namer
}
case qual =>
if (tree.name.isTypeName) checkStable(qual.tpe, qual.sourcePos)
val select = Applications.handleMeta(
checkValue(assignType(cpy.Select(tree)(qual, tree.name), qual), pt))
val select = checkValue(assignType(cpy.Select(tree)(qual, tree.name), qual), pt)
if (select.tpe ne TryDynamicCallType) ConstFold(checkStableIdentPattern(select, pt))
else if (pt.isInstanceOf[FunOrPolyProto] || pt == AssignProto) select
else typedDynamicSelect(tree, Nil, pt)
Expand Down Expand Up @@ -1936,8 +1935,10 @@ class Typer extends Namer
ctx.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.sourcePos)
typed(innerExpr, pt)
case quoted if quoted.isType =>
ctx.compilationUnit.needsStaging = true
typedTypeApply(untpd.TypeApply(untpd.ref(defn.InternalQuoted_typeQuoteR), quoted :: Nil), pt)(quoteContext).withSpan(tree.span)
case quoted =>
ctx.compilationUnit.needsStaging = true
if (ctx.mode.is(Mode.Pattern) && level == 0) {
val exprPt = pt.baseType(defn.QuotedExprClass)
val quotedPt = if (exprPt.exists) exprPt.argTypesHi.head else defn.AnyType
Expand Down Expand Up @@ -2012,6 +2013,7 @@ class Typer extends Namer
ctx.warning("Canceled quote directly inside a splice. ${ '{ XYZ } } is equivalent to XYZ.", tree.sourcePos)
typed(innerExpr, pt)
case expr =>
ctx.compilationUnit.needsStaging = true
if (ctx.mode.is(Mode.QuotedPattern) && level == 1) {
if (isFullyDefined(pt, ForceDegree.all)) {
def spliceOwner(ctx: Context): Symbol =
Expand All @@ -2024,13 +2026,23 @@ class Typer extends Namer
tree.withType(UnspecifiedErrorType)
}
}
else
else {
if (StagingContext.level == 0) {
// Mark the first inline method from the context as a macro
def markAsMacro(c: Context): Unit =
if (c.owner eq c.outer.owner) markAsMacro(c.outer)
else if (c.owner.isInlineMethod) c.owner.setFlag(Macro)
else if (!c.outer.owner.is(Package)) markAsMacro(c.outer)
markAsMacro(ctx)
}
typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprSpliceR), tree.expr), pt)(spliceContext).withSpan(tree.span)
}
}
}

/** Translate ${ t: Type[T] }` into type `t.splice` while tracking the quotation level in the context */
def typedTypSplice(tree: untpd.TypSplice, pt: Type)(implicit ctx: Context): Tree = track("typedTypSplice") {
ctx.compilationUnit.needsStaging = true
checkSpliceOutsideQuote(tree)
typedSelect(untpd.Select(tree.expr, tpnme.splice), pt)(spliceContext).withSpan(tree.span)
}
Expand Down
13 changes: 13 additions & 0 deletions compiler/test-resources/repl/i6263
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
scala> import quoted._
scala> def fn[T : Type](v : T) = println("ok")
def fn[T](v: T)(implicit evidence$1: quoted.Type[T]): Unit
scala> fn("foo")
ok
scala> fn((1,2))
ok
scala> object O
// defined object O
scala> fn(O)
ok
scala> fn(1)
ok
14 changes: 14 additions & 0 deletions tests/run-with-compiler/i6263.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import quoted._
object Test {

def main(args: Array[String]): Unit = {
fn("foo")
fn((1,2))
fn(O)
fn(1)
}

def fn[T : Type](v : T) = "ok"
}

object O