Navigation Menu

Skip to content
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

Another "macro not expanded" error with implicits, inferred Nothing, default args #10073

Closed
scabug opened this issue Nov 21, 2016 · 7 comments
Closed
Assignees
Milestone

Comments

@scabug
Copy link

scabug commented Nov 21, 2016

class Yo[Unused] {
  def yo(hasDefault: Any = ""): String = ""
}

class MacroNotExpanded {
  implicit def toYo[Unused](a: Any)(implicit ct: reflect.ClassTag[Unused]): Yo[Unused] = new Yo[Unused]
   "".yo()  
}
/Users/jz/code/scratch/src/main/scala/Main.scala:7: error: macro has not been expanded
   "".yo()
   ^
one error found

Same symptom and similar causes to #7987

@scabug
Copy link
Author

scabug commented Nov 21, 2016

Imported From: https://issues.scala-lang.org/browse/SI-10073?orig=1
Reporter: @retronym
Affected Versions: 2.11.6, 2.11.8, 2.12.0
See #7987

@scabug
Copy link
Author

scabug commented Nov 21, 2016

@retronym said:
Simpler again:

class Yo[Unused] {
  def yo(hasDefault: Any = ""): String = ""
}
 
class MacroNotExpanded {
  def toYo[Unused](a: Any)(implicit ct: reflect.ClassTag[Unused]): Yo[Unused] = new Yo[Unused]
  toYo("").yo()  
}

@scabug
Copy link
Author

scabug commented Nov 21, 2016

@retronym said:
The bug is in the mechanism in the typechecker to defer macro expansion involving undetermined type parameters until the enclosing tree is typechecked and the type params are fully inferred.

  /** Performs macro expansion on all subtrees of a given tree.
   *  Innermost macros are expanded first, outermost macros are expanded last.
   *  See the documentation for `macroExpand` for more information.
   */
  def macroExpandAll(typer: Typer, expandee: Tree): Tree =
    new Transformer {
      override def transform(tree: Tree) = super.transform(tree match {
        // todo. expansion should work from the inside out
        case tree if (delayed contains tree) && calculateUndetparams(tree).isEmpty && !tree.isErroneous =>
          val context = tree.attachments.get[MacroRuntimeAttachment].get.typerContext
          delayed -= tree
          context.implicitsEnabled = typer.context.implicitsEnabled
          context.enrichmentEnabled = typer.context.enrichmentEnabled
          context.macrosEnabled = typer.context.macrosEnabled
          macroExpand(newTyper(context), tree, EXPRmode, WildcardType)
        case _ =>
          tree
      })
    }.transform(expandee)
  /** Without any restrictions on macro expansion, macro applications will expand at will,
   *  and when type inference is involved, expansions will end up using yet uninferred type params.
   *
   *  For some macros this might be ok (thanks to TreeTypeSubstituter that replaces
   *  the occurrences of undetparams with their inferred values), but in general case this won't work.
   *  E.g. for reification simple substitution is not enough - we actually need to re-reify inferred types.
   *
   *  Luckily, there exists a very simple way to fix the problem: delay macro expansion until everything is inferred.
   *  Here are the exact rules. Macro application gets delayed if any of its subtrees contain:
   *    1) type vars (tpe.isInstanceOf[TypeVar]) // [Eugene] this check is disabled right now, because TypeVars seem to be created from undetparams anyways
   *    2) undetparams (sym.isTypeParameter && !sym.isSkolem)
   */
  var hasPendingMacroExpansions = false
  private val forced = perRunCaches.newWeakSet[Tree]
  private val delayed = perRunCaches.newWeakMap[Tree, scala.collection.mutable.Set[Int]]()

This code assumes that the enclosing expression contains the exact tree, not copies theroef. Names/defaults copies trees iirc

@scabug
Copy link
Author

scabug commented Nov 21, 2016

@retronym said:
We could try to store this metadata in tree attachments (which are propagated through tree copying) rather than in standalone Map[Tree, ...]-s

@retronym
Copy link
Member

I've taken another look at this problem.

I first tried to change the use of the global map delayed: Map[Tree, _] to use of a tree attachment, to have a better chance of surviving tree duplication. But I'm not actually sure if that is a factor in the test cases we have here.

I then found that in macroExpandAll (the code the looks at all subtrees of each typechecked tree for any delayed expansions that are ready to be expanded) to properly issue errors that the macro issues.

https://github.com/scala/scala/compare/2.12.x...retronym:ticket/10073?expand=1

@adriaanm
Copy link
Contributor

(Notify case 10359 when this gets merged.)

@lrytz
Copy link
Member

lrytz commented Mar 6, 2018

Fixed in scala/scala#6330

@lrytz lrytz closed this as completed Mar 6, 2018
@SethTisue SethTisue modified the milestones: Backlog, 2.12.5 Mar 13, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants