为什么以下代码崩溃javac?可以做些什么呢?

时间:2015-09-15 20:02:21

标签: java generics types compilation stack-overflow

我正在阅读this article关于" Java中的奇怪事情"我发现了一个有趣的概念:不可判定的类型。

考虑以下三个类/接口:

public interface Type<T> { }
public class D<P> implements Type<Type<? super D<D<P>>>> { }
public class WildcardTest {
  Type<? super D<Byte>> d = new D<Byte>();
}

显然问题是DType<? super D<Byte>>是不可判定的;任何人都可以进一步解释这个吗?

在尝试编译javac 1.8.0_60时,

StackOverflowError会引发很长的WildcardTest

The system is out of resources.
Consult the following stack trace for details.
java.lang.StackOverflowError
        at com.sun.tools.javac.code.Types$UnaryVisitor.visit(Types.java:4640)
        at com.sun.tools.javac.code.Types$26.visitClassType(Types.java:3834)
        at com.sun.tools.javac.code.Types$26.visitClassType(Types.java:3826)
        at com.sun.tools.javac.code.Type$ClassType.accept(Type.java:778)
        at com.sun.tools.javac.code.Types$UnaryVisitor.visit(Types.java:4640)
        at com.sun.tools.javac.code.Types$26.visitClassType(Types.java:3839)
        at com.sun.tools.javac.code.Types$26.visitClassType(Types.java:3826)
        at com.sun.tools.javac.code.Type$ClassType.accept(Type.java:778)
        at com.sun.tools.javac.code.Types$UnaryVisitor.visit(Types.java:4640)
        at com.sun.tools.javac.code.Types$26.visitClassType(Types.java:3839)
        at com.sun.tools.javac.code.Types$26.visitClassType(Types.java:3826)
        at com.sun.tools.javac.code.Type$ClassType.accept(Type.java:778)
        at com.sun.tools.javac.code.Types$UnaryVisitor.visit(Types.java:4640)
        at com.sun.tools.javac.code.Types$26.visitClassType(Types.java:3839)
        at com.sun.tools.javac.code.Types$26.visitClassType(Types.java:3826)
        at com.sun.tools.javac.code.Type$ClassType.accept(Type.java:778)

此代码也会崩溃整个Eclipse IDE。

作者提交了一个错误with the Eclipse team,但它没有获得任何投票(除了我的投票)。什么都可以做?这只是编译器形式的Halting Problem吗?

There is also a link to this paper about it in the article, but I am hoping there is a more straightforward explanation.

1 个答案:

答案 0 :(得分:3)

正如Tunaki在评论中指出的那样,这可以追溯到由Pierce(TAPL作者)共同撰写的Microsoft research paper。事实上,Tate等人的问题。给出了附录A中的示例2(Byte = TType = ND = C)。

堆栈溢出

首先,让我们弄清楚它为什么会炸掉堆栈。要做到这一点,最好提醒自己编译器检查类型的方式与我们的方式非常相似。我们遇到的问题会遇到。

// To be determined:
D<Byte> <: Type<? super D<Byte>>

// using D<P> implements Type<Type<? super D<D<P>>>>

Type<Type<? super D<D<Byte>>>> <: Type<? super D<Byte>>

// The outermost type constructor (Type) matches. For the
// suptyping relationship to hold, we have to test the type
// arguments. (Sides are flipped due to contravariance.)

D<Byte> <: Type<? super D<D<Byte>>>

// Mhh… That looks an awful lot like above.
// Feel free to rinse and repeat until your "stack" blows too… ;)

皮尔斯等人。描述了与论文第3部分中的例2相似的回归。它略有不同,因为Java不允许类型变量的下限,仅在通配符上。

可以做些什么?

与Pierce等人给出的实施例1相似,该回归遵循一种模式。该模式可以由编译器检测,并且可以假设子类型关系在共感解释下保持。 (这与F-bounded多态性的推理相同,即Enum<E extends Enum<E>>。你输入一个类似的无限回归而没有矛盾。)

<强>不可判定吗

可以通过更智能的算法解决给定的问题。 Java的类型系统是否可判定仍然是一个悬而未决的问题。

如果Java允许类型参数的下限,那么它将是不可判定的(半可判定的)。然而,在他们的论文中,皮尔斯等人。定义可以使这种类型系统再次可判定的限制。顺便提一下,Scala采用了这些限制,具有讽刺意味的是具有图灵完整型系统。