Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions changelog.d/fixed/cpp23-shadow-const-fixes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- Replaced all C-style casts (`(char*)`, `(void*)`, `(uint8_t*)`, `(size_t)`,
`(unsigned)`, `(decltype(...))`) with `static_cast<>` in
`core/src/feature/feature_collector.cpp` and `core/src/sycl/common.cpp`.
- Renamed local variable `capacity` → `new_capacity` in
`core/src/fex_ctx_vector.cpp` to eliminate the `-Wshadow` hit against the
`rfe->capacity` struct member.
- No behaviour change; the fixes are type-system-only (ADR-0839).
39 changes: 20 additions & 19 deletions core/src/feature/feature_collector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ int aggregate_vector_init(AggregateVector *aggregate_vector)
memset(aggregate_vector, 0, sizeof(*aggregate_vector));
const unsigned initial_capacity = 8;
const size_t metric_vector_sz = sizeof(aggregate_vector->metric[0]) * initial_capacity;
aggregate_vector->metric = (decltype(aggregate_vector->metric))malloc(metric_vector_sz);
aggregate_vector->metric =
static_cast<decltype(aggregate_vector->metric)>(malloc(metric_vector_sz));
if (!aggregate_vector->metric)
return -ENOMEM;
memset(aggregate_vector->metric, 0, metric_vector_sz);
Expand Down Expand Up @@ -82,13 +83,13 @@ int aggregate_vector_append(AggregateVector *aggregate_vector, const char *featu
void *metric = realloc(aggregate_vector->metric, initial_size * 2);
if (!metric)
return -ENOMEM;
memset((char *)metric + initial_size, 0, initial_size);
aggregate_vector->metric = (decltype(aggregate_vector->metric))metric;
memset(static_cast<char *>(metric) + initial_size, 0, initial_size);
aggregate_vector->metric = static_cast<decltype(aggregate_vector->metric)>(metric);
aggregate_vector->capacity *= 2;
}

const size_t feature_name_sz = strnlen(feature_name, 2048);
char *f = (char *)malloc(feature_name_sz + 1);
char *f = static_cast<char *>(malloc(feature_name_sz + 1));
if (!f)
return -EINVAL;
memcpy(f, feature_name, feature_name_sz);
Expand Down Expand Up @@ -169,16 +170,16 @@ int feature_vector_init(FeatureVector **const feature_vector, const char *name)
return -EINVAL;

const size_t name_sz = strlen(name);
FeatureVector *const fv = *feature_vector = (FeatureVector *)malloc(sizeof(*fv));
FeatureVector *const fv = *feature_vector = static_cast<FeatureVector *>(malloc(sizeof(*fv)));
if (!fv)
goto fail;
memset(fv, 0, sizeof(*fv));
fv->name = (char *)malloc(name_sz + 1);
fv->name = static_cast<char *>(malloc(name_sz + 1));
if (!fv->name)
goto free_fv;
memcpy(fv->name, name, name_sz + 1);
fv->capacity = 8;
fv->score = (decltype(fv->score))malloc(sizeof(fv->score[0]) * fv->capacity);
fv->score = static_cast<decltype(fv->score)>(malloc(sizeof(fv->score[0]) * fv->capacity));
if (!fv->score)
goto free_name;
memset(fv->score, 0, sizeof(fv->score[0]) * fv->capacity);
Expand Down Expand Up @@ -212,8 +213,8 @@ int feature_vector_append(FeatureVector *feature_vector, unsigned index, double
void *score_buf = realloc(feature_vector->score, initial_size * 2);
if (!score_buf)
return -ENOMEM;
memset((char *)score_buf + initial_size, 0, initial_size);
feature_vector->score = (decltype(feature_vector->score))score_buf;
memset(static_cast<char *>(score_buf) + initial_size, 0, initial_size);
feature_vector->score = static_cast<decltype(feature_vector->score)>(score_buf);
feature_vector->capacity *= 2;
}

Expand All @@ -239,16 +240,16 @@ int vmaf_feature_collector_init(VmafFeatureCollector **const feature_collector)
* goto that could jump over them (C++ cross-initialisation rule). */
size_t fv_sz;
VmafFeatureCollector *const fc = *feature_collector =
(VmafFeatureCollector *)malloc(sizeof(*fc));
static_cast<VmafFeatureCollector *>(malloc(sizeof(*fc)));
if (!fc)
goto fail;
memset(fc, 0, sizeof(*fc));
fc->capacity = 8;
fv_sz = sizeof(FeatureVector *) * fc->capacity;
fc->feature_vector = (FeatureVector **)malloc(fv_sz);
fc->feature_vector = static_cast<FeatureVector **>(malloc(fv_sz));
if (!fc->feature_vector)
goto free_fc;
memset((void *)fc->feature_vector, 0, fv_sz);
memset(static_cast<void *>(fc->feature_vector), 0, fv_sz);
err = aggregate_vector_init(&fc->aggregate_vector);
if (err)
goto free_feature_vector;
Expand All @@ -265,7 +266,7 @@ int vmaf_feature_collector_init(VmafFeatureCollector **const feature_collector)
free_aggregate_vector:
aggregate_vector_destroy(&(fc->aggregate_vector));
free_feature_vector:
free((void *)fc->feature_vector);
free(static_cast<void *>(fc->feature_vector));
free_fc:
free(fc);
fail:
Expand All @@ -279,7 +280,7 @@ int vmaf_feature_collector_mount_model(VmafFeatureCollector *feature_collector,
if (!model)
return -EINVAL;

VmafPredictModel *m = (VmafPredictModel *)malloc(sizeof(VmafPredictModel));
VmafPredictModel *m = static_cast<VmafPredictModel *>(malloc(sizeof(VmafPredictModel)));
if (!m)
return -ENOMEM;

Expand Down Expand Up @@ -374,11 +375,11 @@ static int feature_collector_grow_capacity(VmafFeatureCollector *feature_collect
assert(feature_collector->capacity > 0);
const size_t entry_sz = sizeof(FeatureVector *);
const size_t old_bytes = entry_sz * feature_collector->capacity;
FeatureVector **fv =
(FeatureVector **)realloc((void *)feature_collector->feature_vector, old_bytes * 2);
FeatureVector **fv = static_cast<FeatureVector **>(
realloc(static_cast<void *>(feature_collector->feature_vector), old_bytes * 2));
if (!fv)
return -ENOMEM;
memset((void *)(fv + feature_collector->capacity), 0, old_bytes);
memset(static_cast<void *>(fv + feature_collector->capacity), 0, old_bytes);
feature_collector->feature_vector = fv;
feature_collector->capacity *= 2;
return 0;
Expand Down Expand Up @@ -421,7 +422,7 @@ static void feature_collector_run_model_predict(VmafFeatureCollector *feature_co
if (res) {
pthread_mutex_unlock(&(feature_collector->lock));
(void)vmaf_predict_score_at_index(model, feature_collector, picture_index, score, true,
true, (VmafModelFlags)0);
true, static_cast<VmafModelFlags>(0));
pthread_mutex_lock(&(feature_collector->lock));
}
model_iter = model_iter->next;
Expand Down Expand Up @@ -552,7 +553,7 @@ void vmaf_feature_collector_destroy(VmafFeatureCollector *feature_collector)
vmaf_feature_collector_unmount_model(feature_collector, feature_collector->models->model);
}
vmaf_metadata_destroy(feature_collector->metadata);
free((void *)feature_collector->feature_vector);
free(static_cast<void *>(feature_collector->feature_vector));
pthread_mutex_unlock(&(feature_collector->lock));
pthread_mutex_destroy(&(feature_collector->lock));
free(feature_collector);
Expand Down
6 changes: 3 additions & 3 deletions core/src/fex_ctx_vector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,14 @@ int feature_extractor_vector_append(RegisteredFeatureExtractors *rfe,
/* Guard against size_t overflow in capacity doubling
* (CERT INT30-C; adversarial review 2026-05-28 finding #6). */
assert(rfe->capacity <= (SIZE_MAX / 2u) / sizeof(*rfe->fex_ctx));
const size_t capacity = static_cast<size_t>(rfe->capacity) * 2u;
const size_t new_capacity = static_cast<size_t>(rfe->capacity) * 2u;
auto *fex_ctx_new = static_cast<VmafFeatureExtractorContext **>(realloc(
static_cast<void *>(rfe->fex_ctx), // NOLINT(cppcoreguidelines-no-malloc) — C ABI grow
sizeof(*(rfe->fex_ctx)) * capacity));
sizeof(*(rfe->fex_ctx)) * new_capacity));
if (!fex_ctx_new)
return -ENOMEM;
rfe->fex_ctx = fex_ctx_new;
rfe->capacity = static_cast<unsigned>(capacity);
rfe->capacity = static_cast<unsigned>(new_capacity);
for (unsigned i = rfe->cnt; i < rfe->capacity; i++)
rfe->fex_ctx[i] = nullptr;
}
Expand Down
22 changes: 11 additions & 11 deletions core/src/sycl/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ extern "C" int vmaf_sycl_shared_frame_init(VmafSyclState *state, unsigned w, uns
return 0;

unsigned bytes_per_pixel = (bpc + 7) / 8;
size_t buf_size = (size_t)w * h * bytes_per_pixel;
size_t buf_size = static_cast<size_t>(w) * h * bytes_per_pixel;

// Allocate two sets of ref+dis buffers for double-buffering.
// Buffer [cur_upload] receives H2D data while compute reads [cur_compute].
Expand Down Expand Up @@ -550,16 +550,16 @@ extern "C" int vmaf_sycl_shared_frame_upload(VmafSyclState *state, VmafPicture *
// In-order copy_queue ensures sequential uploads complete in order.
int ui = state->cur_upload;
unsigned bytes_per_pixel = (state->frame_bpc + 7) / 8;
size_t row_bytes = (size_t)state->frame_w * bytes_per_pixel;
size_t row_bytes = static_cast<size_t>(state->frame_w) * bytes_per_pixel;

try {
// If stride matches width, single memcpy; otherwise row-by-row
if ((unsigned)ref->stride[0] == row_bytes) {
if (static_cast<unsigned>(ref->stride[0]) == row_bytes) {
state->copy_queue.memcpy(state->shared_ref_buf[ui], ref->data[0],
state->shared_buf_size);
} else {
uint8_t *dst = (uint8_t *)state->shared_ref_buf[ui];
const uint8_t *src = (const uint8_t *)ref->data[0];
auto *dst = static_cast<uint8_t *>(state->shared_ref_buf[ui]);
const auto *src = static_cast<const uint8_t *>(ref->data[0]);
for (unsigned y = 0; y < state->frame_h; y++) {
state->copy_queue.memcpy(dst, src, row_bytes);
dst += row_bytes;
Expand All @@ -570,12 +570,12 @@ extern "C" int vmaf_sycl_shared_frame_upload(VmafSyclState *state, VmafPicture *
double t1 = monotonic_ms();

sycl::event last_ev;
if ((unsigned)dis->stride[0] == row_bytes) {
if (static_cast<unsigned>(dis->stride[0]) == row_bytes) {
last_ev = state->copy_queue.memcpy(state->shared_dis_buf[ui], dis->data[0],
state->shared_buf_size);
} else {
uint8_t *dst = (uint8_t *)state->shared_dis_buf[ui];
const uint8_t *src = (const uint8_t *)dis->data[0];
auto *dst = static_cast<uint8_t *>(state->shared_dis_buf[ui]);
const auto *src = static_cast<const uint8_t *>(dis->data[0]);
for (unsigned y = 0; y < state->frame_h; y++) {
last_ev = state->copy_queue.memcpy(dst, src, row_bytes);
dst += row_bytes;
Expand Down Expand Up @@ -622,16 +622,16 @@ extern "C" int vmaf_sycl_upload_plane(VmafSyclState *state, const void *src, uns
return -EINVAL;

unsigned bytes_per_pixel = (bpc + 7) / 8;
size_t row_bytes = (size_t)w * bytes_per_pixel;
size_t row_bytes = static_cast<size_t>(w) * bytes_per_pixel;

try {
if (pitch == row_bytes) {
/* Contiguous — single H2D copy via copy queue */
state->copy_queue.memcpy(target_buf, src, row_bytes * h);
} else {
/* Pitched — row-by-row H2D copy via copy queue */
uint8_t *dst = (uint8_t *)target_buf;
const uint8_t *s = (const uint8_t *)src;
auto *dst = static_cast<uint8_t *>(target_buf);
const auto *s = static_cast<const uint8_t *>(src);
for (unsigned y = 0; y < h; y++) {
state->copy_queue.memcpy(dst, s, row_bytes);
dst += row_bytes;
Expand Down
60 changes: 60 additions & 0 deletions docs/adr/0839-cpp23-shadow-const-fixes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# ADR-0839: C++23 wave — shadow-identifier and implicit-cast cleanup

- **Status**: Accepted
- **Date**: 2026-05-29
- **Deciders**: lusoris
- **Tags**: `cpp23`, `lint`, `core`, `sycl`, `fork-local`

## Context

The C++23 wave (ADR-0708 and follow-on waves) converted several `.c` files to `.cpp`.
After conversion, three categories of C-allows-but-C++-warns issues remained unaddressed:

1. **Shadowed identifier** — `fex_ctx_vector.cpp`: local variable `capacity` at the
realloc growth path shares a name with the `rfe->capacity` struct member. Clang's
`-Wshadow` and clang-tidy `bugprone-shadow` flag this.
2. **Implicit C-style casts** — `feature_collector.cpp`: `(char*)`, `(void*)`,
`(FeatureVector**)`, `(VmafFeatureCollector*)`, `(VmafPredictModel*)`, and the
`(decltype(...))` pattern were carried forward from the C original. In C++ these are
`reinterpret_cast`-equivalent and defeat the narrowing checks `static_cast` provides.
`(VmafModelFlags)0` is a C-style enum cast where `static_cast<VmafModelFlags>(0)` is
the idiomatic C++ form.
3. **Implicit C-style casts** — `core/src/sycl/common.cpp`: `(uint8_t*)`, `(size_t)`, and
`(unsigned)` casts in the frame-upload and plane-copy paths.

None of these caused incorrect behaviour on existing platforms, but they produced
clang-tidy `cppcoreguidelines-pro-type-cstyle-cast` hits and blocked the `-Wshadow`
clean-compile goal for the C++23-wave files.

## Decision

Apply mechanical fixes to the three files:

- Rename `capacity` → `new_capacity` in `fex_ctx_vector.cpp` to eliminate the shadow.
- Replace every C-style cast with the appropriate `static_cast<>` in
`feature_collector.cpp` and `common.cpp`.
- No behaviour change; the fixes are type-system-only.

## Alternatives considered

| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
| NOLINT suppressions | Zero-diff to logic | Does not fix the underlying issue; NOLINT requires ADR citation per ADR-0278 | Against policy: refactor first |
| Leave as-is until a dedicated lint sweep | Minimal churn | ADR-0141 (touched-file cleanup rule) requires fixing warnings in files we touch | Violated project rule |

## Consequences

- **Positive**: three files now compile clean under `-Wshadow` and without
`cppcoreguidelines-pro-type-cstyle-cast` hits; `static_cast<>` makes the intent of
each conversion auditable.
- **Negative**: none — the changes are purely syntactic.
- **Neutral**: the `(decltype(...))` idiom in `feature_collector.cpp` is replaced with
`static_cast<decltype(...)>(...)` which is slightly more verbose but standard C++.

## References

- ADR-0708 (C++23 pilot), ADR-0723 (fex_ctx_vector wave), ADR-0727 (dict wave),
ADR-0729 (model/feature_name/picture_copy wave), ADR-0733 (output wave),
ADR-0735 (cpu/ref/thread_locale/log wave).
- ADR-0141 (touched-file cleanup rule).
- ADR-0278 (NOLINT citation closeout).
15 changes: 6 additions & 9 deletions docs/rebase-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,13 @@ syncs from `upstream/master` (Netflix/vmaf). Required by

---

## cli-help-exit-zero (2026-05-29)
## cpp23-shadow-const-fixes (2026-05-29, ADR-0839)

**Files touched:**
`core/tools/cli_parse.c`, `cmd/vmafx-server/main.go`, `cmd/vmafx-node/main.go`

**Rebase impact:** Low. `cli_parse.c` is fork-extended beyond upstream (GPU
backend flags, precision, tiny-model flags). Any upstream sync that touches
`cli_parse.c` will produce a context conflict near the `long_opts[]` table and
`usage()` body; the `ARG_HELP` entry and the `exit_code`/`out` split need to be
re-applied. The Go files are fork-only and have no upstream counterpart.
no rebase impact: REASON — all changed files (`core/src/feature/feature_collector.cpp`,
`core/src/fex_ctx_vector.cpp`, `core/src/sycl/common.cpp`) are fork-local C++23
conversions with no upstream counterpart in Netflix/vmaf master. The upstream originals
(`feature_collector.c`, `fex_ctx_vector.c`) are pure C; the `.cpp` files were created by
the fork's C++23 wave (ADR-0708 ff.) and are not subject to upstream rebase churn.

---

Expand Down
Loading