Enable -Wshadow=local - #2417
Conversation
Add `-Wshadow` to the compiler warning flags for the library, tests, and benchmarks, enforced as errors via the existing `-Werror`. Fixes follow the Google C++ style convention suggested in the discussion on rapidsai#2327: - `size` -> a normal parameter or local - `size_` -> a private class member So shadow conflicts are resolved by renaming the *member* with a trailing underscore when the conflict is between a constructor parameter and a private member. Otherwise the parameter/local is renamed with a descriptive (non-abbreviated) name; abbreviations like `strm`, `p`, `sz` are avoided per reviewer preference. Categories of fixes: - Constructor params shadowing private members: rename member with `_` suffix (e.g. `stream` -> `stream_` in benchmarks::cuda_event_timer). - Constructor params shadowing public struct members in test/benchmark utility structs (`allocation`, `event`): rename params with descriptive non-abbreviated names (`pointer`, `bytes`, `action_type`, ...). - Constructor params shadowing inherited base-class members that cannot be renamed (Thrust's `execution_policy::stream`): rename the param (`stream` -> `stream_view`). - Lambda params shadowing outer params: rename the lambda param (`ptr` -> `memory`, `info` -> `test_info`). - File-scope mutable globals in benchmarks shadowing function params: prefix the globals with `g_` to make them clearly file-scope. - Local variables shadowing fixture members in tests: rename the local (e.g. `pool` -> `local_pool`, `arena_size` -> `local_arena_size`, `mr` -> `local_mr`, `size` -> `expected_size`). - GTest `INSTANTIATE_TEST_SUITE_P` name-generator lambdas: rename `info` to `test_info` to avoid the macro-internal `info`. The full library, tests, and benchmarks build at 100% with `-Wshadow -Werror`, zero warnings. Stacks on top of rapidsai#2327 (which adds -Wnon-virtual-dtor and -Woverloaded-virtual). With this PR, RMM now enforces the three stricter warning flags we wanted, locking the codebase against regressions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (11)
✅ Files skipped from review due to trivial changes (3)
🚧 Files skipped from review as they are similar to previous changes (6)
📝 WalkthroughSummary by CodeRabbit
WalkthroughThis PR consolidates internal refactoring and build-configuration improvements: compiler warning flags now include ChangesCompiler warnings and code quality updates
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
cpp/benchmarks/random_allocations/random_allocations.cpp (1)
222-223: ⚡ Quick winConsider eliminating global state with lambda capture.
While the NOLINT acknowledges the global variables, introducing mutable file-scope state couples
benchmark_rangeto global state and makes testing harder. Google Benchmark supports lambda capture forApply(), allowing you to avoid globals entirely.♻️ Proposed refactor using lambda capture
Remove the globals and capture values in the lambda passed to
Apply():-int g_num_allocations = -1; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -int g_max_size = -1; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - -void benchmark_range(benchmark::Benchmark* bench) -{ - if (g_num_allocations > 0) { - if (g_max_size > 0) { - bench->Args({g_num_allocations, g_max_size})->Unit(benchmark::kMillisecond); +void benchmark_range(benchmark::Benchmark* bench, int num_allocations, int max_size) +{ + if (num_allocations > 0) { + if (max_size > 0) { + bench->Args({num_allocations, max_size})->Unit(benchmark::kMillisecond); } else { - size_range(bench, g_num_allocations); + size_range(bench, num_allocations); } } else { - if (g_max_size > 0) { - num_range(bench, g_max_size); + if (max_size > 0) { + num_range(bench, max_size); } else { num_size_range(bench); }Then in
declare_benchmark, capture the values:void declare_benchmark(std::string const& name) { + auto apply_range = [num=g_num_allocations, size=g_max_size](benchmark::Benchmark* b) { + benchmark_range(b, num, size); + }; if (name == "cuda") { BENCHMARK_CAPTURE(BM_RandomAllocations, cuda_mr, &make_cuda) // NOLINT - ->Apply(benchmark_range); + ->Apply(apply_range);Or if you prefer, define the lambda inline in each
Apply()call.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cpp/benchmarks/random_allocations/random_allocations.cpp` around lines 222 - 223, Remove the file-scope mutable globals g_num_allocations and g_max_size and change the benchmarks to capture those values via lambdas passed to Apply(); specifically, stop using the global variables inside benchmark_range and instead pass the desired num_allocations and max_size into the lambda you hand to Apply(), then call/forward those captured values into declare_benchmark/benchmark_range so the state is local to each benchmark instance.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@cpp/benchmarks/random_allocations/random_allocations.cpp`:
- Around line 222-223: Remove the file-scope mutable globals g_num_allocations
and g_max_size and change the benchmarks to capture those values via lambdas
passed to Apply(); specifically, stop using the global variables inside
benchmark_range and instead pass the desired num_allocations and max_size into
the lambda you hand to Apply(), then call/forward those captured values into
declare_benchmark/benchmark_range so the state is local to each benchmark
instance.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 26bce182-5bd9-4402-ac66-fa866f2b6f31
📒 Files selected for processing (39)
cpp/CMakeLists.txtcpp/benchmarks/CMakeLists.txtcpp/benchmarks/random_allocations/random_allocations.cppcpp/benchmarks/replay/replay.cppcpp/benchmarks/synchronization/synchronization.cppcpp/benchmarks/synchronization/synchronization.hppcpp/benchmarks/utilities/log_parser.hppcpp/include/rmm/cuda_stream.hppcpp/include/rmm/exec_policy.hppcpp/include/rmm/mr/detail/coalescing_free_list.hppcpp/include/rmm/mr/detail/free_list.hppcpp/include/rmm/mr/pinned_host_memory_resource.hppcpp/include/rmm/mr/system_memory_resource.hppcpp/src/cuda_stream.cppcpp/src/exec_policy.cppcpp/src/logger.cppcpp/src/mr/detail/arena_memory_resource_impl.cppcpp/tests/CMakeLists.txtcpp/tests/cuda_stream_pool_tests.cppcpp/tests/device_buffer_tests.cucpp/tests/mr/aligned_mr_tests.cppcpp/tests/mr/arena_mr_tests.cppcpp/tests/mr/callback_mr_tests.cppcpp/tests/mr/cccl_mr_ref_test_mt.hppcpp/tests/mr/host_mr_ref_tests.cppcpp/tests/mr/mr_ref_arena_tests.cppcpp/tests/mr/mr_ref_callback_tests.cppcpp/tests/mr/mr_ref_cuda_async_tests.cppcpp/tests/mr/mr_ref_cuda_tests.cppcpp/tests/mr/mr_ref_managed_tests.cppcpp/tests/mr/mr_ref_pinned_tests.cppcpp/tests/mr/mr_ref_system_tests.cppcpp/tests/mr/mr_ref_test.hppcpp/tests/mr/prefetch_resource_adaptor_tests.cppcpp/tests/mr/resource_ref_conversion_tests.cppcpp/tests/mr/statistics_mr_tests.cppcpp/tests/mr/thrust_allocator_tests.cucpp/tests/mr/tracking_mr_tests.cppcpp/tests/prefetch_tests.cpp
Revert the g_num_allocations / g_max_size globals back to their original names, and rename the shadowing parameters of profile_random_allocations to descriptive non-abbreviated names (allocation_count, max_allocation_size) instead. The g_ prefix isn't part of the Google C++ style guide that the rest of this PR follows; renaming the params keeps the file-scope CLI globals at their natural names. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
wence-
left a comment
There was a problem hiding this comment.
While I am broadly positive to this change, please back out all of the renamings that seem to have nothing to do with -Wshadow (e.g. renaming a parameter to a free function from void *ptr to void *raw_pointer etc...)
| static void profile_random_allocations(MRFactoryFunc const& factory, | ||
| std::size_t num_allocations, | ||
| std::size_t max_size) | ||
| std::size_t allocation_count, | ||
| std::size_t max_allocation_size) | ||
| { | ||
| auto mr = factory(); | ||
|
|
||
| try { | ||
| uniform_random_allocations(mr, num_allocations, max_size, max_usage); | ||
| uniform_random_allocations(mr, allocation_count, max_allocation_size, max_usage); | ||
| } catch (std::exception const& e) { | ||
| std::cout << "Error: " << e.what() << "\n"; | ||
| } |
| auto args = options.parse(argc, argv); | ||
| auto parsed_args = options.parse(argc, argv); | ||
|
|
||
| if (args.count("file") == 0) { | ||
| if (parsed_args.count("file") == 0) { | ||
| std::cout << options.help() << std::endl; | ||
| exit(0); | ||
| } | ||
|
|
||
| return args; | ||
| return parsed_args; | ||
| }(); |
| : stream(stream), p_state(&state) | ||
| : stream_(stream), p_state(&state) | ||
| { |
There was a problem hiding this comment.
FWIW, as is the case with most style guides, this is tremendously hateful.
There was a problem hiding this comment.
@wence- Can you clarify what you mean here? Are you saying you dislike the idea of suffixing member variables with an underscore as a general practice? I don't mind that rule, and I find it somewhat helpful for reasoning about member state.
There was a problem hiding this comment.
I am ok with us deciding that all struct members should have (say) trailing underscore names.
But doing single point rewrites to pacify a warning that I broadly think is just not worth it makes the code worse.
I note as well that the ctor body here is no longer implemented correctly (it refers to stream, not stream_) so this warning didn't help us anyway
There was a problem hiding this comment.
Thanks, that is helpful. I agree this will need some polish and close verification to get right.
| block(char* ptr, std::size_t size, bool is_head) | ||
| : block_base{ptr}, size_bytes{size}, head{is_head} | ||
| block(char* raw_pointer, std::size_t size, bool is_head) | ||
| : block_base{raw_pointer}, size_bytes{size}, head{is_head} | ||
| { |
|
|
||
| block_base() = default; | ||
| block_base(void* ptr) : ptr{ptr} {}; | ||
| block_base(void* raw_pointer) : ptr{raw_pointer} {}; |
| rmm::detail::aligned_host_deallocate(ptr, bytes, alloc_alignment, [](void* ptr) { | ||
| RMM_ASSERT_CUDA_SUCCESS_SAFE_SHUTDOWN(cudaFreeHost(ptr)); | ||
| rmm::detail::aligned_host_deallocate(ptr, bytes, alloc_alignment, [](void* memory) { | ||
| RMM_ASSERT_CUDA_SUCCESS_SAFE_SHUTDOWN(cudaFreeHost(memory)); | ||
| }); |
There was a problem hiding this comment.
Oh, I see, -Wshadow is fundamentally broken. This lambda captures no closure but somehow the compiler warns that the name shadows a local.
| rmm::detail::aligned_host_deallocate( | ||
| ptr, bytes, rmm::CUDA_ALLOCATION_ALIGNMENT, [](void* ptr) { ::operator delete(ptr); }); | ||
| ptr, bytes, rmm::CUDA_ALLOCATION_ALIGNMENT, [](void* memory) { ::operator delete(memory); }); | ||
| } |
|
Having looked harder, I am very negative on this change. e.g. in this example: template<typename Fn>
void do_foo(void *ptr, Fn fn) { fn(ptr); }
int main(void)
{
void *ptr = nullptr;
do_foo(ptr, [](void *ptr) { (void)ptr; });
}The lambda has no default capture, so there is no shadowing. Yet |
|
I'm supportive of underscore-suffixed members but there are a lot of unrelated renames happening here. I would echo @wence- that some work-splitting is needed to only make necessary changes. Perhaps we can isolate underscore-suffixed members as a standalone change, and follow that up with further analysis of what is needed for I think @wence-'s example of the lambda capture is a difficult case. Per https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55357, this is an intentional behavior in GCC (not necessarily a poor implementation). I am sympathetic to the viewpoint expressed here:
I think doing this in pieces will make it easier to review, discuss, and merge. |
# Conflicts: # cpp/include/rmm/mr/pinned_host_memory_resource.hpp # cpp/include/rmm/mr/system_memory_resource.hpp
Per review feedback, switch from full -Wshadow to -Wshadow=local, which
only diagnoses locals shadowing other locals/parameters. This drops the
warnings for constructor parameters shadowing members (the idiomatic
ptr{ptr} pattern) and locals shadowing globals/types, so all of the
member-underscore and parameter renames done to pacify those warnings
are reverted.
The remaining fixes are all cases GCC diagnoses under -Wshadow=local:
- logger.cpp, replay.cpp: a local shadowed the very variable it was
initializing (referencing the outer one would be UB)
- pinned_host/system_memory_resource.cpp, resource_ref_conversion_tests.cpp:
deallocate lambda parameter shadowed the enclosing function's ptr
- callback_mr_tests.cpp: callback-local base_mr shadowed the test-scope
base_mr while referring to a different resource
- mr_ref_*_tests, thrust_allocator_tests: INSTANTIATE_TEST_SUITE_P
name-generator lambda parameter info shadowed a parameter inside the
gtest macro expansion
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
I reviewed the diff for |
|
Thanks for taking another look, @bdice — that's a fair question, and honestly the answer is "best practices" rather than a concrete need. There was no specific bug driving this; it was an attempt to catch a class of shadowing mistakes proactively. The one place it earned its keep was Given that's the only compelling case and you and @wence- both feel the renames are subjectively worse, I agree it's not worth the churn. Closing this out — thanks both for the reviews. |
Description
Enables
-Wshadow=localfor RMM's C++ library, tests, and benchmarks.Scope (narrowed after review feedback)
This PR was originally written against full
-Wshadow, which also warns when constructor parameters shadow class members (e.g. the idiomaticptr{ptr}member-init pattern) and when locals shadow globals or types. Fixing those required Google-stylemember_renames and parameter renames that reviewers (rightly) felt made the code worse — see the discussion below.It now enables
-Wshadow=localinstead, which only warns when a local variable or parameter shadows another local variable or parameter. That keeps the genuinely dangerous cases while leaving idiomatic member initialization and member/global shadowing untouched:-Wshadow-Wshadow=local(this PR)ptr{ptr})Remaining fixes
Every change in this PR fixes a warning that
-Wshadow=localactually emits with GCC 13:cpp/src/logger.cpp— the IIFE that initializes the staticlogger_declared an inner local also namedlogger_. Referencing the outer variable inside its own initializer would be UB; the warning is genuinely useful here. Renamed the inner local toinstance.cpp/benchmarks/replay/replay.cpp— same pattern: innerargsshadows the outerargsit is initializing. Renamed toparsed_args.cpp/src/mr/{pinned_host,system}_memory_resource.cppandcpp/tests/mr/resource_ref_conversion_tests.cpp— deallocate lambdas declared a parameterptrshadowing the enclosing function'sptrparameter. Renamed the lambda parameter tomemory.cpp/tests/mr/callback_mr_tests.cpp— callback lambdas declared a localbase_mrthat shadows the test-scopebase_mr, but refers to a different resource (resolved fromarg). Renamed toresolved_mrto make the distinction visible.cpp/tests/mr/mr_ref_*_tests.cpp,thrust_allocator_tests.cu—INSTANTIATE_TEST_SUITE_Pname-generator lambdas used a parameterinfo, which shadows a parameter of the same name inside the gtest macro expansion. Renamed totest_info.Checklist