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 
instance(Context context)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 
AttrRecover(Context context)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 
doRecovery()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 
recoverMethodInvocation(JCTree tree, Type site, Symbol sym, Env<AttrContext> env, ResultInfo resultInfo)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 
basicMethodInvocationRecovery(JCTree tree, Type site, Symbol sym, Env<AttrContext> env, ResultInfo resultInfo)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 
wrongMethodSymbolCandidate(TypeSymbol errSymbol, Symbol candSym, JCDiagnostic diag)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 
RecoveryErrorType(ErrorType original, Symbol candidateSymbol)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 
RecoverTodo(JCTree tree, Type site, Symbol errSym, Symbol candSym, Env<AttrContext> env, Attr.ResultInfo resultInfo)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 }
283