@@ -503,7 +503,8 @@ module Input3 implements InputSig3 {
503503 )
504504 or
505505 // call without explicit receiver: `foo(42)`
506- result .getNameNode ( ) = b
506+ result .getNameNode ( ) = b and
507+ kind = Ordinary ( )
507508 )
508509 }
509510
@@ -619,34 +620,124 @@ module Input3 implements InputSig3 {
619620 }
620621
621622 /**
622- * Since we handle constructors as instance methods without return types,
623- * we need to explicitly handle constructors where (parts of) the type of
624- * the constructed object must be inferred from the context.
623+ * Provides logic for resolving the types of constructor invocations.
625624 *
626- * Example:
625+ * Constructors are treated as instance methods without return types, in order
626+ * to be able to handle inherited constructors similar to ordinary instance
627+ * methods:
627628 *
628629 * ```swift
629- * class C<T1, T2 > {
630- * init(x : T1 ) { ... } // `T2` must be inferred from the context
630+ * class A<Ta > {
631+ * init(x : Ta ) { ... }
631632 * }
633+ *
634+ * class B<Tb> : A<Tb?> { }
635+ *
636+ * A.init(0); // (1) has type `A<Int>`
637+ * B.init(.some(0)); // (2) has type `B<Int>`
638+ * B<Int>(.none); // (3) has type `B<Int>`
632639 * ```
640+ *
641+ * Because the constructor calls are treated as normal instance method calls,
642+ * we use the type qualifier to represent the receiver, and then allow for
643+ * contextual type information to flow into the receiver:
644+ *
645+ * (1) The receiver has type `A`, `Ta` is inferred to be instantiated to `Int`,
646+ * which is propagated directly back into the receiver.
647+ * (2) The receiver has type `B`, `Ta` is inferred to be instantiated to `Int?`
648+ * which in turn means `Tb` is inferred to be instantiated to `Int`, which
649+ * is then propagated back into the receiver.
650+ * (3) The receiver has type `B<Int>`, and no further type inference is needed.
633651 */
634- private module ContextualConstructorReturnTypeInput implements
635- M3:: ContextualTyping:: ContextualReturnTypeInputSig
636- {
637- predicate callableHasTypeParameterAtReturnType ( Callable c , TypePath path , TypeParameter tp ) {
638- c .asCallable ( ) instanceof ConstructorDeclaration and
639- tp = c .getDeclaringType ( ) .getTypeAt ( path )
652+ private module Construction {
653+ TypeMention getReceiverTypeMention ( AstNode n ) {
654+ exists ( Invocation invocation |
655+ n = result and
656+ // n = invocation.getArgument(0) and
657+ invocation .getTargetViaStaticNameBinding ( _) .asCallable ( ) instanceof ConstructorDeclaration and
658+ result = invocation .getReceiverTypeMention ( )
659+ )
640660 }
641661
642- Parameter getACallableParameter ( Callable c ) {
643- result = c .getParameter ( _) and
644- not result .getParameterEx ( ) .isImplicitReceiverParameter ( _)
662+ /**
663+ * Since we handle constructors as instance methods without return types,
664+ * we need to explicitly handle constructors where (parts of) the type of
665+ * the constructed object must be inferred from the context.
666+ *
667+ * Example:
668+ *
669+ * ```swift
670+ * class C<T1, T2> {
671+ * init(x : T1) { ... } // `T2` must be inferred from the context
672+ * }
673+ * ```
674+ */
675+ private module ContextualConstructorReturnTypeInput implements
676+ M3:: ContextualTyping:: ContextualReturnTypeInputSig
677+ {
678+ predicate callableHasTypeParameterAtReturnType ( Callable c , TypePath path , TypeParameter tp ) {
679+ c .asCallable ( ) instanceof ConstructorDeclaration and
680+ tp = c .getDeclaringType ( ) .getTypeAt ( path )
681+ }
682+
683+ Parameter getACallableParameter ( Callable c ) {
684+ result = c .getParameter ( _) and
685+ not result .getParameterEx ( ) .isImplicitReceiverParameter ( _)
686+ }
687+ }
688+
689+ private module ContextualConstructorReturnType =
690+ M3:: ContextualTyping:: ContextualReturnType< ContextualConstructorReturnTypeInput > ;
691+
692+ /**
693+ * Gets the inferred type at `path` of the object constructed at `i` using constructor
694+ * `cd`. Because of how we handle constructors as instance methods without return types,
695+ * this is simply the inferred type of the receiver.
696+ */
697+ private Type inferConstructionType ( Invocation i , ConstructorDeclaration cd , TypePath path ) {
698+ cd = i .getTargetImpl ( _) .asCallable ( ) and
699+ result = inferType ( i .getArgument ( 0 ) , path )
700+ }
701+
702+ private predicate needsContextualTyping ( Invocation i , TypePath path ) {
703+ exists (
704+ ConstructorDeclaration cd , ClassLikeDeclaration declType , TypeMention constraint ,
705+ TypePath path0 , TypeParameter tp
706+ |
707+ declType =
708+ inferConstructionType ( i , cd , TypePath:: nil ( ) )
709+ .( ClassLikeDeclarationType )
710+ .getClassLikeDeclaration ( ) and
711+ ContextualConstructorReturnType:: needsContextualTyping ( i , path0 )
712+ |
713+ declType .getAMember ( ) = cd and
714+ path = path0
715+ or
716+ // inherited constructor
717+ exists ( TypeAbstraction abs |
718+ BaseTypes:: rootTypesSatisfaction ( _, TClassLikeDeclarationType ( declType ) , abs , _,
719+ constraint ) and
720+ BaseTypes:: conditionSatisfiesConstraintTypeAt ( abs , _, constraint , path0 , tp ) and
721+ path = TypePath:: singleton ( tp )
722+ )
723+ )
645724 }
646- }
647725
648- private module ContextualConstructorReturnType =
649- M3:: ContextualTyping:: ContextualReturnType< ContextualConstructorReturnTypeInput > ;
726+ Type inferConstructionType ( AstNode n , TypePath path ) {
727+ result = getReceiverTypeMention ( n ) .getTypeAt ( path )
728+ or
729+ // exists(TypeMention tm, Invocation invocation |
730+ // n = invocation.getArgument(0) and
731+ // invocation.getTargetViaStaticNameBinding(_).asCallable() instanceof ConstructorDeclaration and
732+ // tm = invocation.getReceiverTypeMention() and
733+ // result = tm.getTypeAt(path)
734+ // )
735+ result = inferConstructionType ( n , _, path )
736+ or
737+ needsContextualTyping ( n , path ) and
738+ result instanceof UnknownType
739+ }
740+ }
650741
651742 Type inferTypeLanguageSpecific ( AstNode n , TypePath path ) {
652743 result = Plugin:: inferType ( n , path )
@@ -687,31 +778,26 @@ module Input3 implements InputSig3 {
687778 path = TypePath:: singleton ( getArrayElementTypeParameter ( ) ) and
688779 result instanceof UnknownType
689780 or
690- exists ( ConstructorDeclaration cd |
691- cd = n .( Invocation ) .getTargetImpl ( _) .asCallable ( ) and
692- result = inferType ( n .( Invocation ) .getArgument ( 0 ) , path )
693- )
694- or
695- ContextualConstructorReturnType:: needsContextualTyping ( n , path ) and
696- result instanceof UnknownType
697- or
698- exists ( TypeMention tm , Invocation invocation |
699- n = invocation .getArgument ( 0 ) and
700- invocation .getTargetViaStaticNameBinding ( _) .asCallable ( ) instanceof ConstructorDeclaration and
701- tm = invocation .getReceiverTypeMention ( ) and
702- result = tm .getTypeAt ( path )
703- )
781+ result = Construction:: inferConstructionType ( n , path )
704782 }
705783
706784 pragma [ nomagic]
707785 Type inferTypeCertainLanguageSpecific ( AstNode n , TypePath path ) { none ( ) }
708786
709- predicate allowContextualInference ( AstNode n , TypePath path ) {
710- exists ( TypeMention tm , Invocation invocation | n = tm |
711- // or
712- // n = invocation and
713- // tm = invocation.getCallee()
714- tm = invocation .getReceiverTypeMention ( ) and
787+ predicate allowFullContextualInference ( AstNode n , TypePath path ) {
788+ // exists(TypeMention tm, Invocation invocation | n = tm |
789+ // tm = invocation.getReceiverTypeMention() and
790+ // exists(TypePath prefix, Type t, TypeParameter tp |
791+ // t = tm.getTypeAt(prefix) and
792+ // not t instanceof UnknownType and
793+ // tp = t.getATypeParameter() and
794+ // path = TypePath::snoc(prefix, tp) and
795+ // not exists(tm.getTypeAt(path))
796+ // )
797+ // )
798+ exists ( TypeMention tm | n = tm |
799+ tm = Construction:: getReceiverTypeMention ( n ) and
800+ // tm = invocation.getReceiverTypeMention() and
715801 exists ( TypePath prefix , Type t , TypeParameter tp |
716802 t = tm .getTypeAt ( prefix ) and
717803 not t instanceof UnknownType and
0 commit comments