Bug 572873 - Cannot make a static reference to the non-static type T when static generic method returns lambda
Summary: Cannot make a static reference to the non-static type T when static generic m...
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 4.20   Edit
Hardware: PC All
: P3 major (vote)
Target Milestone: 4.20 RC2   Edit
Assignee: Kalyan Prasad Tatavarthi CLA
QA Contact:
URL:
Whiteboard:
Keywords: regression
Depends on:
Blocks:
 
Reported: 2021-04-15 07:12 EDT by Lukas Eder CLA
Modified: 2021-06-03 08:19 EDT (History)
5 users (show)

See Also:
manoj.palat: review+


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Lukas Eder CLA 2021-04-15 07:12:13 EDT
I've installed the JDT Patch with Java 16 support for 2021-03:

- Eclipse JDT (Java Development Tools) Patch with Java 16 support for 2021-03 release	1.2.100.v20210317-0429_JAVA16	org.eclipse.jdt.java16patch.feature.group	Eclipse.org
- Eclipse JDT (Java Development Tools) Source Patch with Java 16 support for 2021-03 release	1.2.100.v20210317-0429_JAVA16	org.eclipse.jdt.java16patch.source.feature.group	Eclipse.org

From:
https://download.eclipse.org/eclipse/updates/4.19-P-builds/

I'm now experiencing a significant compiler regression in a class like this:

// --------------------------------------------------
package test;

import java.util.Iterator;

public class Test {
    static <T> Iterable<T> iterable() {
// Error here:
        return () -> new Iterator<T>() {
            @Override
            public boolean hasNext() {
                return false;
            }

            @Override
// Problem here:
            public T next() {
                return null;
            }
        };
    }
}
// --------------------------------------------------

Two errors are reported, the second one being the cause of the first one:

1. The type new Iterator<T>(){} must implement the inherited abstract method Iterator<T>.next()
2. Cannot make a static reference to the non-static type T

A workaround is to avoid the lambda expression. This works:

// --------------------------------------------------
package test;

import java.util.Iterator;

public class Test {
    static <T> Iterable<T> iterable() {
        return new Iterable<T>() {
            @Override
            public Iterator<T> iterator() {
                return new Iterator<T>() {
                    @Override
                    public boolean hasNext() {
                        return false;
                    }

                    @Override
                    public T next() {
                        return null;
                    }
                };
            }
        };
    }
}
// --------------------------------------------------

Removing static also helps, but is obviously not viable in many cases:

// --------------------------------------------------
package test;

import java.util.Iterator;

public class Test {
    <T> Iterable<T> iterable() {
        return () -> new Iterator<T>() {
            @Override
            public boolean hasNext() {
                return false;
            }

            @Override
            public T next() {
                return null;
            }
        };
    }
}
// --------------------------------------------------
Comment 1 Lukas Eder CLA 2021-04-15 07:14:11 EDT
Another workaround is to assign the iterator to a local variable:

// --------------------------------------------------
package test;

import java.util.Iterator;

public class Test {
    static <T> Iterable<T> iterable() {
        Iterator<T> iterator = new Iterator<T>() {
            @Override
            public boolean hasNext() {
                return false;
            }

            @Override
            public T next() {
                return null;
            }
        };

        return () -> iterator;
    }
}
// --------------------------------------------------
Comment 2 Jinbo Wang CLA 2021-05-30 22:43:32 EDT
Also get this error in 2021-03 + Java 16 patch. If the static generic method includes a lambda class and uses the same generic inside the lambda class, it will report this error.


import java.util.function.Consumer;
public class HelloWorld {
    public static <T> void build(T element) {
        new Thread(() -> {
            new Consumer<T>() {

                @Override
                public void accept(T t) { // report error at this line

                }
            };
        });
    }
}

This error doesn't exist in 2020-12.
Comment 3 Jinbo Wang CLA 2021-06-02 03:45:14 EDT
This is a regression introduced by this change. https://git.eclipse.org/r/c/jdt/eclipse.jdt.core/+/176377

When JDT news a MethodScope for a lambda expression, its isStatic property is inherited from its enclosing method. See https://github.com/eclipse/eclipse.jdt.core/blob/33f60d3da79a0ae0b5266c2c7f357a3f7fdb6519/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java#L270 

In this issue case, the external method 'iterable()' is a static method, so JDT treats the method scope of lambda expression as static as well. The method 'public T next()' in the lambda body is considered to be inside a static context and report the error. See https://github.com/eclipse/eclipse.jdt.core/blob/33f60d3da79a0ae0b5266c2c7f357a3f7fdb6519/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java#L3325
Comment 4 Andrey Loskutov CLA 2021-06-02 04:59:01 EDT
Manoy, Jay, Kalyan: this affects not only Java 16 code, probability is high this will affect many people. Any chance to fix it in 4.20?
Comment 5 Manoj N Palat CLA 2021-06-02 05:30:29 EDT
(In reply to Andrey Loskutov from comment #4)
> Manoy, Jay, Kalyan: this affects not only Java 16 code, probability is high
> this will affect many people. Any chance to fix it in 4.20?

Kalyan and I was thinking about going beyond the scope of class if it is anonymous since the inheritance comes from the methodscope for anon classes. Kalyan will implement this and test I will work with him to see whether we can push a solution in RC2 itself..
Comment 6 Eclipse Genie CLA 2021-06-02 06:22:40 EDT
New Gerrit change created: https://git.eclipse.org/r/c/jdt/eclipse.jdt.core/+/181266
Comment 7 Eclipse Genie CLA 2021-06-02 11:02:15 EDT
New Gerrit change created: https://git.eclipse.org/r/c/jdt/eclipse.jdt.core/+/181289
Comment 8 Manoj N Palat CLA 2021-06-02 20:59:18 EDT
+1 for RC2
Comment 9 Manoj N Palat CLA 2021-06-03 00:28:31 EDT
(In reply to Eclipse Genie from comment #6)
> New Gerrit change created:
> https://git.eclipse.org/r/c/jdt/eclipse.jdt.core/+/181266

There is a failure in the test. However, I am taking a decision to commit based on all the following observations:

1)This is a regression that needs to be fixed.
2a) The failure due to the tests is via time-out and individual local runs passed.
2b) Multiple cumulative Local runs have passed taking only just around an hour each.
3) There is nothing in the code change that is going to cause such an increase in time based on the following code observations:

 (a) The code change just replaces the rhs of a boolean variabe(insideClassContext) assignment with a single line with a function call (!sourceType.isAnonymousType())
 (b) The boolean variable insideClassContext is not participating in decision making for loop continuation
(c) The function sourceType.isAnonymousType() implementations do not contain loop but just a bitwise operation.
With the reasons cited in 2 and 3 (subsections) concluding this timeout due to an infrastructure issue and given that this is a regression, giving this case an exception by taking the decision to commit within the RC2 time-frame.
Comment 11 Jay Arthanareeswaran CLA 2021-06-03 08:19:58 EDT
Verified for 4.20 RC2 with build I20210603-0040.