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
Fix generation of derived value classes wrapping Unit, Null, and Nothing #5938
Conversation
*/ | ||
def equalsCore(eqmeth: Symbol, accessors: List[Symbol]) = { | ||
def usefulEquality(acc: Symbol): Boolean = | ||
acc.info.resultType != NothingTpe && acc.info.resultType != NullTpe && acc.info.resultType != UnitTpe |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would think that these three types would have an alias somewhere, but I couldn't find it. Feel free to make me swap it out for something else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The three comparisons look OK to me, but you could extract acc.info.resultType
into a local.
// scala/bug#9240 | ||
final class AnyValNothing(val self: Nothing) extends AnyVal | ||
final class AnyValNull (val self: Null ) extends AnyVal | ||
// scala/bug#10361 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your references are inverted. The below is scala/bug#9240, and the ones above are scala/bug#10361.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Woops. Good catch, thanks!
def avn = new AnyValNull(null) | ||
assert(avn == avn) | ||
/*this throws NPE right now b/c null.hashCode() does...*/ | ||
//assert(avn.hashCode() == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switch this (and avu below) to .##
. If this here returns 0 rather than throw an NPE then you also fixed scala/bug#7396 with this patch!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sadly, that problem is deeper than this, as far as I can tell. No hat tricks here 😞
emit(asm.Opcodes.ATHROW) | ||
if (l == UNIT && r == srBoxedUnitRef) { | ||
/* special case: we can "cast" V to Lscala/runtime/BoxedUnit; by summoning the latter */ | ||
mnode.instructions.add(backendUtils.getBoxedUnit) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be handled in erasure, at the place where the cast is inserted, see where cast
is invoked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 It would also fix the problem for the three backends at the same time :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How rude of me, I almost forgot that non-JVM backends exist... Changed!
*/ | ||
def equalsCore(eqmeth: Symbol, accessors: List[Symbol]) = { | ||
def usefulEquality(acc: Symbol): Boolean = | ||
acc.info.resultType != NothingTpe && acc.info.resultType != NullTpe && acc.info.resultType != UnitTpe |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The three comparisons look OK to me, but you could extract acc.info.resultType
into a local.
b4061c2
to
251c1fd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice work, thanks a lot!
@@ -530,6 +530,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { | |||
if (cast) { | |||
mnode.visitTypeInsn(asm.Opcodes.NEW, jlClassCastExceptionRef.internalName) | |||
bc dup ObjectRef | |||
mnode.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, jlClassCastExceptionRef.internalName, INSTANCE_CONSTRUCTOR_NAME, "()V", true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This certainly looks right, but I wonder how we can end up here. We'd need an x.asInstanceOf[T]
where x
has primitive type. Any idea? When I write 1.asInstanceOf[String]
, the value is boxed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, Scala.js has corresponding code in its codegen, but I'm not sure it is ever exercised ^^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't figure out how to get this branch to get hit... we should have already inserted the boxing by now. This somewhat implies to me that this could, possibly should, be an assertion or similar; before this patch, this would always generate bytecode that doesn't pass verification.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with your analysis.
When in doubt about old code like this, we tend to add a devWarning
(which issues a warning under -Ydebug
or -Xdev
) rather than a hard assert
to fail gracefully
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Devs are duly warned. Good idea!
|
…ing. All sorts o' specialness going on here. Unit morphs into a BoxedUnit when it's in a field, but void when it's the return type of a method, which is expected. This means, though, that the unboxing method of a Unit-wrapping value class has the signature `()V`, not `()Lscala/runtime/BoxedUnit`, so attempting to use its value in the equals method spits out some wonderful invalid bytecode, instead. Similar sadness occurs for Nothing and Null as well. The "solution" is to not even bother to check for equality, as we've only got at most one legitimate value of each of these types. Because the code is shared with `case class`es, this also changes the bytecode we generate for them. Obviously this is an "unrelated change" as far as the bugs this is meant to fix go, but it's innocuous enough as far as I can tell. I also slipped a constructor call into the generated `ClassCastException` that gets thrown when we are asked to emit a cast for a primitive type in `BCodeBodyBuilder`, so we generate valid bytecode if we ever wind in that branch. Discussion on scala#5938 implies that this branch shouldn't ever be reached, so add a devWarning now that it doesn't cause an obvious error. Fixes scala/bug#9240 Fixes scala/bug#10361
251c1fd
to
c9d84a1
Compare
All sorts o' specialness going on here.
Unit
morphs into aBoxedUnit
when it's in a field, but void when it's the return type of a method, which is expected. This means, though, that the unboxing method of a Unit-wrapping value class has the signature()V
, not()Lscala/runtime/BoxedUnit
, so attempting to use its value in the equals method spits out some wonderful invalid bytecode, instead. Similar sadness occurs for Nothing and Null as well.The "solution" is to not even bother to check for equality, as we've only got at most one legitimate value of each of these types. Because the code is shared with
case class
es, this also changes the bytecode we generate for them. Obviously this is an "unrelated change" as far as the bugs this is meant to fix go, but it's innocuous enough as far as I can tell.I also slipped a constructor call into the generated
ClassCastException
that gets thrown when we are asked to emit a cast for a primitive type inBCodeBodyBuilder
, so we generate valid bytecode in that case. I've got no idea howNEW; DUP; ATHROW
is possibly ever legitimate, but hey.Fixes scala/bug#9240
Fixes scala/bug#10361