@@ -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,130 @@ 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+ private TypeMention getAReceiverTypeMention ( ) {
654+ exists ( Invocation invocation |
655+ invocation . getTargetViaStaticNameBinding ( _ ) . asCallable ( ) instanceof ConstructorDeclaration and
656+ result = invocation . getReceiverTypeMention ( )
657+ )
640658 }
641659
642- Parameter getACallableParameter ( Callable c ) {
643- result = c .getParameter ( _) and
644- not result .getParameterEx ( ) .isImplicitReceiverParameter ( _)
660+ /**
661+ * Since we handle constructors as instance methods without return types,
662+ * we need to explicitly handle constructors where (parts of) the type of
663+ * the constructed object must be inferred from the context.
664+ *
665+ * Example:
666+ *
667+ * ```swift
668+ * class C<T1, T2> {
669+ * init(x : T1) { ... } // `T2` must be inferred from the context
670+ * }
671+ * ```
672+ */
673+ private module ContextualConstructorReturnTypeInput implements
674+ M3:: ContextualTyping:: ContextualReturnTypeInputSig
675+ {
676+ predicate callableHasTypeParameterAtReturnType ( Callable c , TypePath path , TypeParameter tp ) {
677+ c .asCallable ( ) instanceof ConstructorDeclaration and
678+ tp = c .getDeclaringType ( ) .getTypeAt ( path )
679+ }
680+
681+ Parameter getACallableParameter ( Callable c ) {
682+ result = c .getParameter ( _) and
683+ not result .getParameterEx ( ) .isImplicitReceiverParameter ( _)
684+ }
645685 }
646- }
647686
648- private module ContextualConstructorReturnType =
649- M3:: ContextualTyping:: ContextualReturnType< ContextualConstructorReturnTypeInput > ;
687+ private module ContextualConstructorReturnType =
688+ M3:: ContextualTyping:: ContextualReturnType< ContextualConstructorReturnTypeInput > ;
689+
690+ /**
691+ * Gets the inferred type at `path` of the object constructed at `i` using constructor
692+ * `cd`. Because of how we handle constructors as instance methods without return types,
693+ * this is simply the inferred type of the receiver.
694+ */
695+ private Type inferConstructionType ( Invocation i , ConstructorDeclaration cd , TypePath path ) {
696+ cd = i .getTargetImpl ( _) .asCallable ( ) and
697+ result = inferType ( i .getArgument ( 0 ) , path )
698+ }
699+
700+ private predicate needsContextualTyping ( Invocation i , TypePath path ) {
701+ exists ( ConstructorDeclaration cd , ClassLikeDeclaration declType , TypePath path0 |
702+ declType =
703+ inferConstructionType ( i , cd , TypePath:: nil ( ) )
704+ .( ClassLikeDeclarationType )
705+ .getClassLikeDeclaration ( ) and
706+ ContextualConstructorReturnType:: needsContextualTyping ( i , path0 )
707+ |
708+ declType .getAMember ( ) = cd and
709+ path = path0
710+ or
711+ // inherited constructor
712+ exists ( TypeAbstraction abs , TypeMention constraint , TypeParameter tp |
713+ BaseTypes:: rootTypesSatisfaction ( _, TClassLikeDeclarationType ( declType ) , abs , _,
714+ constraint ) and
715+ BaseTypes:: conditionSatisfiesConstraintTypeAt ( abs , _, constraint , path0 , tp ) and
716+ path = TypePath:: singleton ( tp )
717+ )
718+ )
719+ }
720+
721+ Type inferConstructionType ( AstNode n , TypePath path ) {
722+ exists ( TypeMention tm |
723+ n = tm and
724+ tm = getAReceiverTypeMention ( ) and
725+ result = tm .getTypeAt ( path )
726+ )
727+ or
728+ result = inferConstructionType ( n , _, path )
729+ or
730+ needsContextualTyping ( n , path ) and
731+ result instanceof UnknownType
732+ }
733+
734+ predicate allowFullContextualInference ( TypeMention tm , TypePath path ) {
735+ // For a construction such as `Optional.some(0)`, we need contextual inference
736+ // to infer the type of the wrapped object
737+ tm = getAReceiverTypeMention ( ) and
738+ exists ( TypePath prefix , Type t , TypeParameter tp |
739+ t = tm .getTypeAt ( prefix ) and
740+ not t instanceof UnknownType and
741+ tp = t .getATypeParameter ( ) and
742+ path = TypePath:: snoc ( prefix , tp ) and
743+ not exists ( tm .getTypeAt ( path ) )
744+ )
745+ }
746+ }
650747
651748 Type inferTypeLanguageSpecific ( AstNode n , TypePath path ) {
652749 result = Plugin:: inferType ( n , path )
@@ -687,39 +784,14 @@ module Input3 implements InputSig3 {
687784 path = TypePath:: singleton ( getArrayElementTypeParameter ( ) ) and
688785 result instanceof UnknownType
689786 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- )
787+ result = Construction:: inferConstructionType ( n , path )
704788 }
705789
706790 pragma [ nomagic]
707791 Type inferTypeCertainLanguageSpecific ( AstNode n , TypePath path ) { none ( ) }
708792
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
715- exists ( TypePath prefix , Type t , TypeParameter tp |
716- t = tm .getTypeAt ( prefix ) and
717- not t instanceof UnknownType and
718- tp = t .getATypeParameter ( ) and
719- path = TypePath:: snoc ( prefix , tp ) and
720- not exists ( tm .getTypeAt ( path ) )
721- )
722- )
793+ predicate allowFullContextualInference ( AstNode n , TypePath path ) {
794+ Construction:: allowFullContextualInference ( n , path )
723795 }
724796}
725797
0 commit comments