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