Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8269121: Type inference bug with method references

Closed
@@ -26,6 +26,8 @@

import sun.invoke.util.Wrapper;

import java.lang.reflect.Modifier;

import static java.lang.invoke.MethodHandleInfo.*;
import static sun.invoke.util.Wrapper.forPrimitiveType;
import static sun.invoke.util.Wrapper.forWrapperType;
@@ -291,7 +293,7 @@ void validateMetafactoryArgs() throws LambdaConversionException {
for (int i=samStart; i<implArity; i++) {
Class<?> implParamType = implMethodType.parameterType(i);
Class<?> dynamicParamType = dynamicMethodType.parameterType(i - capturedArity);
if (!isAdaptableTo(dynamicParamType, implParamType, true)) {
if (!isAdaptableTo(dynamicParamType, implParamType, true, true)) {
throw new LambdaConversionException(
String.format("Type mismatch for lambda argument %d: %s is not convertible to %s",
i, dynamicParamType, implParamType));
@@ -319,9 +321,11 @@ private void checkDescriptor(MethodType descriptor) throws LambdaConversionExcep
for (int i = 0; i < dynamicMethodType.parameterCount(); i++) {
Class<?> dynamicParamType = dynamicMethodType.parameterType(i);
Class<?> descriptorParamType = descriptor.parameterType(i);
if (!descriptorParamType.isAssignableFrom(dynamicParamType)) {
String msg = String.format("Type mismatch for dynamic parameter %d: %s is not a subtype of %s",
i, dynamicParamType, descriptorParamType);
if (!descriptorParamType.isAssignableFrom(dynamicParamType) &&
(descriptorParamType.isPrimitive() || dynamicParamType.isPrimitive() ||
!sideCastExists(descriptorParamType, dynamicParamType))) {
String msg = String.format("Type mismatch for dynamic parameter %d: %s is not convertible to %s",
i, dynamicParamType, descriptorParamType);
throw new LambdaConversionException(msg);
}
}
@@ -343,6 +347,18 @@ private void checkDescriptor(MethodType descriptor) throws LambdaConversionExcep
* @return True if 'fromType' can be passed to an argument of 'toType'
*/
private boolean isAdaptableTo(Class<?> fromType, Class<?> toType, boolean strict) {
return isAdaptableTo(fromType, toType, strict, false);
}

/**
* Check type adaptability for parameter types.
* @param fromType Type to convert from
* @param toType Type to convert to
* @param strict If true, do strict checks, else allow that fromType may be parameterized
* @param allowSideCast If true, then sicasts are allowed
* @return True if 'fromType' can be passed to an argument of 'toType'
*/
private boolean isAdaptableTo(Class<?> fromType, Class<?> toType, boolean strict, boolean allowSideCast) {
if (fromType.equals(toType)) {
return true;
}
@@ -369,12 +385,32 @@ private boolean isAdaptableTo(Class<?> fromType, Class<?> toType, boolean strict
return !strict;
}
} else {
// both are reference types: fromType should be a superclass of toType.
return !strict || toType.isAssignableFrom(fromType);
// both are reference types: fromType should be a superclass of toType or there should exist
// a sidecast from fromType to toType
return !strict || toType.isAssignableFrom(fromType) || (allowSideCast && sideCastExists(fromType, toType));
}
}
}

/**
* Check if a sidecas exist
* @param fromType Type to convert from
* @param toType Type to convert to
* @return True if a sidecast exists from 'fromType' to 'toType'
*/
private boolean sideCastExists(Class<?> fromType, Class<?> toType) {
if (toType.isInterface() && fromType.isInterface()) {
return true;
} else if (toType.isInterface()) {
return ((fromType.getModifiers() & Modifier.FINAL) == 0);
} else if (fromType.isInterface()) {
return ((toType.getModifiers() & Modifier.FINAL) == 0);
} else if (toType.isArray() && fromType.isArray()) {
return sideCastExists(fromType.getComponentType(), toType.getComponentType());
}
return false;
}

/**
* Check type adaptability for return types --
* special handling of void type) and parameterized fromType
@@ -26,7 +26,10 @@
* @bug 8035776 8173587
* @summary metafactory should fail if instantiatedMethodType does not match sam/bridge descriptors
*/

import java.lang.invoke.*;
import java.lang.reflect.Modifier;

import java.util.*;

public class MetafactoryDescriptorTest {
@@ -37,8 +40,6 @@ static MethodType mt(Class<?> ret, Class<?>... params) {
return MethodType.methodType(ret, params);
}

public interface I {}

public static class C {
public static void m_void(String arg) {}
public static boolean m_boolean(String arg) { return true; }
@@ -52,6 +53,10 @@ public static void m_void(String arg) {}
public static String m_String(String arg) { return ""; }
public static Integer m_Integer(String arg) { return 23; }
public static Object m_Object(String arg) { return new Object(); }
public static I m_I(String arg) { return new I() {}; }
public static J m_J(String arg) { return new J() {}; }
public static CC m_CC(String arg) { return new CC(); }
public static FF m_FF(String arg) { return new FF(); }

public static String n_boolean(boolean arg) { return ""; }
public static String n_char(char arg) { return ""; }
@@ -64,6 +69,10 @@ public static void m_void(String arg) {}
public static String n_String(String arg) { return ""; }
public static String n_Integer(Integer arg) { return ""; }
public static String n_Object(Object arg) { return ""; }
public static String n_I(I arg) { return ""; }
public static String n_J(J arg) { return ""; }
public static String n_CC(CC arg) { return ""; }
public static String n_FF(FF arg) { return ""; }

public static MethodHandle getM(Class<?> c) {
try {
@@ -89,12 +98,14 @@ public static MethodHandle getN(Class<?> c) {
public static void main(String... args) {
Class<?>[] t = { void.class, boolean.class, char.class,
byte.class, short.class, int.class, long.class, float.class, double.class,
String.class, Integer.class, Object.class };
String.class, Integer.class, Object.class,
I.class, J.class, CC.class, FF.class};

for (int i = 0; i < t.length; i++) {
MethodHandle m = C.getM(t[i]);
MethodHandle n = C.getN(t[i]); // null for void.class
for (int j = 0; j < t.length; j++) {
//if (i == j) continue;
boolean correctRet = t[j].isAssignableFrom(t[i]) || conversions.contains(t[i], t[j]);
test(correctRet, m, mt(t[i], String.class), mt(t[j], String.class));
testBridge(correctRet, m, mt(t[i], String.class), mt(t[i], String.class),
@@ -103,7 +114,10 @@ public static void main(String... args) {
mt(t[i], CharSequence.class), mt(t[j], Object.class));

if (t[i] != void.class && t[j] != void.class) {
boolean correctParam = t[j].isAssignableFrom(t[i]);
//boolean correctParam = t[j].isAssignableFrom(t[i]) || sideCastExists(t[i], t[j]);
boolean correctParam = t[j].isAssignableFrom(t[i]) ||
(!t[j].isPrimitive() && !t[i].isPrimitive() && sideCastExists(t[j], t[i]));
System.out.println("testing correctParam = " + correctParam + " t[i] = " + t[i] + " t[j] = " + t[j]);
test(correctParam, n, mt(String.class, t[i]), mt(String.class, t[j]));
testBridge(correctParam, n, mt(String.class, t[i]), mt(String.class, t[i]),
mt(Object.class, t[j]));
@@ -266,4 +280,28 @@ public boolean contains(Class<?> from, Class<?> to) {
conversions.put(Boolean.class, boolean.class);
}

// test if a sidecast exist from fromType to toType
static boolean sideCastExists(Class<?> fromType, Class<?> toType) {
if (fromType.isPrimitive() || toType.isPrimitive()) {
return false;
}
if (toType.isInterface() && fromType.isInterface()) {
return true;
} else if (toType.isInterface()) {
return ((fromType.getModifiers() & Modifier.FINAL) == 0);
} else if (fromType.isInterface()) {
return ((toType.getModifiers() & Modifier.FINAL) == 0);
} else if (toType.isArray() && fromType.isArray()) {
return sideCastExists(fromType.getComponentType(), toType.getComponentType());
}
return false;
}

public interface I {}

public interface J {}

public static class CC {}

public static final class FF {}
}
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

/**
* @test
* @bug 8269121
* @summary Type inference bug with method references
*/

public class MethodReferenceIntersection5 {
interface StringLiteral {}

interface Variable {}

class MyFact {
static Object make (StringLiteral v) { return null; }
}

interface OneVariableQuery<VarType extends Variable> {
Object query(VarType var1);
}

static class Interpreter {
<VarType extends Variable> Object query(OneVariableQuery<VarType> query) { return null; }
}

public static void main(String[] args) {
new Interpreter().query(MyFact::make);
}
}