<commit_before>package dotty.tools.dotc.transform.linker

import dotty.tools.dotc.{ast, core}
import core._
import Contexts._
import dotty.tools.dotc.ast.Trees._
import StdNames._
import NameOps._
import dotty.tools.dotc.ast.tpd
import Symbols._
import dotty.tools.dotc.core.Phases.Phase
import dotty.tools.dotc.core.Types.{ConstantType,ExprType, MethodType, MethodicType, NoPrefix, NoType, TermRef, ThisType, Type}
import dotty.tools.dotc.transform.{Erasure, TreeTransforms}
import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo, TreeTransform}
import dotty.tools.dotc.transform.SymUtils._
import Decorators._
import dotty.tools.dotc.core.Constants.Constant
import dotty.tools.dotc.core.DenotTransformers.IdentityDenotTransformer
import dotty.tools.dotc.typer.ConstFold

import scala.collection.mutable
import scala.collection.mutable.ListBuffer


class Simplify extends MiniPhaseTransform with IdentityDenotTransformer {

  import tpd._

  override def phaseName: String = "simplify"

  private var SeqFactoryClass: Symbol = null
  private var symmetricOperations: Set[Symbol] = null



  override def prepareForUnit(tree: _root_.dotty.tools.dotc.ast.tpd.Tree)(implicit ctx: Context): TreeTransform = {
    SeqFactoryClass = ctx.requiredClass("scala.collection.generic.SeqFactory")
    symmetricOperations = Set(defn.Boolean_&&, defn.Boolean_||, defn.Int_+, defn.Int_*, defn.Long_+, defn.Long_*, defn.String_+)
    this
  }

  private def desugarIdent(i: Ident)(implicit ctx: Context): Option[tpd.Select] = {
    i.tpe match {
      case TermRef(prefix: TermRef, name) =>
        Some(tpd.ref(prefix).select(i.symbol))
      case TermRef(prefix: ThisType, name) =>
        Some(tpd.This(prefix.cls).select(i.symbol))
      case _ => None
    }
  }

  private def dropCasts(t: Tree)(implicit ctx: Context): Tree = t match {
    //case TypeApply(aio@Select(rec, nm), _) if aio.symbol == defn.Any_asInstanceOf => dropCasts(rec)
    case Typed(t, tpe) => t
    case _ => t
  }

  private def readingOnlyVals(t: Tree)(implicit ctx: Context): Boolean = dropCasts(t) match {
    case Typed(exp, tpe) => readingOnlyVals(exp)
    case Apply(Select(rec, _), Nil) =>
      if (t.symbol.isGetter && !t.symbol.is(Flags.Mutable)) readingOnlyVals(rec) // getter of a immutable field
      else if (t.symbol.owner.derivesFrom(defn.ProductClass) && t.symbol.owner.is(Flags.CaseClass) && t.symbol.name.isProductAccessorName)
        readingOnlyVals(rec) // accessing a field of a product
      else if (t.symbol.is(Flags.CaseAccessor) && !t.symbol.is(Flags.Mutable))
        readingOnlyVals(rec)
      else false
    case Select(rec, _) if t.symbol.is(Flags.Method) =>
      if (t.symbol.isGetter && !t.symbol.is(Flags.Mutable)) readingOnlyVals(rec) // getter of a immutable field
      else if (t.symbol.owner.derivesFrom(defn.ProductClass) && t.symbol.owner.is(Flags.CaseClass) && t.symbol.name.isProductAccessorName)
        readingOnlyVals(rec) // accessing a field of a product
      else if (t.symbol.is(Flags.CaseAccessor) && !t.symbol.is(Flags.Mutable))
        readingOnlyVals(rec)
      else false
    case Select(qual, _) if !t.symbol.is(Flags.Mutable) =>
      readingOnlyVals(qual)
    case t: Ident if !t.symbol.is(Flags.Mutable) && !t.symbol.is(Flags.Method) =>
      desugarIdent(t) match {
        case Some(t) => readingOnlyVals(t)
        case None => true
      }
    case t: This => true
    case t: Literal => true
    case _ => false
  }

  type Visitor = Tree => Unit
  type ErasureCompatibility = Int
  val BeforeErasure: ErasureCompatibility = 1
  val AfterErasure: ErasureCompatibility  = 2
  val BeforeAndAfterErasure: ErasureCompatibility  = BeforeErasure | AfterErasure

  val NoVisitor: Visitor = (_) => ()
  type Transformer = () => (Context => Tree => Tree)
  type Optimization = (Context) => (String, ErasureCompatibility, Visitor, Transformer)

  private lazy val _optimizations: Seq[Optimization] = Seq(
    inlineCaseIntrinsics
    ,inlineOptions
    ,inlineLabelsCalledOnce
    ,devalify
//    ,dropNoEffects
//    ,inlineLocalObjects // followCases needs to be fixed, see ./tests/pos/rbtree.scala
    /*, varify*/  // varify could stop other transformations from being applied. postponed.
    //, bubbleUpNothing
    ,constantFold
  )
  override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
    val ctx0 = ctx
    if (!tree.symbol.is(Flags.Label)) {
      implicit val ctx: Context = ctx0.withOwner(tree.symbol(ctx0))
      // TODO: optimize class bodies before erasure?
      var rhs0 = tree.rhs
      var rhs1: Tree = null
      val erasureCompatibility = if (ctx.erasedTypes) AfterErasure else BeforeErasure
      while (rhs1 ne rhs0) {
        rhs1 = rhs0
        val initialized = _optimizations.map(x =>x(ctx.withOwner(tree.symbol)))
        var (names, erasureSupport , visitors, transformers) = unzip4(initialized)
        // todo: fuse for performance
        while (names.nonEmpty) {
          val nextVisitor = visitors.head
          val supportsErasure = erasureSupport.head
          if ((supportsErasure & erasureCompatibility) > 0) {
            rhs0.foreachSubTree(nextVisitor)
            val nextTransformer = transformers.head()
            val name = names.head
            val rhst = new TreeMap() {
              override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = {
                val innerCtx = if (tree.isDef && tree.symbol.exists) ctx.withOwner(tree.symbol) else ctx
                nextTransformer(ctx)(super.transform(tree)(innerCtx))
              }
            }.transform(rhs0)
//            if (rhst ne rhs0)
//              println(s"${tree.symbol} after ${name} became ${rhst.show}")
            rhs0 = rhst
          }
          names = names.tail
          visitors = visitors.tail
          erasureSupport = erasureSupport.tail
          transformers = transformers.tail
        }
      }
      if (rhs0 ne tree.rhs) cpy.DefDef(tree)(rhs = rhs0)
      else tree
    } else tree
  }

  val inlineCaseIntrinsics: Optimization = { (ctx0: Context) => {
    implicit val ctx: Context = ctx0
    val transformer: Transformer = () => localCtx => {
      case a: Apply if !a.tpe.isInstanceOf[MethodicType] && a.symbol.is(Flags.Synthetic) && a.symbol.owner.is(Flags.Module) &&
        (a.symbol.name == nme.apply) && a.symbol.owner.companionClass.is(Flags.CaseClass) =>
        def unrollArgs(t: Tree, l: List[List[Tree]]): List[List[Tree]] = t match {
          case Apply(t, args) => unrollArgs(t, args :: l)
          case _ => l
        }
        val argss = unrollArgs(a.fun, a.args :: Nil)
        def rollInArgs(l: List[List[Tree]], fun: Tree): Tree = l match {
          case head :: tail => rollInArgs(tail, fun.appliedToArgs(head))
          case _ => fun
        }
        rollInArgs(argss.tail, tpd.New(a.tpe.dealias, argss.head))
      case a: Apply if a.symbol.is(Flags.Synthetic) && a.symbol.owner.is(Flags.Module) &&
        (a.symbol.name == nme.unapply) && a.symbol.owner.companionClass.is(Flags.CaseClass) =>
        if (!a.symbol.owner.is(Flags.Scala2x))
          a.args.head
        else if (a.tpe.derivesFrom(defn.OptionClass) && a.args.head.tpe.derivesFrom(a.symbol.owner.companionClass))
          tpd.New(a.tpe.dealias.translateParameterized(defn.OptionClass, defn.SomeClass), a.args.head :: Nil)
        else a
      case a: Apply if (a.symbol.name == nme.unapplySeq) && a.symbol.owner.derivesFrom(SeqFactoryClass) && a.symbol.extendedOverriddenSymbols.isEmpty =>
        def reciever(t: Tree): Type = t match {
          case t: Apply => reciever(t.fun)
          case t: TypeApply => reciever(t.fun)
          case t: Ident => desugarIdent(t) match {
            case Some(t) => reciever(t)
            case _ => NoType
          }
          case t: Select =>
            t.qualifier.tpe.widenDealias
        }

        val recv = reciever(a)
        if (recv.typeSymbol.is(Flags.Module))
          tpd.New(a.tpe.dealias.translateParameterized(defn.OptionClass, defn.SomeClass), a.args.head :: Nil)
        else a<commit_msg>Simplify: Start getting rid from Scala2 options and tupples in patterns<commit_after>package dotty.tools.dotc.transform.linker

import dotty.tools.dotc.{ast, core}
import core._
import Contexts._
import dotty.tools.dotc.ast.Trees._
import StdNames._
import NameOps._
import dotty.tools.dotc.ast.tpd
import Symbols._
import dotty.tools.dotc.core.Phases.Phase
import dotty.tools.dotc.core.Types.{ConstantType,ExprType, MethodType, MethodicType, NoPrefix, NoType, TermRef, ThisType, Type}
import dotty.tools.dotc.transform.{Erasure, TreeTransforms}
import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo, TreeTransform}
import dotty.tools.dotc.transform.SymUtils._
import Decorators._
import dotty.tools.dotc.core.Constants.Constant
import dotty.tools.dotc.core.DenotTransformers.IdentityDenotTransformer
import dotty.tools.dotc.typer.ConstFold

import scala.collection.mutable
import scala.collection.mutable.ListBuffer


class Simplify extends MiniPhaseTransform with IdentityDenotTransformer {

  import tpd._

  override def phaseName: String = "simplify"

  private var SeqFactoryClass: Symbol = null
  private var symmetricOperations: Set[Symbol] = null



  override def prepareForUnit(tree: _root_.dotty.tools.dotc.ast.tpd.Tree)(implicit ctx: Context): TreeTransform = {
    SeqFactoryClass = ctx.requiredClass("scala.collection.generic.SeqFactory")
    symmetricOperations = Set(defn.Boolean_&&, defn.Boolean_||, defn.Int_+, defn.Int_*, defn.Long_+, defn.Long_*, defn.String_+)
    this
  }

  private def desugarIdent(i: Ident)(implicit ctx: Context): Option[tpd.Select] = {
    i.tpe match {
      case TermRef(prefix: TermRef, name) =>
        Some(tpd.ref(prefix).select(i.symbol))
      case TermRef(prefix: ThisType, name) =>
        Some(tpd.This(prefix.cls).select(i.symbol))
      case _ => None
    }
  }

  private def dropCasts(t: Tree)(implicit ctx: Context): Tree = t match {
    //case TypeApply(aio@Select(rec, nm), _) if aio.symbol == defn.Any_asInstanceOf => dropCasts(rec)
    case Typed(t, tpe) => t
    case _ => t
  }

  private def readingOnlyVals(t: Tree)(implicit ctx: Context): Boolean = dropCasts(t) match {
    case Typed(exp, tpe) => readingOnlyVals(exp)
    case Apply(Select(rec, _), Nil) =>
      if (t.symbol.isGetter && !t.symbol.is(Flags.Mutable)) readingOnlyVals(rec) // getter of a immutable field
      else if (t.symbol.owner.derivesFrom(defn.ProductClass) && t.symbol.owner.is(Flags.CaseClass) && t.symbol.name.isProductAccessorName)
        readingOnlyVals(rec) // accessing a field of a product
      else if (t.symbol.is(Flags.CaseAccessor) && !t.symbol.is(Flags.Mutable))
        readingOnlyVals(rec)
      else false
    case Select(rec, _) if t.symbol.is(Flags.Method) =>
      if (t.symbol.isGetter && !t.symbol.is(Flags.Mutable)) readingOnlyVals(rec) // getter of a immutable field
      else if (t.symbol.owner.derivesFrom(defn.ProductClass) && t.symbol.owner.is(Flags.CaseClass) && t.symbol.name.isProductAccessorName)
        readingOnlyVals(rec) // accessing a field of a product
      else if (t.symbol.is(Flags.CaseAccessor) && !t.symbol.is(Flags.Mutable))
        readingOnlyVals(rec)
      else false
    case Select(qual, _) if !t.symbol.is(Flags.Mutable) =>
      readingOnlyVals(qual)
    case t: Ident if !t.symbol.is(Flags.Mutable) && !t.symbol.is(Flags.Method) =>
      desugarIdent(t) match {
        case Some(t) => readingOnlyVals(t)
        case None => true
      }
    case t: This => true
    case t: Literal => true
    case _ => false
  }

  type Visitor = Tree => Unit
  type ErasureCompatibility = Int
  val BeforeErasure: ErasureCompatibility = 1
  val AfterErasure: ErasureCompatibility  = 2
  val BeforeAndAfterErasure: ErasureCompatibility  = BeforeErasure | AfterErasure

  val NoVisitor: Visitor = (_) => ()
  type Transformer = () => (Context => Tree => Tree)
  type Optimization = (Context) => (String, ErasureCompatibility, Visitor, Transformer)

  private lazy val _optimizations: Seq[Optimization] = Seq(
    inlineCaseIntrinsics
    ,inlineOptions
    ,inlineLabelsCalledOnce
    ,devalify
//    ,dropNoEffects
//    ,inlineLocalObjects // followCases needs to be fixed, see ./tests/pos/rbtree.scala
    /*, varify*/  // varify could stop other transformations from being applied. postponed.
    //, bubbleUpNothing
    ,constantFold
  )
  override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
    val ctx0 = ctx
    if (!tree.symbol.is(Flags.Label)) {
      implicit val ctx: Context = ctx0.withOwner(tree.symbol(ctx0))
      // TODO: optimize class bodies before erasure?
      var rhs0 = tree.rhs
      var rhs1: Tree = null
      val erasureCompatibility = if (ctx.erasedTypes) AfterErasure else BeforeErasure
      while (rhs1 ne rhs0) {
        rhs1 = rhs0
        val initialized = _optimizations.map(x =>x(ctx.withOwner(tree.symbol)))
        var (names, erasureSupport , visitors, transformers) = unzip4(initialized)
        // todo: fuse for performance
        while (names.nonEmpty) {
          val nextVisitor = visitors.head
          val supportsErasure = erasureSupport.head
          if ((supportsErasure & erasureCompatibility) > 0) {
            rhs0.foreachSubTree(nextVisitor)
            val nextTransformer = transformers.head()
            val name = names.head
            val rhst = new TreeMap() {
              override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = {
                val innerCtx = if (tree.isDef && tree.symbol.exists) ctx.withOwner(tree.symbol) else ctx
                nextTransformer(ctx)(super.transform(tree)(innerCtx))
              }
            }.transform(rhs0)
//            if (rhst ne rhs0)
//              println(s"${tree.symbol} after ${name} became ${rhst.show}")
            rhs0 = rhst
          }
          names = names.tail
          visitors = visitors.tail
          erasureSupport = erasureSupport.tail
          transformers = transformers.tail
        }
      }
      if (rhs0 ne tree.rhs) cpy.DefDef(tree)(rhs = rhs0)
      else tree
    } else tree
  }

  val inlineCaseIntrinsics: Optimization = { (ctx0: Context) => {
    implicit val ctx: Context = ctx0
    val transformer: Transformer = () => localCtx => {
      case a: Apply if !a.tpe.isInstanceOf[MethodicType] && a.symbol.is(Flags.Synthetic) && a.symbol.owner.is(Flags.Module) &&
        (a.symbol.name == nme.apply) && a.symbol.owner.companionClass.is(Flags.CaseClass) =>
        def unrollArgs(t: Tree, l: List[List[Tree]]): List[List[Tree]] = t match {
          case Apply(t, args) => unrollArgs(t, args :: l)
          case _ => l
        }
        val argss = unrollArgs(a.fun, a.args :: Nil)
        def rollInArgs(l: List[List[Tree]], fun: Tree): Tree = l match {
          case head :: tail => rollInArgs(tail, fun.appliedToArgs(head))
          case _ => fun
        }
        rollInArgs(argss.tail, tpd.New(a.tpe.dealias, argss.head))
      case a: Apply if a.symbol.is(Flags.Synthetic) && a.symbol.owner.is(Flags.Module) &&
        (a.symbol.name == nme.unapply) && a.symbol.owner.companionClass.is(Flags.CaseClass) =>
        if (!a.symbol.owner.is(Flags.Scala2x))
          a.args.head
        else if (a.tpe.derivesFrom(defn.OptionClass) && a.args.head.tpe.derivesFrom(a.symbol.owner.companionClass)) {
          val accessors = a.args.head.tpe.widenDealias.classSymbol.caseAccessors.filter(_.is(Flags.Method))
          val fields = accessors.map(x => a.args.head.select(x).ensureApplied)
          val tplType = a.tpe.baseArgTypes(defn.OptionClass).head

          if (!fields.tail.isEmpty) {
            val tplAlloc = tpd.New(tplType, fields)
            tpd.New(a.tpe.dealias.translateParameterized(defn.OptionClass, defn.SomeClass), tplAlloc :: Nil)
          } else { // scalac does not have tupple1
            tpd.New(a.tpe.dealias.translateParameterized(defn.OptionClass, defn.SomeClass), fields.head :: Nil)
          }
        }
        else a
      case a: Apply if (a.symbol.name == nme.unapplySeq) && a.symbol.owner.derivesFrom(SeqFactoryClass) && a.symbol.extendedOverriddenSymbols.isEmpty =>
        def reciever(t: Tree): Type = t match {
          case t: Apply => reciever(t.fun)
          case t: TypeApply => reciever(t.fun)
          case t: Ident => desugarIdent(t) match {
            case Some(t) => reciever(t)
            case _ => NoType
          }
          case t: Select =>
            t.qualifier.tpe.widenDealias
        }

        val recv = reciever(a)
        if (recv.typeSymbol.is(Flags.Module))
          tpd.New(a.tpe.dealias.translateParameterized(defn.OptionClass, defn.SomeClass), a.args.head :: Nil)
        else a
{'fraction_non_alphanumeric': 0.12184823977164605, 'fraction_numerical': 0.002735490009514748, 'mean_word_length': 2.9578724405742527, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 84, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7244527', 'n_tokens_mistral': 5454, 'n_tokens_neox': 5385, 'n_words': 1167}