1 /*
2 * Copyright (c) 2012, 2021, 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
817 * Structural checker for stuck expressions
818 */
819 class StructuralStuckChecker extends TreeScanner {
820
821 ResultInfo resultInfo;
822 InferenceContext inferenceContext;
823 Env<AttrContext> env;
824
825 public void check(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
826 this.resultInfo = resultInfo;
827 this.inferenceContext = deferredAttrContext.inferenceContext;
828 this.env = dt.env;
829 dt.tree.accept(this);
830 dt.speculativeCache.put(stuckTree, resultInfo);
831 }
832
833 @Override
834 public void visitLambda(JCLambda tree) {
835 Check.CheckContext checkContext = resultInfo.checkContext;
836 Type pt = resultInfo.pt;
837 if (!inferenceContext.inferencevars.contains(pt)) {
838 //must be a functional descriptor
839 Type descriptorType = null;
840 try {
841 descriptorType = types.findDescriptorType(pt);
842 } catch (Types.FunctionDescriptorLookupError ex) {
843 checkContext.report(null, ex.getDiagnostic());
844 }
845
846 if (descriptorType.getParameterTypes().length() != tree.params.length()) {
847 checkContext.report(tree,
848 diags.fragment(Fragments.IncompatibleArgTypesInLambda));
849 }
850
851 Type currentReturnType = descriptorType.getReturnType();
852 boolean returnTypeIsVoid = currentReturnType.hasTag(VOID);
853 if (tree.getBodyKind() == BodyKind.EXPRESSION) {
854 boolean isExpressionCompatible = !returnTypeIsVoid ||
855 TreeInfo.isExpressionStatement((JCExpression)tree.getBody());
856 if (!isExpressionCompatible) {
857 resultInfo.checkContext.report(tree.pos(),
1101 }
1102 });
1103 return super.visit(dt);
1104 }
1105
1106 private List<Type> map(List<Type> ts, List<Type> pts) {
1107 if (ts.nonEmpty()) {
1108 List<Type> tail1 = map(ts.tail, pts != null ? pts.tail : null);
1109 Type t = visit(ts.head, pts != null && pts.nonEmpty() ? pts.head : null);
1110 if (tail1 != ts.tail || t != ts.head)
1111 return tail1.prepend(t);
1112 }
1113 return ts;
1114 }
1115 }
1116
1117 /**
1118 * A special tree scanner that would only visit portions of a given tree.
1119 * The set of nodes visited by the scanner can be customized at construction-time.
1120 */
1121 abstract static class FilterScanner extends com.sun.tools.javac.tree.TreeScanner {
1122
1123 final Predicate<JCTree> treeFilter;
1124
1125 FilterScanner(final Set<JCTree.Tag> validTags) {
1126 this.treeFilter = t -> validTags.contains(t.getTag());
1127 }
1128
1129 @Override
1130 public void scan(JCTree tree) {
1131 if (tree != null) {
1132 if (treeFilter.test(tree)) {
1133 super.scan(tree);
1134 } else {
1135 skip(tree);
1136 }
1137 }
1138 }
1139
1140 /**
1141 * handler that is executed when a node has been discarded
1142 */
1143 void skip(JCTree tree) {}
1144 }
1145
1146 /**
1147 * A tree scanner suitable for visiting the target-type dependent nodes of
1148 * a given argument expression.
1149 */
1150 static class PolyScanner extends FilterScanner {
1151
1152 PolyScanner() {
1153 super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE, SWITCH_EXPRESSION));
1154 }
1155 }
1156
1157 /**
1158 * A tree scanner suitable for visiting the target-type dependent nodes nested
1159 * within a lambda expression body.
1160 */
1161 static class LambdaReturnScanner extends FilterScanner {
1162
1163 LambdaReturnScanner() {
1209 public CheckStuckPolicy(ResultInfo resultInfo, DeferredType dt) {
1210 this.pt = resultInfo.pt;
1211 this.inferenceContext = resultInfo.checkContext.inferenceContext();
1212 scan(dt.tree);
1213 if (!stuckVars.isEmpty()) {
1214 resultInfo.checkContext.inferenceContext()
1215 .addFreeTypeListener(List.from(stuckVars), this);
1216 }
1217 }
1218
1219 @Override
1220 public void typesInferred(InferenceContext inferenceContext) {
1221 stuckVars.clear();
1222 }
1223
1224 @Override
1225 public void visitLambda(JCLambda tree) {
1226 if (inferenceContext.inferenceVars().contains(pt)) {
1227 stuckVars.add(pt);
1228 }
1229 if (!types.isFunctionalInterface(pt)) {
1230 return;
1231 }
1232 Type descType = types.findDescriptorType(pt);
1233 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
1234 if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT &&
1235 freeArgVars.nonEmpty()) {
1236 stuckVars.addAll(freeArgVars);
1237 depVars.addAll(inferenceContext.freeVarsIn(descType.getReturnType()));
1238 depVars.addAll(inferenceContext.freeVarsIn(descType.getThrownTypes()));
1239 }
1240 scanLambdaBody(tree, descType.getReturnType());
1241 }
1242
1243 @Override
1244 public void visitReference(JCMemberReference tree) {
1245 scan(tree.expr);
1246 if (inferenceContext.inferenceVars().contains(pt)) {
1247 stuckVars.add(pt);
1248 return;
1249 }
|
1 /*
2 * Copyright (c) 2012, 2024, 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
817 * Structural checker for stuck expressions
818 */
819 class StructuralStuckChecker extends TreeScanner {
820
821 ResultInfo resultInfo;
822 InferenceContext inferenceContext;
823 Env<AttrContext> env;
824
825 public void check(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
826 this.resultInfo = resultInfo;
827 this.inferenceContext = deferredAttrContext.inferenceContext;
828 this.env = dt.env;
829 dt.tree.accept(this);
830 dt.speculativeCache.put(stuckTree, resultInfo);
831 }
832
833 @Override
834 public void visitLambda(JCLambda tree) {
835 Check.CheckContext checkContext = resultInfo.checkContext;
836 Type pt = resultInfo.pt;
837 if (!types.isQuoted(pt) && !inferenceContext.inferencevars.contains(pt)) {
838 //must be a functional descriptor
839 Type descriptorType = null;
840 try {
841 descriptorType = types.findDescriptorType(pt);
842 } catch (Types.FunctionDescriptorLookupError ex) {
843 checkContext.report(null, ex.getDiagnostic());
844 }
845
846 if (descriptorType.getParameterTypes().length() != tree.params.length()) {
847 checkContext.report(tree,
848 diags.fragment(Fragments.IncompatibleArgTypesInLambda));
849 }
850
851 Type currentReturnType = descriptorType.getReturnType();
852 boolean returnTypeIsVoid = currentReturnType.hasTag(VOID);
853 if (tree.getBodyKind() == BodyKind.EXPRESSION) {
854 boolean isExpressionCompatible = !returnTypeIsVoid ||
855 TreeInfo.isExpressionStatement((JCExpression)tree.getBody());
856 if (!isExpressionCompatible) {
857 resultInfo.checkContext.report(tree.pos(),
1101 }
1102 });
1103 return super.visit(dt);
1104 }
1105
1106 private List<Type> map(List<Type> ts, List<Type> pts) {
1107 if (ts.nonEmpty()) {
1108 List<Type> tail1 = map(ts.tail, pts != null ? pts.tail : null);
1109 Type t = visit(ts.head, pts != null && pts.nonEmpty() ? pts.head : null);
1110 if (tail1 != ts.tail || t != ts.head)
1111 return tail1.prepend(t);
1112 }
1113 return ts;
1114 }
1115 }
1116
1117 /**
1118 * A special tree scanner that would only visit portions of a given tree.
1119 * The set of nodes visited by the scanner can be customized at construction-time.
1120 */
1121 public abstract static class FilterScanner extends com.sun.tools.javac.tree.TreeScanner {
1122
1123 final Predicate<JCTree> treeFilter;
1124
1125 protected FilterScanner(final Set<JCTree.Tag> validTags) {
1126 this.treeFilter = t -> validTags.contains(t.getTag());
1127 }
1128
1129 @Override
1130 public void scan(JCTree tree) {
1131 if (tree != null) {
1132 if (treeFilter.test(tree)) {
1133 super.scan(tree);
1134 } else {
1135 skip(tree);
1136 }
1137 }
1138 }
1139
1140 /**
1141 * handler that is executed when a node has been discarded
1142 */
1143 protected void skip(JCTree tree) {}
1144 }
1145
1146 /**
1147 * A tree scanner suitable for visiting the target-type dependent nodes of
1148 * a given argument expression.
1149 */
1150 static class PolyScanner extends FilterScanner {
1151
1152 PolyScanner() {
1153 super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE, SWITCH_EXPRESSION));
1154 }
1155 }
1156
1157 /**
1158 * A tree scanner suitable for visiting the target-type dependent nodes nested
1159 * within a lambda expression body.
1160 */
1161 static class LambdaReturnScanner extends FilterScanner {
1162
1163 LambdaReturnScanner() {
1209 public CheckStuckPolicy(ResultInfo resultInfo, DeferredType dt) {
1210 this.pt = resultInfo.pt;
1211 this.inferenceContext = resultInfo.checkContext.inferenceContext();
1212 scan(dt.tree);
1213 if (!stuckVars.isEmpty()) {
1214 resultInfo.checkContext.inferenceContext()
1215 .addFreeTypeListener(List.from(stuckVars), this);
1216 }
1217 }
1218
1219 @Override
1220 public void typesInferred(InferenceContext inferenceContext) {
1221 stuckVars.clear();
1222 }
1223
1224 @Override
1225 public void visitLambda(JCLambda tree) {
1226 if (inferenceContext.inferenceVars().contains(pt)) {
1227 stuckVars.add(pt);
1228 }
1229 if (types.isQuoted(pt) || !types.isFunctionalInterface(pt)) {
1230 return;
1231 }
1232 Type descType = types.findDescriptorType(pt);
1233 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
1234 if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT &&
1235 freeArgVars.nonEmpty()) {
1236 stuckVars.addAll(freeArgVars);
1237 depVars.addAll(inferenceContext.freeVarsIn(descType.getReturnType()));
1238 depVars.addAll(inferenceContext.freeVarsIn(descType.getThrownTypes()));
1239 }
1240 scanLambdaBody(tree, descType.getReturnType());
1241 }
1242
1243 @Override
1244 public void visitReference(JCMemberReference tree) {
1245 scan(tree.expr);
1246 if (inferenceContext.inferenceVars().contains(pt)) {
1247 stuckVars.add(pt);
1248 return;
1249 }
|