8335159: Move method reference to lambda desugaring before Lower

8336320: NullPointerException: Cannot invoke Type.getTag because type is null after JDK-8334037

Reviewed-by: jlahoda, vromero
This commit is contained in:
Maurizio Cimadamore 2024-07-15 14:24:27 +00:00
parent a253e0ff4b
commit 4635531950
3 changed files with 307 additions and 353 deletions

View File

@ -29,7 +29,6 @@ import java.util.*;
import java.util.stream.Collectors;
import com.sun.source.tree.LambdaExpressionTree.BodyKind;
import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Kinds.KindSelector;
import com.sun.tools.javac.code.Scope.WriteableScope;
@ -39,7 +38,6 @@ import com.sun.tools.javac.main.Option.PkgInfo;
import com.sun.tools.javac.resources.CompilerProperties.Fragments;
import com.sun.tools.javac.resources.CompilerProperties.Notes;
import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.List;
@ -67,8 +65,6 @@ import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.LEFT;
import com.sun.tools.javac.tree.JCTree.JCSwitchExpression;
import javax.lang.model.type.TypeKind;
import static com.sun.tools.javac.tree.JCTree.Tag.*;
/** This pass translates away some syntactic sugar: inner classes,
@ -3903,337 +3899,6 @@ public class Lower extends TreeTranslator {
result = tree;
}
@Override
public void visitReference(JCMemberReference tree) {
if (needsConversionToLambda(tree)) {
// Convert to a lambda, and process as such
MemberReferenceToLambda conv = new MemberReferenceToLambda(tree);
result = translate(conv.lambda());
} else {
super.visitReference(tree);
}
}
// where
boolean needsVarArgsConversion(JCMemberReference tree) {
return tree.varargsElement != null;
}
/**
* @return Is this an array operation like clone()
*/
boolean isArrayOp(JCMemberReference tree) {
return tree.sym.owner == syms.arrayClass;
}
boolean receiverAccessible(JCMemberReference tree) {
//hack needed to workaround 292 bug (7087658)
//when 292 issue is fixed we should remove this and change the backend
//code to always generate a method handle to an accessible method
return tree.ownerAccessible;
}
/**
* Erasure destroys the implementation parameter subtype
* relationship for intersection types.
* Have similar problems for union types too.
*/
boolean interfaceParameterIsIntersectionOrUnionType(JCMemberReference tree) {
List<Type> tl = tree.getDescriptorType(types).getParameterTypes();
for (; tl.nonEmpty(); tl = tl.tail) {
Type pt = tl.head;
if (isIntersectionOrUnionType(pt))
return true;
}
return false;
}
boolean isIntersectionOrUnionType(Type t) {
switch (t.getKind()) {
case INTERSECTION:
case UNION:
return true;
case TYPEVAR:
TypeVar tv = (TypeVar) t;
return isIntersectionOrUnionType(tv.getUpperBound());
}
return false;
}
private boolean isProtectedInSuperClassOfEnclosingClassInOtherPackage(Symbol targetReference,
Symbol currentClass) {
return ((targetReference.flags() & PROTECTED) != 0 &&
targetReference.packge() != currentClass.packge());
}
/**
* This method should be called only when target release <= 14
* where LambdaMetaFactory does not spin nestmate classes.
*
* This method should be removed when --release 14 is not supported.
*/
boolean isPrivateInOtherClass(JCMemberReference tree) {
assert !target.runtimeUseNestAccess();
return (tree.sym.flags() & PRIVATE) != 0 &&
!types.isSameType(
types.erasure(tree.sym.enclClass().asType()),
types.erasure(currentClass.asType()));
}
/**
* Does this reference need to be converted to a lambda
* (i.e. var args need to be expanded or "super" is used)
*/
boolean needsConversionToLambda(JCMemberReference tree) {
return interfaceParameterIsIntersectionOrUnionType(tree) ||
tree.hasKind(ReferenceKind.SUPER) ||
needsVarArgsConversion(tree) ||
isArrayOp(tree) ||
(!target.runtimeUseNestAccess() && isPrivateInOtherClass(tree)) ||
isProtectedInSuperClassOfEnclosingClassInOtherPackage(tree.sym, currentClass) ||
!receiverAccessible(tree) ||
(tree.getMode() == ReferenceMode.NEW &&
tree.kind != ReferenceKind.ARRAY_CTOR &&
(tree.sym.owner.isDirectlyOrIndirectlyLocal() || tree.sym.owner.isInner()));
}
/**
* Converts a method reference which cannot be used directly into a lambda
*/
private class MemberReferenceToLambda {
private final JCMemberReference tree;
private final ListBuffer<JCExpression> args = new ListBuffer<>();
private final ListBuffer<JCVariableDecl> params = new ListBuffer<>();
private final MethodSymbol owner = new MethodSymbol(0, names.empty, Type.noType, currentClass);
private JCExpression receiverExpression = null;
MemberReferenceToLambda(JCMemberReference tree) {
this.tree = tree;
}
JCExpression lambda() {
int prevPos = make.pos;
try {
make.at(tree);
//body generation - this can be either a method call or a
//new instance creation expression, depending on the member reference kind
VarSymbol rcvr = addParametersReturnReceiver();
JCExpression expr = (tree.getMode() == ReferenceMode.INVOKE)
? expressionInvoke(rcvr)
: expressionNew();
JCLambda slam = make.Lambda(params.toList(), expr);
slam.target = tree.target;
slam.type = tree.type;
slam.pos = tree.pos;
slam.wasMethodReference = true;
if (receiverExpression != null) {
// use a let expression so that the receiver expression is evaluated eagerly
return make.at(tree.pos).LetExpr(
make.VarDef(rcvr, translate(receiverExpression)), slam).setType(tree.type);
} else {
return slam;
}
} finally {
make.at(prevPos);
}
}
/**
* Generate the parameter list for the converted member reference.
*
* @return The receiver variable symbol, if any
*/
VarSymbol addParametersReturnReceiver() {
Type samDesc = types.erasure(types.findDescriptorSymbol(tree.target.tsym).type);
List<Type> samPTypes = samDesc.getParameterTypes();
List<Type> descPTypes = tree.getDescriptorType(types).getParameterTypes();
// Determine the receiver, if any
VarSymbol rcvr;
switch (tree.kind) {
case BOUND:
// The receiver is explicit in the method reference
rcvr = new VarSymbol(SYNTHETIC, names.fromString("rec$"), tree.getQualifierExpression().type, owner);
rcvr.pos = tree.pos;
receiverExpression = attr.makeNullCheck(tree.getQualifierExpression());
break;
case UNBOUND:
// The receiver is the first parameter, extract it and
// adjust the SAM and unerased type lists accordingly
rcvr = addParameter("rec$", samDesc.getParameterTypes().head, false);
samPTypes = samPTypes.tail;
descPTypes = descPTypes.tail;
break;
default:
rcvr = null;
break;
}
List<Type> implPTypes = tree.sym.type.getParameterTypes();
int implSize = implPTypes.size();
int samSize = samPTypes.size();
// Last parameter to copy from referenced method, exclude final var args
int last = needsVarArgsConversion(tree) ? implSize - 1 : implSize;
// Failsafe -- assure match-up
boolean checkForIntersection = tree.varargsElement != null || implSize == descPTypes.size();
// Use parameter types of the implementation method unless the unerased
// SAM parameter type is an intersection type, in that case use the
// erased SAM parameter type so that the supertype relationship
// the implementation method parameters is not obscured.
// Note: in this loop, the lists implPTypes, samPTypes, and descPTypes
// are used as pointers to the current parameter type information
// and are thus not usable afterwards.
for (int i = 0; implPTypes.nonEmpty() && i < last; ++i) {
// By default use the implementation method parameter type
Type parmType = implPTypes.head;
if (checkForIntersection) {
if (descPTypes.head.getKind() == TypeKind.INTERSECTION) {
parmType = samPTypes.head;
}
// If the unerased parameter type is a type variable whose
// bound is an intersection (eg. <T extends A & B>) then
// use the SAM parameter type
if (descPTypes.head.getKind() == TypeKind.TYPEVAR) {
TypeVar tv = (TypeVar) descPTypes.head;
if (tv.getUpperBound().getKind() == TypeKind.INTERSECTION) {
parmType = samPTypes.head;
}
}
}
addParameter("x$" + i, parmType, true);
// Advance to the next parameter
implPTypes = implPTypes.tail;
samPTypes = samPTypes.tail;
descPTypes = descPTypes.tail;
}
// Flatten out the var args
for (int i = last; i < samSize; ++i) {
addParameter("xva$" + i, tree.varargsElement, true);
}
return rcvr;
}
private JCExpression makeReceiver(VarSymbol rcvr) {
if (rcvr == null) return null;
JCExpression rcvrExpr = make.Ident(rcvr);
boolean protAccess =
isProtectedInSuperClassOfEnclosingClassInOtherPackage(tree.sym, currentClass);
Type rcvrType = tree.ownerAccessible && !protAccess ? tree.sym.enclClass().type
: tree.expr.type;
if (rcvrType == syms.arrayClass.type) {
// Map the receiver type to the actually type, not just "array"
rcvrType = tree.getQualifierExpression().type;
}
if (!rcvr.type.tsym.isSubClass(rcvrType.tsym, types)) {
rcvrExpr = make.TypeCast(make.Type(rcvrType), rcvrExpr).setType(rcvrType);
}
return rcvrExpr;
}
/**
* determine the receiver of the method call - the receiver can
* be a type qualifier, the synthetic receiver parameter or 'super'.
*/
private JCExpression expressionInvoke(VarSymbol rcvr) {
JCExpression qualifier =
(rcvr != null) ?
makeReceiver(rcvr) :
tree.getQualifierExpression();
//create the qualifier expression
JCFieldAccess select = make.Select(qualifier, tree.sym.name);
select.sym = tree.sym;
select.type = tree.sym.erasure(types);
//create the method call expression
JCExpression apply = make.Apply(List.nil(), select,
convertArgs(tree.sym, args.toList(), tree.varargsElement)).
setType(tree.sym.erasure(types).getReturnType());
apply = transTypes.coerce(attrEnv, apply,
types.erasure(tree.referentType.getReturnType()));
setVarargsIfNeeded(apply, tree.varargsElement);
return apply;
}
/**
* Lambda body to use for a 'new'.
*/
private JCExpression expressionNew() {
if (tree.kind == ReferenceKind.ARRAY_CTOR) {
//create the array creation expression
JCNewArray newArr = make.NewArray(
make.Type(types.elemtype(tree.getQualifierExpression().type)),
List.of(make.Ident(params.first())),
null);
newArr.type = tree.getQualifierExpression().type;
return newArr;
} else {
//create the instance creation expression
//note that method reference syntax does not allow an explicit
//enclosing class (so the enclosing class is null)
// but this may need to be patched up later with the proxy for the outer this
JCNewClass newClass = make.NewClass(null,
List.nil(),
make.Type(tree.getQualifierExpression().type),
convertArgs(tree.sym, args.toList(), tree.varargsElement),
null);
newClass.constructor = tree.sym;
newClass.constructorType = tree.sym.erasure(types);
newClass.type = tree.getQualifierExpression().type;
setVarargsIfNeeded(newClass, tree.varargsElement);
return newClass;
}
}
private VarSymbol addParameter(String name, Type p, boolean genArg) {
VarSymbol vsym = new VarSymbol(PARAMETER | SYNTHETIC, names.fromString(name), p, owner);
vsym.pos = tree.pos;
params.append(make.VarDef(vsym, null));
if (genArg) {
args.append(make.Ident(vsym));
}
return vsym;
}
}
/**
* Convert method/constructor arguments by inserting appropriate cast
* as required by type-erasure - this is needed when bridging a lambda/method
* reference, as the bridged signature might require downcast to be compatible
* with the generated signature.
*/
private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) {
Assert.check(meth.kind == MTH);
List<Type> formals = types.erasure(meth.type).getParameterTypes();
if (varargsElement != null) {
Assert.check((meth.flags() & VARARGS) != 0);
}
return transTypes.translateArgs(args, formals, varargsElement, attrEnv);
}
/**
* Set varargsElement field on a given tree (must be either a new class tree
* or a method call tree)
*/
private void setVarargsIfNeeded(JCTree tree, Type varargsElement) {
if (varargsElement != null) {
switch (tree.getTag()) {
case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break;
case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break;
case TYPECAST: setVarargsIfNeeded(((JCTypeCast) tree).expr, varargsElement); break;
default: throw new AssertionError();
}
}
}
public void visitSwitch(JCSwitch tree) {
List<JCCase> cases = tree.patternSwitch ? addDefaultIfNeeded(tree.patternSwitch,
tree.wasEnumSelector,

View File

@ -26,10 +26,13 @@
package com.sun.tools.javac.comp;
import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Attribute.TypeCompound;
import com.sun.tools.javac.code.Source.Feature;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.code.Type.TypeVar;
import com.sun.tools.javac.jvm.Target;
import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind;
@ -46,6 +49,8 @@ import static com.sun.tools.javac.code.TypeTag.VOID;
import static com.sun.tools.javac.comp.CompileStates.CompileState;
import com.sun.tools.javac.tree.JCTree.JCBreak;
import javax.lang.model.type.TypeKind;
/** This pass translates Generic Java to conventional Java.
*
* <p><b>This is NOT part of any supported API.
@ -75,6 +80,7 @@ public class TransTypes extends TreeTranslator {
private Attr attr;
private final Resolve resolve;
private final CompileStates compileStates;
private final Target target;
@SuppressWarnings("this-escape")
protected TransTypes(Context context) {
@ -89,6 +95,7 @@ public class TransTypes extends TreeTranslator {
resolve = Resolve.instance(context);
annotate = Annotate.instance(context);
attr = Attr.instance(context);
target = Target.instance(context);
}
/** Construct an attributed tree for a cast of expression to target type,
@ -539,6 +546,268 @@ public class TransTypes extends TreeTranslator {
}
}
@Override
public void visitReference(JCMemberReference tree) {
if (needsConversionToLambda(tree)) {
// Convert to a lambda, and process as such
MemberReferenceToLambda conv = new MemberReferenceToLambda(tree);
result = translate(conv.lambda());
} else {
Type t = types.skipTypeVars(tree.expr.type, false);
Type receiverTarget = t.isCompound() ? erasure(tree.sym.owner.type) : erasure(t);
if (tree.kind == ReferenceKind.UNBOUND) {
tree.expr = make.Type(receiverTarget);
} else {
tree.expr = translate(tree.expr, receiverTarget);
}
if (!tree.type.isIntersection()) {
tree.type = erasure(tree.type);
} else {
tree.type = types.erasure(types.findDescriptorSymbol(tree.type.tsym).owner.type);
}
result = tree;
}
}
// where
boolean needsVarArgsConversion(JCMemberReference tree) {
return tree.varargsElement != null;
}
/**
* @return Is this an array operation like clone()
*/
boolean isArrayOp(JCMemberReference tree) {
return tree.sym.owner == syms.arrayClass;
}
boolean receiverAccessible(JCMemberReference tree) {
//hack needed to workaround 292 bug (7087658)
//when 292 issue is fixed we should remove this and change the backend
//code to always generate a method handle to an accessible method
return tree.ownerAccessible;
}
/**
* Erasure destroys the implementation parameter subtype
* relationship for intersection types.
* Have similar problems for union types too.
*/
boolean interfaceParameterIsIntersectionOrUnionType(JCMemberReference tree) {
List<Type> tl = tree.getDescriptorType(types).getParameterTypes();
for (; tl.nonEmpty(); tl = tl.tail) {
Type pt = tl.head;
if (isIntersectionOrUnionType(pt))
return true;
}
return false;
}
boolean isIntersectionOrUnionType(Type t) {
return switch (t.getKind()) {
case INTERSECTION, UNION -> true;
case TYPEVAR -> {
TypeVar tv = (TypeVar) t;
yield isIntersectionOrUnionType(tv.getUpperBound());
}
default -> false;
};
}
private boolean isProtectedInSuperClassOfEnclosingClassInOtherPackage(Symbol targetReference,
Symbol currentClass) {
return ((targetReference.flags() & PROTECTED) != 0 &&
targetReference.packge() != currentClass.packge());
}
/**
* This method should be called only when target release <= 14
* where LambdaMetaFactory does not spin nestmate classes.
*
* This method should be removed when --release 14 is not supported.
*/
boolean isPrivateInOtherClass(JCMemberReference tree) {
return (tree.sym.flags() & PRIVATE) != 0 &&
!types.isSameType(
types.erasure(tree.sym.enclClass().asType()),
types.erasure(env.enclClass.sym.asType()));
}
/**
* Does this reference need to be converted to a lambda
* (i.e. var args need to be expanded or "super" is used)
*/
boolean needsConversionToLambda(JCMemberReference tree) {
return interfaceParameterIsIntersectionOrUnionType(tree) ||
tree.hasKind(ReferenceKind.SUPER) ||
needsVarArgsConversion(tree) ||
isArrayOp(tree) ||
(!target.runtimeUseNestAccess() && isPrivateInOtherClass(tree)) ||
isProtectedInSuperClassOfEnclosingClassInOtherPackage(tree.sym, env.enclClass.sym) ||
!receiverAccessible(tree) ||
(tree.getMode() == ReferenceMode.NEW &&
tree.kind != ReferenceKind.ARRAY_CTOR &&
(tree.sym.owner.isDirectlyOrIndirectlyLocal() || tree.sym.owner.isInner()));
}
/**
* Converts a method reference which cannot be used directly into a lambda
*/
private class MemberReferenceToLambda {
private final JCMemberReference tree;
private final ListBuffer<JCExpression> args = new ListBuffer<>();
private final ListBuffer<JCVariableDecl> params = new ListBuffer<>();
private final MethodSymbol owner = new MethodSymbol(0, names.empty, Type.noType, env.enclClass.sym);
private JCExpression receiverExpression = null;
MemberReferenceToLambda(JCMemberReference tree) {
this.tree = tree;
}
JCExpression lambda() {
int prevPos = make.pos;
try {
make.at(tree);
//body generation - this can be either a method call or a
//new instance creation expression, depending on the member reference kind
VarSymbol rcvr = addParametersReturnReceiver();
JCExpression expr = (tree.getMode() == ReferenceMode.INVOKE)
? expressionInvoke(rcvr)
: expressionNew();
JCLambda slam = make.Lambda(params.toList(), expr);
slam.target = tree.target;
slam.type = tree.type;
slam.pos = tree.pos;
slam.wasMethodReference = true;
if (receiverExpression != null) {
// use a let expression so that the receiver expression is evaluated eagerly
return make.at(tree.pos).LetExpr(
make.VarDef(rcvr, receiverExpression), slam).setType(tree.type);
} else {
return slam;
}
} finally {
make.at(prevPos);
}
}
/**
* Generate the parameter list for the converted member reference.
*
* @return The receiver variable symbol, if any
*/
VarSymbol addParametersReturnReceiver() {
List<Type> descPTypes = tree.getDescriptorType(types).getParameterTypes();
// Determine the receiver, if any
VarSymbol rcvr;
switch (tree.kind) {
case BOUND:
// The receiver is explicit in the method reference
rcvr = new VarSymbol(SYNTHETIC, names.fromString("rec$"), tree.getQualifierExpression().type, owner);
rcvr.pos = tree.pos;
receiverExpression = attr.makeNullCheck(tree.getQualifierExpression());
break;
case UNBOUND:
// The receiver is the first parameter, extract it and
// adjust the SAM and unerased type lists accordingly
rcvr = addParameter("rec$", descPTypes.head, false);
descPTypes = descPTypes.tail;
break;
default:
rcvr = null;
break;
}
List<Type> implPTypes = tree.sym.type.getParameterTypes();
int implSize = implPTypes.size();
int samSize = descPTypes.size();
// Last parameter to copy from referenced method, exclude final var args
int last = needsVarArgsConversion(tree) ? implSize - 1 : implSize;
for (int i = 0; implPTypes.nonEmpty() && i < last; ++i) {
// Use the descriptor parameter type
Type parmType = descPTypes.head;
addParameter("x$" + i, parmType, true);
// Advance to the next parameter
implPTypes = implPTypes.tail;
descPTypes = descPTypes.tail;
}
// Flatten out the var args
for (int i = last; i < samSize; ++i) {
addParameter("xva$" + i, tree.varargsElement, true);
}
return rcvr;
}
/**
* determine the receiver of the method call - the receiver can
* be a type qualifier, the synthetic receiver parameter or 'super'.
*/
private JCExpression expressionInvoke(VarSymbol rcvr) {
JCExpression qualifier =
(rcvr != null) ?
make.Ident(rcvr) :
tree.getQualifierExpression();
//create the qualifier expression
JCFieldAccess select = make.Select(qualifier, tree.sym.name);
select.sym = tree.sym;
select.type = tree.referentType;
//create the method call expression
JCExpression apply = make.Apply(List.nil(), select,
args.toList()).setType(tree.referentType.getReturnType());
TreeInfo.setVarargsElement(apply, tree.varargsElement);
return apply;
}
/**
* Lambda body to use for a 'new'.
*/
private JCExpression expressionNew() {
if (tree.kind == ReferenceKind.ARRAY_CTOR) {
//create the array creation expression
JCNewArray newArr = make.NewArray(
make.Type(types.elemtype(tree.getQualifierExpression().type)),
List.of(make.Ident(params.first())),
null);
newArr.type = tree.getQualifierExpression().type;
return newArr;
} else {
//create the instance creation expression
//note that method reference syntax does not allow an explicit
//enclosing class (so the enclosing class is null)
// but this may need to be patched up later with the proxy for the outer this
JCNewClass newClass = make.NewClass(null,
List.nil(),
make.Type(tree.getQualifierExpression().type),
args.toList(),
null);
newClass.constructor = tree.sym;
newClass.constructorType = tree.sym.erasure(types);
newClass.type = tree.getQualifierExpression().type;
TreeInfo.setVarargsElement(newClass, tree.varargsElement);
return newClass;
}
}
private VarSymbol addParameter(String name, Type p, boolean genArg) {
VarSymbol vsym = new VarSymbol(PARAMETER | SYNTHETIC, names.fromString(name), p, owner);
vsym.pos = tree.pos;
params.append(make.VarDef(vsym, null));
if (genArg) {
args.append(make.Ident(vsym));
}
return vsym;
}
}
public void visitSwitch(JCSwitch tree) {
Type selsuper = types.supertype(tree.selector.type);
boolean enumSwitch = selsuper != null &&
@ -860,24 +1129,6 @@ public class TransTypes extends TreeTranslator {
}
}
public void visitReference(JCMemberReference tree) {
Type t = types.skipTypeVars(tree.expr.type, false);
Type receiverTarget = t.isCompound() ? erasure(tree.sym.owner.type) : erasure(t);
if (tree.kind == ReferenceKind.UNBOUND) {
tree.expr = make.Type(receiverTarget);
} else {
tree.expr = translate(tree.expr, receiverTarget);
}
if (!tree.type.isIntersection()) {
tree.type = erasure(tree.type);
} else {
tree.type = types.erasure(types.findDescriptorSymbol(tree.type.tsym).owner.type);
}
if (tree.varargsElement != null)
tree.varargsElement = erasure(tree.varargsElement);
result = tree;
}
public void visitTypeArray(JCArrayTypeTree tree) {
tree.elemtype = translate(tree.elemtype, null);
tree.type = erasure(tree.type);

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2024, 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 8336320
* @summary NullPointerException: Cannot invoke Type.getTag because type is null after JDK-8334037
* @compile MrefDoubleTrans.java
*/
class MrefDoubleTrans {
public void f() {
Runnable r = new I()::m;
}
class I {
void m(Object... xs) {}
}
}