@@ -610,16 +610,20 @@ TEST_P(InferTUTest, AutoNoStarType) {
610610 inferredSlot (1 , Nullability::NONNULL)}),
611611 inference (hasName (" AutoLocalInAutoReturn" ),
612612 {inferredSlot (0 , Nullability::NULLABLE)}),
613- // Functions with parameters of type `auto` are templates, so
614- // we infer for/from the instantiations.
613+ // Functions with parameters of type `auto` are templates, and in this
614+ // case, the parameter type is a bare `T` template parameter (vs
615+ // `T*`), so we do not infer for/from the instantiations, as they may
616+ // have generic nullability.
617+ // For `autoReturnAndParam`, the return type is also `auto`, but in
618+ // that case, the compiler deduces that the `auto` is `int* _Nullable`
619+ // based on the `return` statement, so the return type is not a bare
620+ // `T` template parameter, and we do infer.
615621 inference (functionDecl (hasName (" autoParamAkaTemplate" ),
616622 isTemplateInstantiation ()),
617- {inferredSlot (0 , Nullability::NULLABLE),
618- inferredSlot (1 , Nullability::NONNULL)}),
623+ {inferredSlot (0 , Nullability::NULLABLE)}),
619624 inference (functionDecl (hasName (" autoReturnAndParam" ),
620625 isTemplateInstantiation ()),
621- {inferredSlot (0 , Nullability::NULLABLE),
622- inferredSlot (1 , Nullability::NONNULL)}),
626+ {inferredSlot (0 , Nullability::NULLABLE)}),
623627 inference (
624628 varDecl (hasName (" AutoLocalInTemplate" ),
625629 hasDeclContext (functionDecl (isTemplateInstantiation ()))),
@@ -831,23 +835,24 @@ TEST_P(InferTUTest, Pragma) {
831835TEST_P (InferTUTest, FunctionTemplate) {
832836 build (R"cc(
833837 template <typename T>
834- T functionTemplate(int * P, int * _Nullable Q, T * R, T * _Nullable S, T U) {
838+ T functionTemplate(int* P, int* _Nullable Q, T* R, T* _Nullable S, T U) {
835839 *P;
836840 *R;
837841 return U;
838842 }
839843
840844 void usage() {
841845 int I = 0;
842- int * A = &I;
843- int * B = &I;
844- int * C = &I;
845- int * D = &I;
846- int * E = &I;
846+ int* A = &I;
847+ int* B = &I;
848+ int* C = &I;
849+ int* D = &I;
850+ int* E = &I;
847851 // In the first iteration, infer (for the instantiation) P and R as
848- // Nonnull, Q and S as Nullable, U as Nonnull, and Unknown for the int*
849- // return type (which hasn't yet seen the inference of U as Nonnull).
850- int *TargetIntStarResult = functionTemplate(A, B, &C, &D, E);
852+ // Nonnull, Q and S as Nullable, and nothing for U and the return type
853+ // (which have generic nullability -- the T for each instantiation could
854+ // specify the nullability).
855+ int* TargetIntStarResult = functionTemplate(A, B, &C, &D, E);
851856 // Infer (for the instantiation) P and R as Nonnull, Q and S as Nullable,
852857 // and nothing for the int U and int return type.
853858 int TargetIntResult = functionTemplate(A, B, C, D, I);
@@ -860,12 +865,10 @@ TEST_P(InferTUTest, FunctionTemplate) {
860865 functionDecl (
861866 hasName (" functionTemplate" ), isTemplateInstantiation (),
862867 hasTemplateArgument (0 , refersToType (asString (" int *" )))),
863- {inferredSlot (0 , Nullability::UNKNOWN),
864- inferredSlot (1 , Nullability::NONNULL),
868+ {inferredSlot (1 , Nullability::NONNULL),
865869 inferredSlot (2 , Nullability::NULLABLE),
866870 inferredSlot (3 , Nullability::NONNULL),
867- inferredSlot (4 , Nullability::NULLABLE),
868- inferredSlot (5 , Nullability::NONNULL)}),
871+ inferredSlot (4 , Nullability::NULLABLE)}),
869872 inference (functionDecl (
870873 hasName (" functionTemplate" ), isTemplateInstantiation (),
871874 hasTemplateArgument (0 , refersToType (asString (" int" )))),
@@ -875,6 +878,71 @@ TEST_P(InferTUTest, FunctionTemplate) {
875878 inferredSlot (4 , Nullability::NULLABLE)})}));
876879}
877880
881+ TEST_P (InferTUTest, FunctionTemplateParamToReturn) {
882+ build (R"cc(
883+ // A function that could have parametric nullability -- the return
884+ // is *intended* to be nullable only if the parameter is nullable
885+ // (or return is nonnull if the parameter is nonnull).
886+ // It's also possible to consider the param/return nullable, but we'd
887+ // likely detect conflicts when the return value is dereferenced without a
888+ // check.
889+ // NOTE: We need at least one inferable slot for this test to be
890+ // interesting so use `U*` for the param, instead of just `T`.
891+ template <typename T, typename U>
892+ T identity(U* X) {
893+ return X;
894+ }
895+
896+ int target(int* _Nullable A, int* _Nonnull B) {
897+ int* C = identity<int*>(A);
898+ int* D = identity<int*>(B);
899+ return *D;
900+ }
901+ )cc" );
902+
903+ bool UseSummaries = GetParam () == InferenceMode::kTestWithSummaries ;
904+ EXPECT_THAT (
905+ inferTU (AST->context (), Pragmas, UseSummaries,
906+ /* Iterations=*/ 3 ),
907+ UnorderedElementsAre (
908+ inference (hasName (" target" ), {inferredSlot (1 , Nullability::NULLABLE),
909+ inferredSlot (2 , Nullability::NONNULL)}),
910+ inference (
911+ functionDecl (hasName (" identity" ), isTemplateInstantiation ()),
912+ {inferredSlot (1 , Nullability::NULLABLE)}),
913+ inference (hasName (" C" ), {inferredSlot (0 , Nullability::UNKNOWN)}),
914+ inference (hasName (" D" ), {inferredSlot (0 , Nullability::UNKNOWN)})));
915+ }
916+
917+ TEST_P (InferTUTest, FunctionParamToReturn) {
918+ build (R"cc(
919+ // A function that returns what it's given. However, the type is not a
920+ // template parameter, so we can't have different nullability for different
921+ // call sites through a template argument.
922+ int* identity(int* X) { return X; }
923+
924+ int target(int* _Nullable A, int* _Nonnull B) {
925+ int* C = identity(A);
926+ int* D = identity(B);
927+ return *D;
928+ }
929+ )cc" );
930+
931+ bool UseSummaries = GetParam () == InferenceMode::kTestWithSummaries ;
932+ EXPECT_THAT (
933+ inferTU (AST->context (), Pragmas, UseSummaries,
934+ /* Iterations=*/ 2 ),
935+ UnorderedElementsAre (
936+ inference (hasName (" target" ), {inferredSlot (1 , Nullability::NULLABLE),
937+ inferredSlot (2 , Nullability::NONNULL)}),
938+ inference (
939+ hasName (" identity" ),
940+ {inferredSlot (0 , Nullability::NONNULL, /* Conflict*/ true ),
941+ inferredSlot (1 , Nullability::NULLABLE, /* Conflict*/ true )}),
942+ inference (hasName (" C" ), {inferredSlot (0 , Nullability::NONNULL)}),
943+ inference (hasName (" D" ), {inferredSlot (0 , Nullability::NONNULL)})));
944+ }
945+
878946TEST_P (InferTUTest, LambdaWithCaptureInit) {
879947 build (R"cc(
880948 void foo() {
0 commit comments