Strange covariant/generics downcasting issue with JDK8

Paul Sandoz paul.sandoz at oracle.com
Tue Oct 8 05:33:37 PDT 2013


[i am not subscribed to compiler-dev]

You are using raw types for the condition:

  public class RegexMatch extends Condition

replace with:

  public class RegexMatch extends Condition<Object>

and it will compile.

So it seems the raw types for the parameter is triggering the compiler to loose the type information:

class ListAssert <T> extends AbstractIterableAssert<org.fest.assertions.api.ListAssert<T>,java.util.List<T>,T>

class AbstractIterableAssert <S extends org.fest.assertions.api.AbstractIterableAssert<S,A,T>, A extends java.lang.Iterable<T>, T> 
    extends org.fest.assertions.api.AbstractAssert<S,A>

class AbstractAssert <S extends org.fest.assertions.api.AbstractAssert<S,A>, A>  implements org.fest.assertions.core.Assert<S,A> {
    ...
    public S doesNotHave(org.fest.assertions.core.Condition<? super A> condition) { /* compiled code */ }
    ...
}

        assertThat(strings).describedAs("test")
                .isNotEmpty()
                .doesNotHave(regexMatch("moo.*"))  // raw type for A
                .has(regexMatch("th.*"))  // raw type for A
                .hasSize(3);


A is undefined so S also becomes undefined but only when two calls with raw types occur. I suspect javac is forgetting to propagate some information when following the chain of methods.

Paul.

On Oct 5, 2013, at 11:15 PM, Mark Derricutt <mark at talios.com> wrote:

> Just following up on this now that Java One is over and hopefully more eyes can help me track down just what's going on here.
> 
> Try as a may I've not yet been able to distil this problem down to a single class file to reproduce, but the extracted project on github ( https://github.com/talios/jdk8-covariantfail ) continues to fail under JDK8.
> 
> The FEST-Assert library uses a fairly complex class hierarchy, employing some gnarly "self type" generics in order to provide the appropriate type to javac.
> 
> When compiling under Java 7 the project works fine, but when compiling under JDK8 ( developer preview, and also  HEAD/master builds from mercurial ) fail to compile the 2nd test function, as the inferred return type is getting lost somewhere along the way.
> 
> If the 2nd test is broken up with intermediate variables then the call to hasSize(3) on line 37 compiles fine.
> 
> Given this works under Java 7, and fails under Java 8 - this is clearly some form of regression/bug somewhere, 
> just exactly what is broken, and how to reproduce it is so far eluding me, any help in isolating this issue so that a relevant bug can be raised, tracked, and fixed would be helpful.
> 
> Mark
> 
> 
> --
> Mark Derricutt — twitter — podcast — blog — google+
> 
> On 23/09/2013, at 5:30 PM, Mark Derricutt <mark at talios.com> wrote:
> 
>> Hi all,
>> 
>> Since you're all enjoying JavaOne at the moment I was thinking it was about time that I starting building $work's project under JDK8 and give it some love over my normal small projects and ran into an odd issue with generics/covariant returns that works fine under 7u40 and breaks under 8DP.
>> 
>> I've extracted this into a simple project [1] which I've pushed to github, I've added a comment on the code that breaks under JDK8.
>> 
>> The code in question is:
>> 
>> List<Strings> strings…
>> 
>> assertThat(strings).describedAs("test")
>>  .isNotEmpty()
>>  .has(regexMatch("th.*"))
>>  .doesNotHave(regexMatch("moo.*"))
>>  .hasSize(3);
>> 
>> When calling assertThat(strings) a class of type ListAssertion is returned, and passed along the call chain.
>> 
>> The problem appears to be that when doesNotHave() returns, javac no longer sees a ListAssertion but rather a top level AbstractAssertion on which doesNotHave is defined:
>> 
>>  /** {@inheritDoc} */
>>  public S has(Condition<? super A> condition) {
>>    conditions.assertHas(info, actual, condition);
>>    return myself;
>>  }
>> 
>>  /** {@inheritDoc} */
>>  public S doesNotHave(Condition<? super A> condition) {
>>    conditions.assertDoesNotHave(info, actual, condition);
>>    return myself;
>>  }
>> 
>> Now, both has() and doesNotHave() appear to have the exact same signature, yet commenting out the call to doesNotHave() doesn't appear to trigger what appears to be a down casting to AbstractAssertion which doesn't include a method called hasSize(), the output I see from javac is:
>> 
>> ~/D/covariantfail (master|…) $ /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/bin/java -version
>> java version "1.8.0-ea"
>> Java(TM) SE Runtime Environment (build 1.8.0-ea-b106)
>> Java HotSpot(TM) 64-Bit Server VM (build 25.0-b48, mixed mode)
>> 
>> ~/D/covariantfail (master|…) $ /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/bin/javac -version
>> javac 1.8.0-ea
>> 
>> /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/bin/javac -d /Users/amrk/Dropbox/covariantfail/target/test-classes -classpath /Users/amrk/Dropbox/covariantfail/target/test-classes:/Users/amrk/Dropbox/covariantfail/target/classes:/Users/amrk/.m2/repository/org/testng/testng/6.8.5/testng-6.8.5.jar:/Users/amrk/.m2/repository/junit/junit/4.10/junit-4.10.jar:/Users/amrk/.m2/repository/org/hamcrest/hamcrest-core/1.1/hamcrest-core-1.1.jar:/Users/amrk/.m2/repository/org/beanshell/bsh/2.0b4/bsh-2.0b4.jar:/Users/amrk/.m2/repository/com/beust/jcommander/1.27/jcommander-1.27.jar:/Users/amrk/.m2/repository/org/yaml/snakeyaml/1.6/snakeyaml-1.6.jar:/Users/amrk/.m2/repository/org/easytesting/fest-assert-core/2.0M10/fest-assert-core-2.0M10.jar:/Users/amrk/.m2/repository/org/easytesting/fest-util/1.2.5/fest-util-1.2.5.jar: -sourcepath /Users/amrk/Dropbox/covariantfail/src/test/java: /Users/amrk/Dropbox/covariantfail/src/test/java/com/talios/RegexMatch.java /Users/amrk/Dropbox/covariantfail/src/test/java/com/talios/AppTest.java -g -nowarn -target 1.5 -source 1.5 -encoding UTF-8
>> /Users/amrk/Dropbox/covariantfail/src/test/java/com/talios/AppTest.java:37: error: cannot find symbol
>>                .hasSize(3);
>>                ^
>>  symbol:   method hasSize(int)
>>  location: class AbstractAssert
>> Note: /Users/amrk/Dropbox/covariantfail/src/test/java/com/talios/AppTest.java uses unchecked or unsafe operations.
>> Note: Recompile with -Xlint:unchecked for details.
>> 1 error
>> 
>> 
>> Is this a known bug in the JDK at all? I'm quite surprised no one else has hit something like this before if its not?
>> 
>> 
>> 
>> 
>> [1] https://github.com/talios/jdk8-covariantfail
>> [2] https://github.com/talios/jdk8-covariantfail/commit/faa016e57f754230da46e3453b964d497ec065b5#commitcomment-4151896
>> 
>> 
>> 
>> -- Mark Derricutt ( mark at talios.com )
>> — twitter: https://twitter.com/talios
>> — podcast: http://www.illegalargument.com
>> — blog: http://www.theoryinpractice.net
>> — google+: http://gplus.to/talios
>> 
> 

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 841 bytes
Desc: Message signed with OpenPGP using GPGMail
Url : http://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20131008/d49a37af/signature.asc 


More information about the compiler-dev mailing list