Skip to content

Commit 68da63d

Browse files
committed
8240658: Code completion not working for lambdas in method invocations that require type inference
Reviewed-by: vromero
1 parent b05290a commit 68da63d

File tree

9 files changed

+699
-36
lines changed

9 files changed

+699
-36
lines changed

src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,11 @@ public static EnumSet<Flag> asFlagSet(long flags) {
220220
*/
221221
public static final long UNION = 1L<<39;
222222

223-
// Flag bit (1L << 40) is available.
223+
/**
224+
* Flags an erroneous TypeSymbol as viable for recovery.
225+
* TypeSymbols only.
226+
*/
227+
public static final long RECOVERABLE = 1L<<40;
224228

225229
/**
226230
* Flag that marks an 'effectively final' local variable.
@@ -508,6 +512,7 @@ public enum Flag {
508512
MATCH_BINDING(Flags.MATCH_BINDING),
509513
MATCH_BINDING_TO_OUTER(Flags.MATCH_BINDING_TO_OUTER),
510514
RECORD(Flags.RECORD),
515+
RECOVERABLE(Flags.RECOVERABLE),
511516
SEALED(Flags.SEALED),
512517
NON_SEALED(Flags.NON_SEALED) {
513518
@Override

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ public class Attr extends JCTree.Visitor {
120120
final Annotate annotate;
121121
final ArgumentAttr argumentAttr;
122122
final MatchBindingsComputer matchBindingsComputer;
123+
final AttrRecover attrRecover;
123124

124125
public static Attr instance(Context context) {
125126
Attr instance = context.get(attrKey);
@@ -157,6 +158,7 @@ protected Attr(Context context) {
157158
dependencies = Dependencies.instance(context);
158159
argumentAttr = ArgumentAttr.instance(context);
159160
matchBindingsComputer = MatchBindingsComputer.instance(context);
161+
attrRecover = AttrRecover.instance(context);
160162

161163
Options options = Options.instance(context);
162164

@@ -419,8 +421,9 @@ private Env<AttrContext> attribToTree(JCTree root, Env<AttrContext> env, JCTree
419421
JavaFileObject prev = log.useSource(env.toplevel.sourcefile);
420422
try {
421423
deferredAttr.attribSpeculative(root, env, resultInfo,
422-
null, DeferredAttr.AttributionMode.ANALYZER,
424+
null, DeferredAttr.AttributionMode.ATTRIB_TO_TREE,
423425
argumentAttr.withLocalCacheContext());
426+
attrRecover.doRecovery();
424427
} catch (BreakAttr b) {
425428
return b.env;
426429
} catch (AssertionError ae) {
@@ -738,6 +741,7 @@ public Type attribStat(JCTree tree, Env<AttrContext> env) {
738741
Env<AttrContext> analyzeEnv = analyzer.copyEnvIfNeeded(tree, env);
739742
Type result = attribTree(tree, env, statInfo);
740743
analyzer.analyzeIfNeeded(tree, analyzeEnv);
744+
attrRecover.doRecovery();
741745
return result;
742746
}
743747

@@ -2092,6 +2096,7 @@ public void visitIf(JCIf tree) {
20922096
}
20932097

20942098
void preFlow(JCTree tree) {
2099+
attrRecover.doRecovery();
20952100
new PostAttrAnalyzer() {
20962101
@Override
20972102
public void scan(JCTree tree) {
@@ -3114,6 +3119,7 @@ TargetInfo getTargetInfo(JCPolyExpression that, ResultInfo resultInfo, List<Type
31143119
}
31153120

31163121
void preFlow(JCLambda tree) {
3122+
attrRecover.doRecovery();
31173123
new PostAttrAnalyzer() {
31183124
@Override
31193125
public void scan(JCTree tree) {
@@ -4307,10 +4313,7 @@ Type checkMethodIdInternal(JCTree tree,
43074313
Env<AttrContext> env,
43084314
ResultInfo resultInfo) {
43094315
if (resultInfo.pkind.contains(KindSelector.POLY)) {
4310-
Type pt = resultInfo.pt.map(deferredAttr.new RecoveryDeferredTypeMap(AttrMode.SPECULATIVE, sym, env.info.pendingResolutionPhase));
4311-
Type owntype = checkIdInternal(tree, site, sym, pt, env, resultInfo);
4312-
resultInfo.pt.map(deferredAttr.new RecoveryDeferredTypeMap(AttrMode.CHECK, sym, env.info.pendingResolutionPhase));
4313-
return owntype;
4316+
return attrRecover.recoverMethodInvocation(tree, site, sym, env, resultInfo);
43144317
} else {
43154318
return checkIdInternal(tree, site, sym, resultInfo.pt, env, resultInfo);
43164319
}
@@ -4916,9 +4919,12 @@ public void visitAnnotatedType(JCAnnotatedType tree) {
49164919
}
49174920

49184921
public void visitErroneous(JCErroneous tree) {
4919-
if (tree.errs != null)
4922+
if (tree.errs != null) {
4923+
Env<AttrContext> errEnv = env.dup(env.tree);
4924+
errEnv.info.returnResult = unknownExprInfo;
49204925
for (JCTree err : tree.errs)
4921-
attribTree(err, env, new ResultInfo(KindSelector.ERR, pt()));
4926+
attribTree(err, errEnv, new ResultInfo(KindSelector.ERR, pt()));
4927+
}
49224928
result = tree.type = syms.errType;
49234929
}
49244930

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
/*
2+
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.sun.tools.javac.comp;
26+
27+
import com.sun.tools.javac.code.Flags;
28+
import com.sun.tools.javac.code.Symbol;
29+
import com.sun.tools.javac.code.Symbol.TypeSymbol;
30+
import com.sun.tools.javac.code.Symtab;
31+
import com.sun.tools.javac.code.Type;
32+
import com.sun.tools.javac.code.Type.ArrayType;
33+
import com.sun.tools.javac.code.Type.ErrorType;
34+
import com.sun.tools.javac.code.TypeTag;
35+
import com.sun.tools.javac.code.Types;
36+
import com.sun.tools.javac.comp.Attr.ResultInfo;
37+
import com.sun.tools.javac.comp.DeferredAttr.AttrMode;
38+
import com.sun.tools.javac.tree.JCTree;
39+
import com.sun.tools.javac.tree.JCTree.JCBlock;
40+
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
41+
import com.sun.tools.javac.tree.JCTree.JCErroneous;
42+
import com.sun.tools.javac.tree.JCTree.JCExpression;
43+
import com.sun.tools.javac.tree.JCTree.JCLambda;
44+
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
45+
import com.sun.tools.javac.tree.JCTree.JCReturn;
46+
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
47+
import com.sun.tools.javac.tree.JCTree.Tag;
48+
import com.sun.tools.javac.tree.TreeInfo;
49+
import com.sun.tools.javac.tree.TreeMaker;
50+
import com.sun.tools.javac.tree.TreeTranslator;
51+
import com.sun.tools.javac.util.Context;
52+
import com.sun.tools.javac.util.JCDiagnostic;
53+
import com.sun.tools.javac.util.List;
54+
import com.sun.tools.javac.util.ListBuffer;
55+
import com.sun.tools.javac.util.Names;
56+
57+
/** This is an error recovery addon for Attr. Currently, it recovers
58+
* method invocations with lambdas, that require type inference.
59+
*
60+
* <p><b>This is NOT part of any supported API.
61+
* If you write code that depends on this, you do so at your own risk.
62+
* This code and its internal interfaces are subject to change or
63+
* deletion without notice.</b>
64+
*/
65+
public class AttrRecover {
66+
protected static final Context.Key<AttrRecover> attrRepairKey = new Context.Key<>();
67+
68+
final Attr attr;
69+
final DeferredAttr deferredAttr;
70+
final Names names;
71+
final TreeMaker make;
72+
final Symtab syms;
73+
final Types types;
74+
75+
public static AttrRecover instance(Context context) {
76+
AttrRecover instance = context.get(attrRepairKey);
77+
if (instance == null)
78+
instance = new AttrRecover(context);
79+
return instance;
80+
}
81+
82+
protected AttrRecover(Context context) {
83+
context.put(attrRepairKey, this);
84+
85+
attr = Attr.instance(context);
86+
deferredAttr = DeferredAttr.instance(context);
87+
names = Names.instance(context);
88+
make = TreeMaker.instance(context);
89+
syms = Symtab.instance(context);
90+
types = Types.instance(context);
91+
}
92+
93+
private final ListBuffer<RecoverTodo> recoveryTodo = new ListBuffer<>();
94+
95+
public void doRecovery() {
96+
while (recoveryTodo.nonEmpty()) {
97+
RecoverTodo todo = recoveryTodo.remove();
98+
ListBuffer<Runnable> rollback = new ListBuffer<>();
99+
boolean repaired = false;
100+
RECOVER: if (todo.env.tree.hasTag(Tag.APPLY)) {
101+
JCMethodInvocation mit = (JCMethodInvocation) todo.env.tree;
102+
boolean vararg = (todo.candSym.flags() & Flags.VARARGS) != 0;
103+
if (!vararg &&
104+
mit.args.length() > todo.candSym.type.getParameterTypes().length()) {
105+
break RECOVER; //too many actual parameters, skip
106+
}
107+
List<JCExpression> args = mit.args;
108+
List<Type> formals = todo.candSym.type.getParameterTypes();
109+
while (args.nonEmpty() && formals.nonEmpty()) {
110+
JCExpression arg = args.head;
111+
Type formal = formals.tail.nonEmpty() || !vararg
112+
? formals.head : ((ArrayType) formals.head).elemtype;
113+
if (arg.hasTag(JCTree.Tag.LAMBDA)) {
114+
final JCTree.JCLambda lambda = (JCLambda) arg;
115+
if (lambda.paramKind == JCLambda.ParameterKind.IMPLICIT) {
116+
for (JCVariableDecl var : lambda.params) {
117+
var.vartype = null; //reset type
118+
}
119+
}
120+
if (types.isFunctionalInterface(formal)) {
121+
Type functionalType = types.findDescriptorType(formal);
122+
boolean voidCompatible = functionalType.getReturnType().hasTag(TypeTag.VOID);
123+
lambda.body = new TreeTranslator() {
124+
@Override
125+
public void visitReturn(JCReturn tree) {
126+
result = tree;
127+
if (voidCompatible) {
128+
if (tree.expr != null) {
129+
JCErroneous err = make.Erroneous(List.of(tree));
130+
result = err;
131+
rollback.append(() -> {
132+
lambda.body = new TreeTranslator() {
133+
@SuppressWarnings("unchecked")
134+
public <T extends JCTree> T translate(T t) {
135+
if (t == err) return (T) tree;
136+
else return super.translate(t);
137+
}
138+
}.translate(lambda.body);
139+
});
140+
}
141+
} else {
142+
if (tree.expr == null) {
143+
tree.expr = make.Erroneous().setType(syms.errType);
144+
rollback.append(() -> {
145+
tree.expr = null;
146+
});
147+
}
148+
}
149+
}
150+
@Override
151+
public void visitLambda(JCLambda tree) {
152+
//do not touch nested lambdas
153+
}
154+
@Override
155+
public void visitClassDef(JCClassDecl tree) {
156+
//do not touch nested classes
157+
}
158+
}.translate(lambda.body);
159+
if (!voidCompatible) {
160+
JCReturn ret = make.Return(make.Erroneous().setType(syms.errType));
161+
((JCBlock) lambda.body).stats = ((JCBlock) lambda.body).stats.append(ret);
162+
rollback.append(() -> {
163+
((JCBlock) lambda.body).stats = List.filter(((JCBlock) lambda.body).stats, ret);
164+
});
165+
}
166+
}
167+
repaired = true;
168+
}
169+
args = args.tail;
170+
if (formals.tail.nonEmpty() || !vararg) {
171+
formals = formals.tail;
172+
}
173+
}
174+
List<JCExpression> prevArgs = mit.args;
175+
while (formals.nonEmpty()) {
176+
mit.args = mit.args.append(make.Erroneous().setType(syms.errType));
177+
formals = formals.tail;
178+
repaired = true;
179+
}
180+
rollback.append(() -> {
181+
mit.args = prevArgs;
182+
});
183+
}
184+
185+
Type owntype;
186+
if (repaired) {
187+
List<JCExpression> args = TreeInfo.args(todo.env.tree);
188+
List<Type> pats = todo.resultInfo.pt.getParameterTypes();
189+
while (pats.length() < args.length()) {
190+
pats = pats.append(syms.errType);
191+
}
192+
owntype = attr.checkMethod(todo.site, todo.candSym,
193+
attr.new ResultInfo(todo.resultInfo.pkind, todo.resultInfo.pt.getReturnType(), todo.resultInfo.checkContext, todo.resultInfo.checkMode),
194+
todo.env, args, pats,
195+
todo.resultInfo.pt.getTypeArguments());
196+
rollback.stream().forEach(Runnable::run);
197+
} else {
198+
owntype = basicMethodInvocationRecovery(todo.tree, todo.site, todo.errSym, todo.env, todo.resultInfo);
199+
}
200+
todo.tree.type = owntype;
201+
}
202+
}
203+
204+
Type recoverMethodInvocation(JCTree tree,
205+
Type site,
206+
Symbol sym,
207+
Env<AttrContext> env,
208+
ResultInfo resultInfo) {
209+
if ((sym.flags_field & Flags.RECOVERABLE) != 0 && env.info.attributionMode.recover()) {
210+
recoveryTodo.append(new RecoverTodo(tree, site, sym, ((RecoveryErrorType) sym.type).candidateSymbol, attr.copyEnv(env), resultInfo));
211+
return syms.errType;
212+
} else {
213+
return basicMethodInvocationRecovery(tree, site, sym, env, resultInfo);
214+
}
215+
}
216+
217+
private Type basicMethodInvocationRecovery(JCTree tree,
218+
Type site,
219+
Symbol sym,
220+
Env<AttrContext> env,
221+
ResultInfo resultInfo) {
222+
Type pt = resultInfo.pt.map(deferredAttr.new RecoveryDeferredTypeMap(AttrMode.SPECULATIVE, sym, env.info.pendingResolutionPhase));
223+
Type owntype = attr.checkIdInternal(tree, site, sym, pt, env, resultInfo);
224+
resultInfo.pt.map(deferredAttr.new RecoveryDeferredTypeMap(AttrMode.CHECK, sym, env.info.pendingResolutionPhase));
225+
return owntype;
226+
}
227+
228+
void wrongMethodSymbolCandidate(TypeSymbol errSymbol, Symbol candSym, JCDiagnostic diag) {
229+
List<JCDiagnostic> diags = List.of(diag);
230+
boolean recoverable = false;
231+
while (!recoverable && diags.nonEmpty()) {
232+
JCDiagnostic d = diags.head;
233+
diags = diags.tail;
234+
switch (d.getCode()) {
235+
case "compiler.misc.missing.ret.val":
236+
case "compiler.misc.unexpected.ret.val":
237+
case "compiler.misc.infer.arg.length.mismatch":
238+
case "compiler.misc.arg.length.mismatch":
239+
errSymbol.type = new RecoveryErrorType((Type.ErrorType) errSymbol.type, candSym);
240+
errSymbol.flags_field |= Flags.RECOVERABLE;
241+
return ;
242+
default:
243+
break;
244+
}
245+
for (Object a : d.getArgs()) {
246+
if (a instanceof JCDiagnostic) {
247+
diags = diags.prepend((JCDiagnostic) a);
248+
}
249+
}
250+
}
251+
}
252+
253+
private static class RecoveryErrorType extends ErrorType {
254+
public final Symbol candidateSymbol;
255+
256+
public RecoveryErrorType(ErrorType original, Symbol candidateSymbol) {
257+
super(original.getOriginalType(), original.tsym);
258+
this.candidateSymbol = candidateSymbol;
259+
}
260+
261+
}
262+
263+
private static class RecoverTodo {
264+
public final JCTree tree;
265+
public final Type site;
266+
public final Symbol errSym;
267+
public final Symbol candSym;
268+
public final Env<AttrContext> env;
269+
public final ResultInfo resultInfo;
270+
271+
public RecoverTodo(JCTree tree, Type site, Symbol errSym, Symbol candSym,
272+
Env<AttrContext> env, Attr.ResultInfo resultInfo) {
273+
this.tree = tree;
274+
this.site = site;
275+
this.errSym = errSym;
276+
this.candSym = candSym;
277+
this.env = env;
278+
this.resultInfo = resultInfo;
279+
}
280+
281+
}
282+
}

0 commit comments

Comments
 (0)