Skip to content

Commit 37731cc

Browse files
Googlercopybara-github
authored andcommitted
Change the check for whether to collect evidence for a Return and Arg->Params to hasInferable in addition to isSupportedPointerType.
For now, we keep the isSupportedPointerType, since the code will then proceed to do `getPointerValue(ReturnExpr, ...)`, which could crash if `hasInferable` but not `isSupportedPointerType` (e.g., the currently behind a flag support for vector<T*>). Checking 'hasInferable' matches what the caller would check for gatherInferableSlots, and what countInferableSlots would do. PiperOrigin-RevId: 833402835
1 parent 3ec56d6 commit 37731cc

File tree

4 files changed

+92
-24
lines changed

4 files changed

+92
-24
lines changed

bazel/llvm.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def _llvm_loader_repository(repository_ctx):
5353
executable = False,
5454
)
5555

56-
LLVM_COMMIT_SHA = "9625cf6cc0e3e530ea0bed971d85b363f77c49d8"
56+
LLVM_COMMIT_SHA = "741ba8209c1f9bd5b1a145d9c137f5e18bfffb84"
5757

5858
def llvm_loader_repository_dependencies():
5959
# This *declares* the dependency, but it won't actually be *downloaded* unless it's used.

nullability/inference/collect_evidence.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,8 @@ class NullabilityBehaviorVisitor {
10281028

10291029
for (ParamAndArgIterator<CallOrConstructExpr> Iter(CalleeDecl, Expr); Iter;
10301030
++Iter) {
1031-
if (!isSupportedPointerType(Iter.param().getType().getNonReferenceType()))
1031+
if (!hasInferable(Iter.param().getType()) ||
1032+
!isSupportedPointerType(Iter.param().getType().getNonReferenceType()))
10321033
continue;
10331034
bool ArgIsNullPtrT = Iter.arg().getType()->isNullPtrType();
10341035
if (!isSupportedPointerType(Iter.arg().getType())) {
@@ -1377,7 +1378,8 @@ class NullabilityBehaviorVisitor {
13771378
if (!ReturnExpr) return;
13781379
const FunctionDecl *CurrentFunc = Env.getCurrentFunc();
13791380
CHECK(CurrentFunc) << "A return statement outside of a function?";
1380-
if (!isSupportedPointerType(
1381+
if (!hasInferable(CurrentFunc->getReturnType()) ||
1382+
!isSupportedPointerType(
13811383
CurrentFunc->getReturnType().getNonReferenceType()))
13821384
return;
13831385

nullability/inference/collect_evidence_test.cc

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3796,8 +3796,6 @@ TEST_P(CollectEvidenceFromDefinitionTest, FunctionTemplate) {
37963796
evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
37973797
functionNamed("tmpl<#b>")),
37983798
evidence(paramSlot(0), Evidence::NULLABLE_ARGUMENT,
3799-
functionNamed("tmpl<#*C>")),
3800-
evidence(paramSlot(1), Evidence::NULLABLE_ARGUMENT,
38013799
functionNamed("tmpl<#*C>"))));
38023800

38033801
EXPECT_THAT(

nullability/inference/infer_tu_test.cc

Lines changed: 87 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -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) {
831835
TEST_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+
878946
TEST_P(InferTUTest, LambdaWithCaptureInit) {
879947
build(R"cc(
880948
void foo() {

0 commit comments

Comments
 (0)