Skip to content

TailRec phase and tests for it. #117

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 11, 2014
Merged

Conversation

DarkDimius
Copy link
Contributor

Ported tailcall phase from scalac with such changes:

  • all transformation is done in the phase itself
    (previously half of the work was done in backend)
  • it is now able to run before uncurry
  • it is now a treeTransform
  • renamed to tailrec to make it more obvious that
    this phase transforms only recursive calls.

For now this is a single phase which speculatively
transforms DefDefs.
Speculation can be potentially removed by
splitting into 2 phases:
one detecting which methods should be transformed
second performing transformation.

But, as transformation requires as same amount of work
as detection, I believe it will be simpler to maintain
it as a single phase.

@DarkDimius
Copy link
Contributor Author

@odersky @gzm0 please review

@@ -427,8 +430,42 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def mkAnd(tree1: Tree, tree2: Tree)(implicit ctx: Context) =
Apply(Select(tree1, defn.Boolean_and), tree2 :: Nil)

def deriveDefDef(ddef: Tree)(applyToRhs: Tree => Tree): DefDef = ddef match {
Copy link
Member

Choose a reason for hiding this comment

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

The parameter ddef should clearly be typed as DefDef, not as Tree.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd prefer to leave it as is, as otherwise callsite will be responsible for cast, and match will reside in callsite.
This way we can simplify error handling, and have it in single place for a very common case when particular type of tree is expected and modified.

Copy link
Member

Choose a reason for hiding this comment

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

In all the call sites that are in this PR, the caller already has a statically typed version of the tree that it can use to pass to these methods.

For example here:
https://github.com/lampepfl/dotty/pull/117/files#diff-374479fca6aeeda64b2b850c4c611cc5R267
the identifier tree can be rebound in the pattern so that its static type becomes CaseDef.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, as TailRec reconstructs execution flow, it pattern matches on type of the tree, and is able to use it this way. But common usecase in scalac(that I like) is that those methods replace trivial match-and-transform clauses.

Copy link
Member

Choose a reason for hiding this comment

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

those methods replace trivial match-and-transform clauses.

Except they don't: they replace a .asInstanceOf[DefDef], not a match. They only work when the type of the argument is exactly the expected type, and fail with an error that is no better than a ClassCastException if it doesn't. I would like to see a compelling call-site reason to do this before losing type-safety.

Copy link
Member

Choose a reason for hiding this comment

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

AFAICT, the reason that scalac tends to weaken the type requirements to Tree comes from the fact that typed /typedPos and a few others return Tree, even in cases when you know that you'll get back the same tree.

   val t: Tree = typedPos(pos)(DefDef(.....))
   deriveDefDef(t)(pos)

Maybe that calls for variants of those methods instead:

  def typedPosExact[T <: Tree](t: T): T
  val t: DefDef = typedPosExact(pos)(DefDef(.....))

A while back, I thought that TreeCopy was obviously too liberal in accepting Trees, but it often makes sense to use that with different trees:

val vd : ValDef = ...
treeCopy.DefDef(vd, .., ...)

In short, it might be worth starting with more restrictive types.

@gzm0
Copy link
Contributor

gzm0 commented Apr 9, 2014

LGTM

@DarkDimius
Copy link
Contributor Author

@odersky is there anything except hasAnnotation?

@odersky
Copy link
Contributor

odersky commented Apr 10, 2014

annotations gives you the list of all annotations.

Helper method that tests weather this particular
SymDenotation cant have overrides: eg if it's defined in module class,
if it is inner method, if it is method of anonymous class, etc.
This shouldn't require any changes to backend,
as all type parameters will be erased in erasure

Conflicts:
	src/dotty/tools/dotc/core/Symbols.scala
@DarkDimius
Copy link
Contributor Author

@odersky I've removed deriveXXX methods

Ported tailcall phase from scalac with such changes:
  - all transformation is done in the phase itself
     (previously half of the work was done in backend)
  - it is now able to run before uncurry
  - it is now a treeTransform
  - renamed to tailrec to make it more obvious that
     this phase transforms only recursive calls.

For now this is a single phase which speculatively
transforms DefDefs.
Speculation can be potentially removed by
splitting into 2 phases:
  one detecting which methods should be transformed
  second performing transformation.

But, as transformation requires as same amount of work
as detection, I believe it will be simpler to maintain
it as a single phase.

Conflicts:
	tests/pos/typers.scala
DarkDimius added a commit that referenced this pull request Apr 11, 2014
TailRec phase and tests for it.
@DarkDimius DarkDimius merged commit 42e7488 into scala:master Apr 11, 2014
WojciechMazur pushed a commit to WojciechMazur/dotty that referenced this pull request Mar 19, 2025
Backport "Do not crash when typing a closure with unknown type, since it can occur for erroneous input" to 3.3 LTS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants