为什么编译这段代码会导致编译器堆栈溢出?

发布于 2024-12-17 14:40:26 字数 948 浏览 0 评论 0原文

interface Pong<T> {}
class Ping<T> implements Pong<Pong<? super Ping<Ping<T>>>> {
    static void Ping() {
        Pong<? super Ping<Long>> Ping = new Ping<Long>();
    }
}

尝试编译此代码会出现错误:

The system is out of resources.
Consult the following stack trace for details.
java.lang.StackOverflowError
    at com.sun.tools.javac.code.Types$23.visitClassType(Types.java:2579)
    at com.sun.tools.javac.code.Type$ClassType.accept(Type.java:554)
    at com.sun.tools.javac.code.Types$UnaryVisitor.visit(Types.java:3260)
    at com.sun.tools.javac.code.Types$23.visitClassType(Types.java:2592)
    at com.sun.tools.javac.code.Types$23.visitClassType(Types.java:2579)
    at com.sun.tools.javac.code.Type$ClassType.accept(Type.java:554)
    ...

代码由 github 上的 etorreborre 提供。

interface Pong<T> {}
class Ping<T> implements Pong<Pong<? super Ping<Ping<T>>>> {
    static void Ping() {
        Pong<? super Ping<Long>> Ping = new Ping<Long>();
    }
}

Trying to compile this gives the error:

The system is out of resources.
Consult the following stack trace for details.
java.lang.StackOverflowError
    at com.sun.tools.javac.code.Types$23.visitClassType(Types.java:2579)
    at com.sun.tools.javac.code.Type$ClassType.accept(Type.java:554)
    at com.sun.tools.javac.code.Types$UnaryVisitor.visit(Types.java:3260)
    at com.sun.tools.javac.code.Types$23.visitClassType(Types.java:2592)
    at com.sun.tools.javac.code.Types$23.visitClassType(Types.java:2579)
    at com.sun.tools.javac.code.Type$ClassType.accept(Type.java:554)
    ...

Code courtesy of etorreborre on github.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

肩上的翅膀 2024-12-24 14:40:26

因为编译器无法确定 Long 是否为 Pong(即 PingsuperPingLong 的 >Ping 或是否是扩展 PongPingPing Pong 的 code> ...但我可能是 错误的。

Because the compiler cannot decide whether a Long is a Pong that is the super of a Ping of a Ping of a Long or whether it is a Ping of a Ping of something that extends a Pong of a Pong ... but I may be wrong.

穿透光 2024-12-24 14:40:26

显然,这是Java编译器中的一个错误。编译器不应该崩溃,尤其是在这么小的程序上。

它甚至可能是 Java 语言规范中的一个漏洞;即 JLS 作者没有考虑到的泛型中一个模糊的边缘情况。

但是(IMO)这只不过是一种好奇,除非你能想出一个不那么明显故意来破坏编译器的例子。我的意思是,这个示例代码并不完全有意义……


对 Java 编译器的实现有深入了解的人可能会弄清楚为什么这会导致堆栈溢出。但除非那个人也打算修复这个错误,否则它几乎没有意义。除非有人能提出一个有意义的例子来触发同样的问题,否则我看不到修复它的任何价值。

Clearly, it is a bug in the Java compiler. The compiler shouldn't crash, especially on a program so small.

It could even be a hole in the Java Language Specification; i.e. an obscure edge case in generics that the JLS authors haven't considered.

But (IMO) this is nothing more than a curiosity, unless you can come up with an example that isn't so obviously contrived to break the compiler. I mean, this example code isn't exactly meaningful ...


Someone with a deep understanding of the Java compiler's implementation could probably figure out why this causes a stack overflow. But it is hardly relevant unless that person is also going to fix the bug. And unless someone can come up with a meaningful example that triggers the same problem, I can't see any value in fixing it.

骷髅 2024-12-24 14:40:26

我有一个同事在实际代码中也有类似的问题。在那里,他有一个带有 2 个类型参数的抽象基类,该基类有两个子类将它们固定为具体类型。基本上,这允许在抽象类中定义完整的逻辑,而不必在具有交换的具体类型的两个子类中复制代码。

基本上,代码是这样的:

public abstract class AImpl<X extends A<Y>, Y extends A<X>> {
    public X foo(Y o) {
        return o.doStuff();
    }

    public Y bar(X o) {
        return o.doStuff();
    }
}

class VImpl extends AImpl<V, E> {}
class EImpl extends AImpl<E, V> {}

interface A<T> {
    T doStuff();
}

interface V extends A<E> {}
interface E extends A<V> {}

该代码实际上可以编译。实际上,不仅有2个子类,还有更深层次的类型层次结构,例如VImpl和EImpl的三个变体,每个变体都有任意多个子类。嗯,实际上,有 3 个类型参数,并且限制有点复杂,如上所示。

当只有 VImpl 和 EImpl 两个变体时,它仍然编译得很好。一旦添加第三个变体,他就会在编译器中出现堆栈溢出。也就是说,Eclipse 编译器仍然能够编译代码,因此看起来它只是 javac 递归地执行一些应该更好地迭代执行的操作。

不幸的是,我们无法将完整的代码库缩减为适合错误报告的最小示例......

I have a collegue which has a similar problem in real code. There, he has an abstract base class with 2 type parameters that has two subclasses fixing them to concrete types. Basically, this allows for defining the complete logic in the abstract class instead of having to duplicate the code in both subclasses with swapped concrete types.

Basically, the code is something along these lines:

public abstract class AImpl<X extends A<Y>, Y extends A<X>> {
    public X foo(Y o) {
        return o.doStuff();
    }

    public Y bar(X o) {
        return o.doStuff();
    }
}

class VImpl extends AImpl<V, E> {}
class EImpl extends AImpl<E, V> {}

interface A<T> {
    T doStuff();
}

interface V extends A<E> {}
interface E extends A<V> {}

This code actually compiles. In reality, there are not just 2 subclasses, but a deeper type hierarchy, e.g., three variants of VImpl and EImpl, each of which have arbitrary many subclasses. Well, and actually, there are 3 type parameters, and the restrictions are a bit more complicated as shown above.

When there were only two variants of VImpl and EImpl, it still compiled fine. As soon as the third variants were added, he got that stack overflow in the compiler. That said, the Eclipse compiler is still able to compile the code, so it seems that it's simply javac doing something recursively which it should better do iteratively.

Unfortunately, we are not able to reduce the complete code base to some minimal example suitable for a bug report...

花海 2024-12-24 14:40:26

我在 JDK 8_u25 中遇到了同样的问题,更新到 JDK 8u_65 解决了这个问题。

I experienced the same with some generic stuff in JDK 8_u25, updating to JDK 8u_65 resolved the issue.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文