Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 518157

Summary: A Class<? extends RawType> = Impl.class where RawType is a nested interface within a generic class fails compilation
Product: [Eclipse Project] JDT Reporter: David Elliott <dfe+eclipse>
Component: CoreAssignee: Till Brychcy <register.eclipse>
Status: VERIFIED FIXED QA Contact: Stephan Herrmann <stephan.herrmann>
Severity: major    
Priority: P3 CC: abuehler, dfe+eclipse, jarthana, register.eclipse, stephan.herrmann
Version: 4.7Flags: stephan.herrmann: review? (stephan.herrmann)
Target Milestone: 4.8 M4   
Hardware: Macintosh   
OS: Mac OS X   
See Also: https://git.eclipse.org/r/100969
https://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=30089883a917eade20b36479b5c83c148ec35e5a
https://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=143b17c103956858fc3a331c687d4e89e8148cdd
https://git.eclipse.org/r/110746
https://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=1eb1eeafeb83314c2c9956f72dab41ada788a34c
Whiteboard:
Bug Depends on:    
Bug Blocks: 526423    

Description David Elliott CLA 2017-06-12 20:58:14 EDT
*** Overview

This is a new bug in Oxygen; Neon is able to compile this code, as is javac.

The problem occurs when a generic interface is nested inside a generic class.  If the outer class is not generic then JDT behaves correctly.

Unfortunately this is unavoidable in some cases (e.g. Map.Entry is a generic interface nested inside a generic class).

*** Steps to reproduce
Try to compile this class:

package com.example;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Demonstration class
 * <p>
 * Bug is that if the class containing a nested generic interface is itself generic then
 * JDT does not recognize implementations of it as extending it.
 * @param <Oops>  Any gneeric parameter, the mere existence of which causes the bug.
 */
class RawClassParameterizationBug<Oops> {

    public interface Example<K,V> {
    }

    public static class DefaultExample<K,V> implements Example<K,V> {
    }

    public static class SpecializedExample implements Example<Long,String> {
    }

    /**
     * Works when the interface is fully specialized
     */
    static final Class<? extends Example> works = SpecializedExample.class;

    /**
     * {@literal Type mismatch: cannot convert from Class<RawClassParameterizationBug.DefaultExample> to Class<? extends RawClassParameterizationBug.Example>}
     */
    static final Class<? extends Example> fails = DefaultExample.class;

}

/**
 * Second demonstration, making use of JRE interfaces.
 */
class AlternateRawClassParameterizationBug {

    abstract static class MapEntry<K,V> implements Map.Entry<K, V> {
    }

    /**
     * Fails because Entry is a generic interface nested with Map which is also
     * generic
     */
    static final Class<? extends Map.Entry> mapFails = MapEntry.class;

    /**
     * Works because List is a non-nested interface.  Although nesting a generic
     * interface in a non-generic class would also work.
     */
    static final Class<? extends List> listWorks = ArrayList.class;

}


*** Expected Results

All of this code should compile.

*** Actual Results

It does not compile, failing to understand that raw DefaultExample does in fact extend raw Example and raw MapEntry does in fact extend raw Map.Entry.

It does, however, correctly recognize that raw ArrayList extends raw List.
Comment 1 Stephan Herrmann CLA 2017-07-07 14:42:06 EDT
Putting this on our radar, but currently Java 9 work has priority.
Comment 2 Stephan Herrmann CLA 2017-07-07 14:46:19 EDT
Meant to mention that the change in behavior was caused by the fix in bug 460491.
Comment 3 Eclipse Genie CLA 2017-07-10 02:43:08 EDT
New Gerrit change created: https://git.eclipse.org/r/100969
Comment 4 Till Brychcy CLA 2017-07-11 02:31:22 EDT
ANALYSIS:
At the location...
 RawTypeBinding(TypeBinding).isTypeArgumentContainedBy(TypeBinding) line: 1271 
 ParameterizedTypeBinding.isEquivalentTo(TypeBinding) line: 837	
 ParameterizedTypeBinding(ReferenceBinding).isCompatibleWith0(TypeBinding, Scope) line: 1325	 
 ParameterizedTypeBinding(ReferenceBinding).isCompatibleWith(TypeBinding, Scope) line: 1300	
 FieldDeclaration.resolve(MethodScope) line: 271	
 TypeDeclaration.resolve() line: 1131	
 TypeDeclaration.resolve(CompilationUnitScope) line: 1308	
 CompilationUnitDeclaration.resolve() line: 605	
 AbstractRegressionTest$4(Compiler).process(CompilationUnitDeclaration, int) line: 867	
...the variable "match" (returned by upperBound.findSuperTypeOriginatingFrom(otherBound)) is:
  RawClassParameterizationBug.Example#RAW
but "otherBound" is:
  RawClassParameterizationBug#RAW.Example#RAW

So in the first case, the (statically) enclosing class is not RAW.
This is caused by addition of "&& !originalType.isStatic()" to 
the if() in line 521 of org.eclipse.jdt.internal.compiler.lookup.Scope.Substitutor.substitute(Substitution, TypeBinding) for bug 460491

PATCH:
A possible patch could be  in  org.eclipse.jdt.internal.compiler.lookup.Scope.Substitutor.substitute so "match" ends up to be "RawClassParameterizationBug#RAW.Example#RAW" (like pre-bug 460491)

But I think it is really better, if statically enclosing classes are always the original() type (so "otherBound" will be "RawClassParameterizationBug.Example#RAW"):
(In reply to Eclipse Genie from comment #3)
> New Gerrit change created: https://git.eclipse.org/r/100969
Comment 5 Till Brychcy CLA 2017-07-11 02:35:53 EDT
Stephan (when you have time), do you agree with the solution?

As this is a regression in 4.7, this should probably go into 4.7.1, too.
But as I understand that the master should better stay untouched until the Java 9 patch is finished, I'm setting 4.8M2 as target.
Comment 7 Till Brychcy CLA 2017-10-10 13:26:41 EDT
(In reply to Eclipse Genie from comment #6)
> Gerrit change https://git.eclipse.org/r/100969 was merged to [master].
> Commit:
> http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/
> ?id=30089883a917eade20b36479b5c83c148ec35e5a

Released for 4.8M3
Comment 8 Till Brychcy CLA 2017-10-10 13:32:13 EDT
I have merged this now, so it some time in a milestone build before a
possible backport to 4.7.2, for which I'm reopening this.
Comment 9 Till Brychcy CLA 2017-10-25 01:47:14 EDT
Causes bug 526423, so I'll revert the commit for 4.8M3.
Comment 11 Eclipse Genie CLA 2017-10-30 12:24:16 EDT
New Gerrit change created: https://git.eclipse.org/r/110746
Comment 12 Till Brychcy CLA 2017-11-27 01:48:15 EST
(In reply to Eclipse Genie from comment #11)
> New Gerrit change created: https://git.eclipse.org/r/110746

The isStatic()-check needed to be done at beginning of getRawType(), before checking for derived types.

Released for 4.8M4
Comment 13 Till Brychcy CLA 2017-11-27 13:48:38 EST
It seems the Genie still still doesn't work after the bugzilla upgrade.

Resolved with https://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=1eb1eeafeb83314c2c9956f72dab41ada788a34c
Comment 14 Stephan Herrmann CLA 2017-11-30 07:49:52 EST
*** Bug 527632 has been marked as a duplicate of this bug. ***
Comment 15 Jay Arthanareeswaran CLA 2017-12-06 06:07:30 EST
Verified for 4.8 M4 using build I20171205-2000.