diff --git a/src/clangsa/GCChecker.cpp b/src/clangsa/GCChecker.cpp index c86de709472b7..e6dc76353e8b0 100644 --- a/src/clangsa/GCChecker.cpp +++ b/src/clangsa/GCChecker.cpp @@ -201,8 +201,13 @@ class GCChecker VS.FD = FD; return VS; } - // Assume arguments are pinned - return getRooted(nullptr, ValueState::Pinned, -1); + bool require_tpin = declHasAnnotation(PVD, "julia_require_tpin"); + if (require_tpin) { + return getRooted(nullptr, ValueState::TransitivelyPinned, -1); + } else { + // Assume arguments are pinned + return getRooted(nullptr, ValueState::Pinned, -1); + } } }; @@ -884,7 +889,10 @@ void GCChecker::checkBeginFunction(CheckerContext &C) const { auto Param = State->getLValue(P, LCtx); const MemRegion *Root = State->getSVal(Param).getAsRegion(); State = State->set(Root, RootState::getRoot(-1)); - State = State->set(Root, PinState::getPin(-1)); + if (declHasAnnotation(P, "julia_require_tpin")) + State = State->set(Root, PinState::getTransitivePin(-1)); + else + State = State->set(Root, PinState::getTransitivePin(-1)); } else if (isGCTrackedType(P->getType())) { auto Param = State->getLValue(P, LCtx); SymbolRef AssignedSym = State->getSVal(Param).getAsSymbol(); @@ -1631,6 +1639,11 @@ void GCChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { report_value_error(C, Sym, "Passing non-pinned value as argument to function that may GC", range); } } + if (FD && idx < FD->getNumParams() && declHasAnnotation(FD->getParamDecl(idx), "julia_require_tpin")) { + if (!ValState->isTransitivelyPinned()) { + report_value_error(C, Sym, "Passing non-tpinned argument to function that requires a tpin argument."); + } + } } } diff --git a/src/support/analyzer_annotations.h b/src/support/analyzer_annotations.h index 493bd11937907..cbf7cc63fdd00 100644 --- a/src/support/analyzer_annotations.h +++ b/src/support/analyzer_annotations.h @@ -23,6 +23,7 @@ #define JL_ALWAYS_LEAFTYPE JL_GLOBALLY_ROOTED #define JL_ROOTS_TEMPORARILY __attribute__((annotate("julia_temporarily_roots"))) #define JL_REQUIRE_ROOTED_SLOT __attribute__((annotate("julia_require_rooted_slot"))) +#define JL_REQUIRE_TPIN __attribute__((annotate("julia_require_tpin"))) #ifdef __cplusplus extern "C" { #endif @@ -52,6 +53,7 @@ extern "C" { #define JL_ALWAYS_LEAFTYPE #define JL_ROOTS_TEMPORARILY #define JL_REQUIRE_ROOTED_SLOT +#define JL_REQUIRE_TPIN #define JL_GC_PROMISE_ROOTED(x) (void)(x) #define jl_may_leak(x) (void)(x) diff --git a/test/clangsa/MissingPinning.c b/test/clangsa/MissingPinning.c index 927a7bfdc0fa2..5a5a87d807a1f 100644 --- a/test/clangsa/MissingPinning.c +++ b/test/clangsa/MissingPinning.c @@ -179,3 +179,25 @@ void rebind_tpin_simple2() { look_at_value(v); JL_GC_POP(); } + +int transitive_closure(jl_value_t *v JL_REQUIRE_TPIN) { + if (jl_is_unionall(v)) { + jl_unionall_t *ua = (jl_unionall_t*)v; + return transitive_closure(ua->body); + } + return 0; +} + +extern void look_at_tpin_value(jl_value_t *v JL_REQUIRE_TPIN); + +int properly_tpin_arg(jl_value_t *v) { + JL_GC_PUSH1(&v); + look_at_tpin_value(v); + JL_GC_POP(); +} + +int no_tpin_arg(jl_value_t *v) { + look_at_tpin_value(v); // expected-warning{{Passing non-tpinned argument to function that requires a tpin argument}} + // expected-note@-1{{Passing non-tpinned argument to function that requires a tpin argument}} + // expected-note@+1{{Started tracking value here (root was inherited)}} +}