8322003: JShell - Incorrect type inference in lists of records implementing interfaces

Reviewed-by: vromero
This commit is contained in:
Jan Lahoda 2024-01-08 14:09:27 +00:00
parent c90768c93b
commit 57a65fe436
3 changed files with 109 additions and 33 deletions

View File

@ -967,6 +967,20 @@ public class JavacParser implements Parser {
return result;
}
protected JCExpression parseIntersectionType(int pos, JCExpression firstType) {
JCExpression t = firstType;
int pos1 = pos;
List<JCExpression> targets = List.of(t);
while (token.kind == AMP) {
accept(AMP);
targets = targets.prepend(parseType());
}
if (targets.length() > 1) {
t = toP(F.at(pos1).TypeIntersection(targets.reverse()));
}
return t;
}
public JCExpression unannotatedType(boolean allowVar) {
return unannotatedType(allowVar, TYPE);
}
@ -1337,15 +1351,7 @@ public class JavacParser implements Parser {
case CAST:
accept(LPAREN);
selectTypeMode();
int pos1 = pos;
List<JCExpression> targets = List.of(t = parseType());
while (token.kind == AMP) {
accept(AMP);
targets = targets.prepend(parseType());
}
if (targets.length() > 1) {
t = toP(F.at(pos1).TypeIntersection(targets.reverse()));
}
t = parseIntersectionType(pos, parseType());
accept(RPAREN);
selectExprMode();
JCExpression t1 = term3();

View File

@ -80,8 +80,13 @@ import com.sun.tools.javac.comp.AttrContext;
import com.sun.tools.javac.comp.Enter;
import com.sun.tools.javac.comp.Env;
import com.sun.tools.javac.comp.Resolve;
import com.sun.tools.javac.parser.JavacParser;
import com.sun.tools.javac.parser.Lexer;
import com.sun.tools.javac.parser.Parser;
import com.sun.tools.javac.parser.ParserFactory;
import com.sun.tools.javac.parser.ScannerFactory;
import static com.sun.tools.javac.parser.Tokens.TokenKind.AMP;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCTypeCast;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
@ -363,7 +368,7 @@ class TaskFactory {
JavacTaskImpl task,
DiagnosticCollector<JavaFileObject> diagnostics,
boolean forceExpression) {
super(sh, task, diagnostics);
super(sh, task, diagnostics, false);
ReplParserFactory.preRegister(context, forceExpression);
cuts = parse();
units = Util.stream(cuts)
@ -402,7 +407,7 @@ class TaskFactory {
private AnalyzeTask(SourceHandler<OuterWrap> sh,
JavacTaskImpl task,
DiagnosticCollector<JavaFileObject> diagnostics) {
super(sh, task, diagnostics);
super(sh, task, diagnostics, true);
cuts = analyze();
}
@ -440,7 +445,7 @@ class TaskFactory {
CompileTask(SourceHandler<OuterWrap>sh,
JavacTaskImpl jti,
DiagnosticCollector<JavaFileObject> diagnostics) {
super(sh, jti, diagnostics);
super(sh, jti, diagnostics, true);
}
boolean compile() {
@ -504,11 +509,15 @@ class TaskFactory {
private BaseTask(SourceHandler<S> sh,
JavacTaskImpl task,
DiagnosticCollector<JavaFileObject> diagnostics) {
DiagnosticCollector<JavaFileObject> diagnostics,
boolean analyzeParserFactory) {
this.sourceHandler = sh;
this.task = task;
context = task.getContext();
this.diagnostics = diagnostics;
if (analyzeParserFactory) {
JShellAnalyzeParserFactory.preRegister(context);
}
}
abstract Iterable<? extends CompilationUnitTree> cuTrees();
@ -693,7 +702,7 @@ class TaskFactory {
Symtab syms = Symtab.instance(context);
Names names = Names.instance(context);
Log log = Log.instance(context);
ParserFactory parserFactory = ParserFactory.instance(context);
JShellAnalyzeParserFactory parserFactory = (JShellAnalyzeParserFactory) ParserFactory.instance(context);
Attr attr = Attr.instance(context);
Enter enter = Enter.instance(context);
DisableAccessibilityResolve rs = (DisableAccessibilityResolve) Resolve.instance(context);
@ -709,26 +718,28 @@ class TaskFactory {
//ignore any errors:
JavaFileObject prev = log.useSource(null);
DiscardDiagnosticHandler h = new DiscardDiagnosticHandler(log);
try {
//parse the type as a cast, i.e. "(<typeName>) x". This is to support
//intersection types:
CharBuffer buf = CharBuffer.wrap(("(" + typeName +")x\u0000").toCharArray(), 0, typeName.length() + 3);
Parser parser = parserFactory.newParser(buf, false, false, false);
JCExpression expr = parser.parseExpression();
if (expr.hasTag(Tag.TYPECAST)) {
//if parsed OK, attribute and set the type:
var2OriginalType.put(field, field.type);
parserFactory.runPermitIntersectionTypes(() -> {
try {
//parse the type as a cast, i.e. "(<typeName>) x". This is to support
//intersection types:
CharBuffer buf = CharBuffer.wrap(("(" + typeName +")x\u0000").toCharArray(), 0, typeName.length() + 3);
Parser parser = parserFactory.newParser(buf, false, false, false);
JCExpression expr = parser.parseExpression();
if (expr.hasTag(Tag.TYPECAST)) {
//if parsed OK, attribute and set the type:
var2OriginalType.put(field, field.type);
JCTypeCast tree = (JCTypeCast) expr;
rs.runWithoutAccessChecks(() -> {
field.type = attr.attribType(tree.clazz,
enter.getEnvs().iterator().next().enclClass.sym);
});
JCTypeCast tree = (JCTypeCast) expr;
rs.runWithoutAccessChecks(() -> {
field.type = attr.attribType(tree.clazz,
enter.getEnvs().iterator().next().enclClass.sym);
});
}
} finally {
log.popDiagnosticHandler(h);
log.useSource(prev);
}
} finally {
log.popDiagnosticHandler(h);
log.useSource(prev);
}
});
}
}
}
@ -777,4 +788,52 @@ class TaskFactory {
private static final class Marker {}
}
private static final class JShellAnalyzeParserFactory extends ParserFactory {
public static void preRegister(Context context) {
if (context.get(Marker.class) == null) {
context.put(parserFactoryKey, ((Factory<ParserFactory>) c -> new JShellAnalyzeParserFactory(c)));
context.put(Marker.class, new Marker());
}
}
private final ScannerFactory scannerFactory;
private boolean permitIntersectionTypes;
public JShellAnalyzeParserFactory(Context context) {
super(context);
this.scannerFactory = ScannerFactory.instance(context);
}
/**Run the given Runnable with intersection type permitted.
*
* @param r Runnnable to run
*/
public void runPermitIntersectionTypes(Runnable r) {
boolean prevPermitIntersectionTypes = permitIntersectionTypes;
try {
permitIntersectionTypes = true;
r.run();
} finally {
permitIntersectionTypes = prevPermitIntersectionTypes;
}
}
@Override
public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap, boolean parseModuleInfo) {
com.sun.tools.javac.parser.Lexer lexer = scannerFactory.newScanner(input, keepDocComments);
return new JavacParser(this, lexer, keepDocComments, keepLineMap, keepEndPos, parseModuleInfo) {
@Override
public JCExpression parseType(boolean allowVar, com.sun.tools.javac.util.List<JCTree.JCAnnotation> annotations) {
int pos = token.pos;
JCExpression t = super.parseType(allowVar, annotations);
if (permitIntersectionTypes) {
t = parseIntersectionType(pos, t);
}
return t;
}
};
}
private static final class Marker {}
}
}

View File

@ -23,7 +23,7 @@
/*
* @test
* @bug 8144903 8177466 8191842 8211694 8213725 8239536 8257236 8252409 8294431 8322532
* @bug 8144903 8177466 8191842 8211694 8213725 8239536 8257236 8252409 8294431 8322003 8322532
* @summary Tests for EvaluationState.variables
* @library /tools/lib
* @modules jdk.compiler/com.sun.tools.javac.api
@ -627,4 +627,15 @@ public class VariablesTest extends KullaTesting {
" int i;", true);
}
public void intersectionTypeAsTypeArgument() { //JDK-8322003
assertEval("interface Shape {}");
assertEval("record Square(int edge) implements Shape {}");
assertEval("record Circle(int radius) implements Shape {}");
assertEval("java.util.function.Consumer<Shape> printShape = System.out::println;");
assertEval("Square square = new Square(1);");
assertEval("Circle circle = new Circle(1);");
assertEval("var shapes = java.util.List.of(square, circle);");
assertEval("shapes.forEach(printShape);");
}
}