23 * questions. |
23 * questions. |
24 */ |
24 */ |
25 |
25 |
26 package com.sun.tools.javac.comp; |
26 package com.sun.tools.javac.comp; |
27 |
27 |
28 import com.sun.source.tree.MemberReferenceTree; |
28 import com.sun.source.tree.LambdaExpressionTree.BodyKind; |
29 import com.sun.tools.javac.code.*; |
29 import com.sun.tools.javac.code.*; |
30 import com.sun.tools.javac.tree.*; |
30 import com.sun.tools.javac.tree.*; |
31 import com.sun.tools.javac.util.*; |
31 import com.sun.tools.javac.util.*; |
32 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; |
32 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; |
33 import com.sun.tools.javac.code.Symbol.*; |
33 import com.sun.tools.javac.code.Symbol.*; |
34 import com.sun.tools.javac.code.Type.*; |
34 import com.sun.tools.javac.code.Type.*; |
35 import com.sun.tools.javac.comp.Attr.ResultInfo; |
35 import com.sun.tools.javac.comp.Attr.ResultInfo; |
36 import com.sun.tools.javac.comp.Infer.InferenceContext; |
36 import com.sun.tools.javac.comp.Infer.InferenceContext; |
37 import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase; |
37 import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase; |
38 import com.sun.tools.javac.comp.Resolve.ReferenceLookupHelper; |
|
39 import com.sun.tools.javac.tree.JCTree.*; |
38 import com.sun.tools.javac.tree.JCTree.*; |
40 |
|
41 |
39 |
42 import java.util.ArrayList; |
40 import java.util.ArrayList; |
43 import java.util.Collections; |
41 import java.util.Collections; |
44 import java.util.EnumSet; |
42 import java.util.EnumSet; |
45 import java.util.LinkedHashMap; |
43 import java.util.LinkedHashMap; |
46 import java.util.LinkedHashSet; |
44 import java.util.LinkedHashSet; |
47 import java.util.Map; |
45 import java.util.Map; |
48 import java.util.Set; |
46 import java.util.Set; |
49 import java.util.WeakHashMap; |
47 import java.util.WeakHashMap; |
50 |
48 |
|
49 import static com.sun.tools.javac.code.Kinds.VAL; |
51 import static com.sun.tools.javac.code.TypeTag.*; |
50 import static com.sun.tools.javac.code.TypeTag.*; |
52 import static com.sun.tools.javac.tree.JCTree.Tag.*; |
51 import static com.sun.tools.javac.tree.JCTree.Tag.*; |
53 |
52 |
54 /** |
53 /** |
55 * This is an helper class that is used to perform deferred type-analysis. |
54 * This is an helper class that is used to perform deferred type-analysis. |
93 rs = Resolve.instance(context); |
94 rs = Resolve.instance(context); |
94 log = Log.instance(context); |
95 log = Log.instance(context); |
95 syms = Symtab.instance(context); |
96 syms = Symtab.instance(context); |
96 make = TreeMaker.instance(context); |
97 make = TreeMaker.instance(context); |
97 types = Types.instance(context); |
98 types = Types.instance(context); |
98 Names names = Names.instance(context); |
99 flow = Flow.instance(context); |
|
100 names = Names.instance(context); |
99 stuckTree = make.Ident(names.empty).setType(Type.stuckType); |
101 stuckTree = make.Ident(names.empty).setType(Type.stuckType); |
100 emptyDeferredAttrContext = |
102 emptyDeferredAttrContext = |
101 new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, infer.emptyContext, null, null) { |
103 new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, infer.emptyContext, null, null) { |
102 @Override |
104 @Override |
103 void addDeferredAttrNode(DeferredType dt, ResultInfo ri, DeferredStuckPolicy deferredStuckPolicy) { |
105 void addDeferredAttrNode(DeferredType dt, ResultInfo ri, DeferredStuckPolicy deferredStuckPolicy) { |
591 |
600 |
592 @Override |
601 @Override |
593 public void visitLambda(JCLambda tree) { |
602 public void visitLambda(JCLambda tree) { |
594 Check.CheckContext checkContext = resultInfo.checkContext; |
603 Check.CheckContext checkContext = resultInfo.checkContext; |
595 Type pt = resultInfo.pt; |
604 Type pt = resultInfo.pt; |
596 if (inferenceContext.inferencevars.contains(pt)) { |
605 if (!inferenceContext.inferencevars.contains(pt)) { |
597 //ok |
|
598 return; |
|
599 } else { |
|
600 //must be a functional descriptor |
606 //must be a functional descriptor |
|
607 Type descriptorType = null; |
601 try { |
608 try { |
602 Type desc = types.findDescriptorType(pt); |
609 descriptorType = types.findDescriptorType(pt); |
603 if (desc.getParameterTypes().length() != tree.params.length()) { |
|
604 checkContext.report(tree, diags.fragment("incompatible.arg.types.in.lambda")); |
|
605 } |
|
606 } catch (Types.FunctionDescriptorLookupError ex) { |
610 } catch (Types.FunctionDescriptorLookupError ex) { |
607 checkContext.report(null, ex.getDiagnostic()); |
611 checkContext.report(null, ex.getDiagnostic()); |
608 } |
612 } |
|
613 |
|
614 if (descriptorType.getParameterTypes().length() != tree.params.length()) { |
|
615 checkContext.report(tree, |
|
616 diags.fragment("incompatible.arg.types.in.lambda")); |
|
617 } |
|
618 |
|
619 Type currentReturnType = descriptorType.getReturnType(); |
|
620 boolean returnTypeIsVoid = currentReturnType.hasTag(VOID); |
|
621 if (tree.getBodyKind() == BodyKind.EXPRESSION) { |
|
622 boolean isExpressionCompatible = !returnTypeIsVoid || |
|
623 TreeInfo.isExpressionStatement((JCExpression)tree.getBody()); |
|
624 if (!isExpressionCompatible) { |
|
625 resultInfo.checkContext.report(tree.pos(), |
|
626 diags.fragment("incompatible.ret.type.in.lambda", |
|
627 diags.fragment("missing.ret.val", currentReturnType))); |
|
628 } |
|
629 } else { |
|
630 LambdaBodyStructChecker lambdaBodyChecker = |
|
631 new LambdaBodyStructChecker(); |
|
632 |
|
633 tree.body.accept(lambdaBodyChecker); |
|
634 boolean isVoidCompatible = lambdaBodyChecker.isVoidCompatible; |
|
635 |
|
636 if (returnTypeIsVoid) { |
|
637 if (!isVoidCompatible) { |
|
638 resultInfo.checkContext.report(tree.pos(), |
|
639 diags.fragment("unexpected.ret.val")); |
|
640 } |
|
641 } else { |
|
642 boolean isValueCompatible = lambdaBodyChecker.isPotentiallyValueCompatible |
|
643 && !canLambdaBodyCompleteNormally(tree); |
|
644 if (!isValueCompatible && !isVoidCompatible) { |
|
645 log.error(tree.body.pos(), |
|
646 "lambda.body.neither.value.nor.void.compatible"); |
|
647 } |
|
648 |
|
649 if (!isValueCompatible) { |
|
650 resultInfo.checkContext.report(tree.pos(), |
|
651 diags.fragment("incompatible.ret.type.in.lambda", |
|
652 diags.fragment("missing.ret.val", currentReturnType))); |
|
653 } |
|
654 } |
|
655 } |
|
656 } |
|
657 } |
|
658 |
|
659 boolean canLambdaBodyCompleteNormally(JCLambda tree) { |
|
660 JCLambda newTree = new TreeCopier<>(make).copy(tree); |
|
661 /* attr.lambdaEnv will create a meaningful env for the |
|
662 * lambda expression. This is specially useful when the |
|
663 * lambda is used as the init of a field. But we need to |
|
664 * remove any added symbol. |
|
665 */ |
|
666 Env<AttrContext> localEnv = attr.lambdaEnv(newTree, env); |
|
667 try { |
|
668 List<JCVariableDecl> tmpParams = newTree.params; |
|
669 while (tmpParams.nonEmpty()) { |
|
670 tmpParams.head.vartype = make.at(tmpParams.head).Type(syms.errType); |
|
671 tmpParams = tmpParams.tail; |
|
672 } |
|
673 |
|
674 attr.attribStats(newTree.params, localEnv); |
|
675 |
|
676 /* set pt to Type.noType to avoid generating any bound |
|
677 * which may happen if lambda's return type is an |
|
678 * inference variable |
|
679 */ |
|
680 Attr.ResultInfo bodyResultInfo = attr.new ResultInfo(VAL, Type.noType); |
|
681 localEnv.info.returnResult = bodyResultInfo; |
|
682 |
|
683 // discard any log output |
|
684 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); |
|
685 try { |
|
686 JCBlock body = (JCBlock)newTree.body; |
|
687 /* we need to attribute the lambda body before |
|
688 * doing the aliveness analysis. This is because |
|
689 * constant folding occurs during attribution |
|
690 * and the reachability of some statements depends |
|
691 * on constant values, for example: |
|
692 * |
|
693 * while (true) {...} |
|
694 */ |
|
695 attr.attribStats(body.stats, localEnv); |
|
696 |
|
697 attr.preFlow(newTree); |
|
698 /* make an aliveness / reachability analysis of the lambda |
|
699 * to determine if it can complete normally |
|
700 */ |
|
701 flow.analyzeLambda(localEnv, newTree, make, true); |
|
702 } finally { |
|
703 log.popDiagnosticHandler(diagHandler); |
|
704 } |
|
705 return newTree.canCompleteNormally; |
|
706 } finally { |
|
707 JCBlock body = (JCBlock)newTree.body; |
|
708 unenterScanner.scan(body.stats); |
|
709 localEnv.info.scope.leave(); |
609 } |
710 } |
610 } |
711 } |
611 |
712 |
612 @Override |
713 @Override |
613 public void visitNewClass(JCNewClass tree) { |
714 public void visitNewClass(JCNewClass tree) { |
654 checkContext.report(tree, diags.fragment("incompatible.arg.types.in.mref")); |
752 checkContext.report(tree, diags.fragment("incompatible.arg.types.in.mref")); |
655 } |
753 } |
656 } |
754 } |
657 } |
755 } |
658 } |
756 } |
|
757 |
|
758 /* This visitor looks for return statements, its analysis will determine if |
|
759 * a lambda body is void or value compatible. We must analyze return |
|
760 * statements contained in the lambda body only, thus any return statement |
|
761 * contained in an inner class or inner lambda body, should be ignored. |
|
762 */ |
|
763 class LambdaBodyStructChecker extends TreeScanner { |
|
764 boolean isVoidCompatible = true; |
|
765 boolean isPotentiallyValueCompatible = true; |
|
766 |
|
767 @Override |
|
768 public void visitClassDef(JCClassDecl tree) { |
|
769 // do nothing |
|
770 } |
|
771 |
|
772 @Override |
|
773 public void visitLambda(JCLambda tree) { |
|
774 // do nothing |
|
775 } |
|
776 |
|
777 @Override |
|
778 public void visitNewClass(JCNewClass tree) { |
|
779 // do nothing |
|
780 } |
|
781 |
|
782 @Override |
|
783 public void visitReturn(JCReturn tree) { |
|
784 if (tree.expr != null) { |
|
785 isVoidCompatible = false; |
|
786 } else { |
|
787 isPotentiallyValueCompatible = false; |
|
788 } |
|
789 } |
|
790 } |
659 } |
791 } |
660 |
792 |
661 /** an empty deferred attribution context - all methods throw exceptions */ |
793 /** an empty deferred attribution context - all methods throw exceptions */ |
662 final DeferredAttrContext emptyDeferredAttrContext; |
794 final DeferredAttrContext emptyDeferredAttrContext; |
663 |
795 |
793 static class LambdaReturnScanner extends FilterScanner { |
920 static class LambdaReturnScanner extends FilterScanner { |
794 |
921 |
795 LambdaReturnScanner() { |
922 LambdaReturnScanner() { |
796 super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP, |
923 super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP, |
797 FORLOOP, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP)); |
924 FORLOOP, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP)); |
798 } |
|
799 |
|
800 @Override |
|
801 void skip(JCTree tree) { |
|
802 //do nothing |
|
803 } |
925 } |
804 } |
926 } |
805 |
927 |
806 /** |
928 /** |
807 * This visitor is used to check that structural expressions conform |
929 * This visitor is used to check that structural expressions conform |