Fix the harmful uses of #define private public - #7908
Closed
Critsium-xy wants to merge 6 commits into
Closed
Conversation
Critsium-xy
force-pushed
the
fix/test-access-control-hack
branch
from
September 4, 2026 07:09
032bfe6 to
8de0ae1
Compare
mohanchen
reviewed
Sep 4, 2026
Collaborator
There was a problem hiding this comment.
I don't like this solution.
Critsium-xy
force-pushed
the
fix/test-access-control-hack
branch
from
September 7, 2026 05:09
8de0ae1 to
0f5f8a5
Compare
`cal_test.cpp` is compiled into the main library (source/source_io/CMakeLists.txt), not into a test target, and wrapped `parameter.h` in `#define private public`. That gave this translation unit a definition of `Parameter` in which `input` and `sys` are public, while every other translation unit in the library sees them as private -- one class with two different definitions in one program, i.e. an ODR violation. The hack was also unnecessary: the file only reads `PARAM.inp.nbands` and `PARAM.globalv.nlocal`, both public const references on `Parameter`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nothing includes this header -- it is referenced by no source file and by no CMakeLists.txt. It was also the only header in the tree that applied `#define private public`, and it applied it to 22 module headers at once (`unitcell.h`, `elecstate_lcao.h`, `hsolver_lcao.h`, `force_stress_lcao.h`, ...), so any translation unit that had included it would have compiled a large part of the codebase with access control disabled. On top of that it defined non-inline globals (`berryphase::berry_phase_flag`, `elecstate::Gatefield::zgate`, ...) at file scope, which would be a duplicate symbol as soon as a second translation unit included it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Six regions did nothing at all and are removed: - `unitcell_test_setupcell.cpp`, `unitcell_test_pw.cpp` had a bare `#define private public` immediately followed by `#undef private`, with no include in between -- left over from an earlier refactor. - `write_orb_info_test.cpp`, `rho_io_test.cpp`, `deltaspin_pw_test.cpp` and the second region of `bfgs_test.cpp` wrapped only `parameter.h`, and none of them touches a private member of `Parameter`. `test_diago_assist.cpp` is deleted. It is listed in no CMakeLists.txt, includes a header that does not exist (`diago_iter_assis.h`; the real name is `diago_iter_assist.h`), and has a statement in a class body (`DIAGOTEST::hamilt.create(4, 4);`), so it cannot compile. It has been dead since it was added in deepmodeling#6305. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`source/source_base/test/CMakeLists.txt:1` calls `abacus_disable_feature_definitions(__MPI)`, so `__MPI` is never defined for any target in that directory and every `#ifdef __MPI` in it is always false. `opt_tn_test.cpp` and gave those two targets a real `mpi_test_main.cpp`. What remains of the pattern is: - `global_function_test.cpp` -- three `#ifdef __MPI / #undef __MPI / #endif` blocks. Their test bodies sit *outside* the `#ifdef`, so they always compiled; the blocks are pure no-ops. - `math_chebyshev_test.cpp` -- guards wrapping `checkconverge` and `checkconverge_float`, so those two bodies were compiled out in *every* configuration: two tests reporting PASS while asserting nothing. - `opt_cg_test.cpp`, `opt_test_tools.cpp` -- a file-level `#undef __MPI` that only duplicates what CMake already does. The `#undef __MPI` / `#define __MPI` pairs were no-ops in any case: the preprocessor cannot retroactively change headers already processed above, and the library these tests link against is a separate translation unit, compiled with `__MPI` regardless of what the test does. The two chebyshev bodies this exposes are handled in the next commit.
The previous commit stops `checkconverge` and `checkconverge_float` from being compiled out of `MODULE_BASE_math_chebyshev`. They cannot actually run in that target: they reach `GlobalFunc::ddot_real` -> `Parallel_Reduce::reduce_pool`, which needs a valid `POOL_WORLD`, and this target has no MPI setup -- unlike `MODULE_BASE_opt_cg` / `MODULE_BASE_opt_tn`, which deepmodeling#7888 gave `mpi_test_main.cpp`. They are also redundant: both are byte-identical to the tests of the same names in `test_parallel/math_cheby_mpi_test.cpp` (`MODULE_BASE_math_chebyshev_mpi`), which sets `POOL_WORLD` up in its fixture and runs them properly. So delete the copies rather than stand up a second MPI harness for them. No coverage is lost.
Sixteen test files opened `#define private public` (usually `#define protected public` too) and never closed it, so from the include block onwards the whole translation unit compiled with `private` and `protected` meaning `public` -- up to 1016 lines in `charge_mixing_test.cpp`. `elecstate_pw_test.cpp` was a partial case: it closed `protected` 318 lines later at the end of the file, and never closed `private` at all. Each `#undef` now sits immediately after the include block it is meant to cover. No include is moved: in every one of these files the standard headers already came after the project headers, so they simply fall outside the region once it is closed. That removes the incidental treatment of `<omp.h>` (charge_mixing_test), `<string>` (elecstate_energy_test), `<fstream>` (verlet_test), `<mpi.h>` and `<source_base/module_external/scalapack_connector.h>` (propagator_test1/2/3), `<source_base/macros.h>` (test_hsolver) and `"mpi.h"` (read_wf2rho_pw_test). Closing a region early is safe: the class definitions the tests reach into have already been compiled by the time the `#undef` is seen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Critsium-xy
force-pushed
the
fix/test-access-control-hack
branch
from
September 7, 2026 06:02
0f5f8a5 to
07d8a2e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Background
#define private publicaround an#includeappears in 81 files in this tree(111 occurrences). It is not a harmless test trick: it gives that translation unit
a different definition of the class than the rest of the library it links
against, which is an ODR violation, and it silently applies to every other header
pulled in by the same region.
This PR does not remove the pattern. It fixes the places where it is currently
doing damage, and removes the instances that do nothing at all.
What is fixed
1. The one occurrence in production code
source_io/module_output/cal_test.cppis compiled into the main library(
source/source_io/CMakeLists.txt), not into a test target, and wrappedparameter.h. The shipped binary therefore contained one translation unit that sawParameter::input/Parameter::sysas public while every other one saw them asprivate.
The hack was also unnecessary: the file only reads
PARAM.inp.nbandsandPARAM.globalv.nlocal, both public.2. Sixteen regions that were never closed
These opened
#define private public(usuallyprotectedtoo) and ran to end offile — up to 1016 lines in
charge_mixing_test.cpp.elecstate_pw_test.cppwas apartial case: it closed
protected318 lines later at the end of the file andnever closed
privateat all.Each
#undefnow sits immediately after the include block it is meant to cover.No include is moved: in every one of these files the standard headers already came
after the project headers, so they simply fall outside the region once it is
closed. That removes the incidental treatment of
<omp.h>,<string>,<fstream>,<mpi.h>,<source_base/macros.h>,<source_base/module_external/scalapack_connector.h>and"mpi.h"— eight fileswere compiling a standard or system header with access control disabled.
Closing a region early is safe: the class definitions the tests reach into have
already been compiled by the time the
#undefis seen.3. Dead
__MPItoggles, and two tests that asserted nothingsource/source_base/test/CMakeLists.txt:1callsabacus_disable_feature_definitions(__MPI), so__MPIis never defined for anytarget in that directory and every
#ifdef __MPIthere is always false.#7888 already fixed the worst of this — it removed the guards from
opt_cg_test.cpp/opt_tn_test.cppand gave those targets a sharedmpi_test_main.cppthat actually initialises MPI. This PR clears what it leftbehind:
math_chebyshev_test.cpp— guards still wrapcheckconvergeandcheckconverge_float, so those two bodies are compiled out in everyconfiguration: two tests reporting PASS while asserting nothing.
global_function_test.cpp— three#ifdef __MPI / #undef __MPI / #endifblocks whose test bodies sit outside the
#ifdef. Pure no-ops.opt_cg_test.cpp,opt_test_tools.cpp— a file-level#undef __MPIthat onlyduplicates what CMake already does.
The
#undef __MPI/#define __MPIpairs were no-ops regardless: the preprocessorcannot retroactively change headers already processed above, and the library these
tests link is a separate translation unit compiled with
__MPIeither way.The two chebyshev bodies this exposes cannot run in
MODULE_BASE_math_chebyshev—they reach
GlobalFunc::ddot_real->Parallel_Reduce::reduce_pool, which needs avalid
POOL_WORLD, and that target has no MPI setup. They are alsobyte-identical duplicates of the tests of the same names in
test_parallel/math_cheby_mpi_test.cpp(MODULE_BASE_math_chebyshev_mpi), whichsets
POOL_WORLDup in its fixture and runs them properly. So they are deletedrather than given a second MPI harness. No coverage is lost.
4. Dead code removed
source_io/test/for_testing_input_conv.h— included by nothing. It was the onlyheader applying
#define private public, and applied it to 22 module headers atonce; it also defined non-inline globals at file scope, which would be a
duplicate symbol the moment a second TU included it.
source_hsolver/test/test_diago_assist.cpp— in noCMakeLists.txt, includes aheader that does not exist (
diago_iter_assis.h; the real name isdiago_iter_assist.h), and has a statement in a class body, so it cannotcompile. Dead since [Refactor] Rename module_hsolver to source_hsolver #6305.
unitcell_test_setupcell.cppandunitcell_test_pw.cpphad abare
#define private publicimmediately followed by#undef private;write_orb_info_test.cpp,rho_io_test.cpp,deltaspin_pw_test.cppand thesecond region of
bfgs_test.cppwrapped onlyparameter.hand never touch aprivate member of
Parameter.Result
#define private publicin production codeScope
This deliberately stays narrow. It does not try to remove the pattern from the
files that wrap the header of the class under test (
unitcell.h,klist.h,charge.h,sltk_grid.h, ...) to reach its private members. Those regions are nowall balanced, tight, and free of standard headers, but the hack is still there.
In particular, 41 translation units still compile
parameter.hwith access controldisabled, because their tests need to write
PARAM.input/PARAM.sys. An earlierrevision of this PR routed those through the
TestParametersfriend class thatparameter.halready declares; that was not wanted, so it has been dropped. PerAGENTS.mdrule 10 the sanctioned remedies are to promote member visibilityexplicitly or add a public test-only accessor — worth settling separately, since it
touches ~1600 call sites and should be a deliberate design decision rather than a
side effect of this cleanup.
read_wf2rho_pw_test.cpplikewise keeps its file-level#undef __LCAO: it isload-bearing (it selects the non-LCAO path in the headers below it) and belongs in
that test's CMakeLists as
abacus_disable_feature_definitions(__LCAO), butchanging it changes what the test compiles.
Review notes
#undeflines.agent_governance_check.py --base upstream/develop: no blockers.no docs update is required.
Testing
Verified with gcc / Ninja,
BUILD_TESTING=ON,-DENABLE_LCAO=ON -DENABLE_MPI=ON -DENABLE_OPENMP=ON. Build clean.Unit tests compared against an
upstream/developbuild from the same commit base(
fb9ce1da4), same configuration,ctestrun serially withOMP_NUM_THREADS=1:upstream/developNo regressions and no differences — the failing set is identical on both trees.
Those 29 are pre-existing in this environment: mostly
mpirun-based*_para/*_parallelwrappers, plus tests in files this PR never touches(
blas_connector,math_sphbes,cubic_spline,PSI_init,dav,bpcg).Integration tests (
tests/) were not run locally; they need the full toolchainbuild, and CI covers them.