All Downloads are FREE. Search and download functionalities are using the official Maven repository.

plugin.PreTyperPhase.scala Maven / Gradle / Ivy

The newest version!
package dfhdl.plugin

import dotty.tools.dotc._

import plugins._

import core._
import Contexts._
import Symbols._
import Flags._
import SymDenotations._

import Decorators._
import ast.Trees._
import ast.untpd
import StdNames.nme
import Names._
import Constants.Constant
import Types._
import scala.language.implicitConversions
import collection.mutable
import annotation.tailrec

/** This is a pre-typer phase that does very minor things:
  *   - change infix operator precedence of type signature: `a X b <> c` to be `(a X b) <> c`
  *   - change infix operator precedence of terms: `a <> b op c` to be `a <> (b op c)`, where op is
  *     `|`, `||`, `&`, `&&`, or `^`
  *   - workaround for https://github.com/scala/scala3/issues/20053
  */
class PreTyperPhase(setting: Setting) extends PluginPhase:
  import untpd.*

  val phaseName = "PreTyper"

  override val runsAfter = Set("parser")
  override val runsBefore = Set("typer")
  private var debugFlag = false

  def debug(str: => Any*): Unit =
    if (debugFlag) println(str.mkString(", "))

  val opSet = Set("|", "||", "&", "&&", "^")
  private val `fix<>andOpPrecedence` = new UntypedTreeMap:
    object InfixOpArgsChange:
      def unapply(tree: InfixOp)(using Context): Option[(Tree, Ident, Tree)] =
        tree match
          case InfixOp(InfixOpArgsChange(a, Ident(conn), b), Ident(op), c)
              if opSet.contains(op.toString) =>
            Some(a, Ident(conn), InfixOp(b, Ident(op), c))
          case InfixOp(InfixOp(a, Ident(conn), b), Ident(op), c)
              if conn.toString == "<>" && opSet.contains(op.toString) =>
            Some(a, Ident(conn), InfixOp(b, Ident(op), c))
          case _ => None
    object InfixOpChange:
      def unapply(tree: InfixOp)(using Context): Option[InfixOp] =
        tree match
          case InfixOpArgsChange(a, Ident(conn), b) => Some(InfixOp(a, Ident(conn), Parens(b)))
          case _                                    => None
    end InfixOpChange
    override def transformBlock(blk: Block)(using Context): Block =
      super.transformBlock(blk) match
        // a connection could be in return expression position of a Unit-typed block
        case Block(stats, InfixOpChange(expr)) => Block(stats, expr)
        case blk                               => blk
    override def transformStats(trees: List[Tree], exprOwner: Symbol)(using Context): List[Tree] =
      super.transformStats(trees, exprOwner).map:
        // only handling pure statements that begin as an infix
        case InfixOpChange(tree) => tree
        case tree =>
          debug(tree)
          tree
    override def transform(tree: Tree)(using Context): Tree =
      super.transform(tree) match
        // a connection could be in return position of a DFHDL Unit definition (if no block is used)
        case tree @ DefDef(_, _, _, InfixOpChange(rhs)) =>
          cpy.DefDef(tree)(rhs = rhs)
        case t => t
      end match
    end transform

  val reduceOpSet = Set("|", "&", "^")
  val warnOpSet = Set("reduce|", "reduce&", "reduce^")
  private val `fixXand<>Precedence` = new UntypedTreeMap:
    object InfixOpChange:
      def unapply(tree: InfixOp)(using Context): Option[InfixOp] =
        tree match
          case InfixOp(a, Ident(x), InfixOp(b, Ident(conn), c))
              if x.toString == "X" && conn.toString == "<>" =>
            Some(InfixOp(Parens(InfixOp(a, Ident(x), b)), Ident(conn), c))
          case _ => None
    override def transform(tree: Tree)(using Context): Tree =
      super.transform(tree) match
        case tree @ ValDef(_, InfixOpChange(tpt), _) =>
          cpy.ValDef(tree)(tpt = tpt)
        case tree @ DefDef(_, _, InfixOpChange(tpt), _) =>
          cpy.DefDef(tree)(tpt = tpt)
        // workaround https://github.com/scala/scala3/issues/20053
        case tree @ Select(t, name) =>
          val nameStr = name.toString()
          if (reduceOpSet.contains(nameStr)) cpy.Select(tree)(t, s"reduce$nameStr".toTermName)
          else if (warnOpSet.contains(nameStr))
            report.error(
              s"do not directly call `.$nameStr`; instead call `.${nameStr.last}`",
              tree.srcPos
            )
            tree
          else tree
        case t => t
      end match
    end transform

  override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] =
    val parsed = super.runOn(units)
    parsed.foreach { cu =>
      // debugFlag = cu.source.file.path.contains("Example.scala")
      cu.untpdTree = `fix<>andOpPrecedence`.transform(cu.untpdTree)
      cu.untpdTree = `fixXand<>Precedence`.transform(cu.untpdTree)
    }
    parsed
end PreTyperPhase




© 2015 - 2024 Weber Informatics LLC | Privacy Policy