Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Fix ClassCastException on call to trait method with call-by-name argument, if implemented as SAM #10830

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

Draft
wants to merge 1 commit into
base: 2.13.x
Choose a base branch
from

Conversation

som-snytt
Copy link
Contributor

@som-snytt som-snytt commented Aug 9, 2024

Always just forward args in function expansion

Fixes scala/bug#11237

@scala-jenkins scala-jenkins added this to the 2.13.16 milestone Aug 9, 2024
@som-snytt som-snytt marked this pull request as ready for review August 10, 2024 01:32
@SethTisue SethTisue modified the milestones: 2.13.16, 2.13.15 Aug 13, 2024
@@ -242,7 +245,16 @@ abstract class UnCurry extends InfoTransform
val typedNewFun = localTyper.typedPos(fun.pos)(Block(liftedMethod :: Nil, super.transform(newFun)))
if (mustExpand) {
val Block(stats, expr : Function) = typedNewFun: @unchecked
treeCopy.Block(typedNewFun, stats, gen.expandFunction(localTyper)(expr, inConstructorFlag))
Copy link
Member

Choose a reason for hiding this comment

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

Spent too much time on understanding this thing... comparing with

  def f(x: => Int) = 0
  def g(x: => Int) = f(x)

here it's not noApply that is in the works, but the x tree is added to byNameArgs.

I was wondering why it doesn't work here.

The compiler first creates ((x1: Box, x2: () => Box) => $anonfun$run(x1, x2)), runs super.transform on it, and then translates that into the anonymous class with def append(f1: Box, f2: () => Box): Box = $anonfun$run(f1, f2.apply()).

The .apply() is the bug. The issue is that byNameArgs no longer works on the second transformation.

https://github.com/scala/scala/blob/v2.13.14/src/compiler/scala/tools/nsc/transform/UnCurry.scala#L499-L501

because at the second pass, fn.tpe already has the post-uncurry type, I guess from here

https://github.com/scala/scala/blob/v2.13.14/src/compiler/scala/tools/nsc/transform/UnCurry.scala#L557.

so fnParams no longer has by name => Box, but () => Box. Then the f2 argument tree is not added to byNameArgs, and the apply is added.

Your fix looks fine, I was just not 100% it would only ever trigger in that case we are looking at, or if it might cause other changes. So I went into the rabbit hole.

Maybe you have more thoughts..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, I ought to have written it up. I spent a day to understand LL220, and wasn't confident in the desired behavior until I saw that dotty does the same thing (that is, the same result as this PR).

I think I had a different approach but finally did LL250 when I ran out of patience. This looks too brute-force or ad-hoc to me now. Maybe it seemed natural after looking at the trees for hours.

I remember wondering why L223 doesn't use attachments.contains, and assumed I would make another pass here, but didn't want to spend a half-day on subtleties about attachments. (contains does not use retronym's hand-rolled set function.)

At a minimum, I'll respool and document.

Copy link
Member

@lrytz lrytz Aug 21, 2024

Choose a reason for hiding this comment

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

This would be the right fix I think https://github.com/scala/scala/compare/2.13.x...lrytz:scala:pr10830?expand=1

But it needs to be fixed for multiple param lists, Apply(Apply(f), args0), args1) we need to get the second params.

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'll make some time now for the more thoughts you asked for.

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 spent too much time" etc. I think my previous idea was to pass a flag to say "this is just a forwarder", but today I noticed that you can detect it because the forwarder has (x: () => T) => R instead of (x: => T) => R. The method param and the function param happen to be mismatched. More precisely, the vparam.tpt is a function type, while the symbol.info is cbn. I exploit that to tag references with PreserveArg attachment. (That could be improved to do only cbn args, but I think for a forwarder that is always the case.)

Today I was looking at the change in TreeGen, which I previously avoided. I looked again at how byNameArgs is used and it's still not clear to me. Maybe if I take a quick power nap first.

Copy link
Member

Choose a reason for hiding this comment

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

I think the current logic in UnCurry is fine and we don't need anything specific for this bug.

The underlying issue is that the check isByNameParamType(param.info) tests a param of a MethodType that went through the uncurry type map, which replaces by-name params (DesugaredParameterType).

If we get the param symbol from the method symbol (and not from the AST), then it has a full type history and we see (during uncurry) that it's by-name. And it cleans up the thing explained in the comment ("Read the param symbols before transform(fn)...").

@som-snytt som-snytt changed the title Always just forward args in function expansion Always just forward args in function expansion [ci: last-only] Aug 20, 2024
@som-snytt som-snytt force-pushed the issue/11237-CCE-value-class-SAM branch 3 times, most recently from 88d0d02 to 9cc281e Compare August 22, 2024 00:16
@som-snytt som-snytt closed this Sep 11, 2024
Notice args of forwarder when generating it
@som-snytt som-snytt reopened this Oct 31, 2024
@som-snytt som-snytt force-pushed the issue/11237-CCE-value-class-SAM branch from 9cc281e to adea02c Compare October 31, 2024 21:38
@SethTisue
Copy link
Member

@som-snytt is this ready for review?

@SethTisue SethTisue changed the title Always just forward args in function expansion [ci: last-only] Fix ClassCastException on call to trait method with call-by-name argument, if implemented as SAM Nov 6, 2024
@som-snytt
Copy link
Contributor Author

change title to Fix CCE on CBN arg if SAM.

I'll redraft to review the summertime review.

@som-snytt som-snytt marked this pull request as draft November 6, 2024 20:05
@SethTisue SethTisue modified the milestones: 2.13.16, 2.13.17 Dec 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants