Skip to content

Fix NRVO build errors with clang-21+#270

Merged
syoyo merged 1 commit into
releasefrom
fix/nrvo-single-return-variable
Apr 10, 2026
Merged

Fix NRVO build errors with clang-21+#270
syoyo merged 1 commit into
releasefrom
fix/nrvo-single-return-variable

Conversation

@syoyo

@syoyo syoyo commented Apr 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Restructure functions across 8 source files to use a single named return variable per function, enabling Named Return Value Optimization (NRVO)
  • Fixes -Werror,-Wnrvo build failures introduced by clang-21 without triggering -Wpessimizing-move
  • Verified clean builds with g++ 13.3, clang++-20, and clang++-21

Test plan

  • Build with g++ 13.3 — 0 errors, 0 warnings
  • Build with clang++-20 — 0 errors, 0 warnings
  • Build with clang++-21 (original failing compiler) — 0 errors, 0 warnings

🤖 Generated with Claude Code

…nction

Restructure functions that return named local variables to use a single
return variable across all code paths, enabling Named Return Value
Optimization (NRVO). This fixes -Wnrvo errors (clang-21+) without
triggering -Wpessimizing-move.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 10, 2026 20:51
@claude

claude Bot commented Apr 10, 2026

Copy link
Copy Markdown

Claude finished @syoyo's task —— View job


Pull Request Review: NRVO Build Fix

This PR addresses Named Return Value Optimization (NRVO) build errors introduced by clang-21+ by restructuring functions to use a single named return variable across all code paths.

✅ Code Quality and Best Practices

Excellent implementation of NRVO compliance:

  • Functions now consistently declare a single return variable at the top
  • All code paths use the same named variable for return statements
  • Changes enable compiler optimizations while maintaining readability

Examples of well-executed transformations:

// Before (problematic for NRVO):
if (condition) {
  return std::string(); // Creates temporary
}

// After (NRVO-compliant):
std::string result;
if (condition) {
  result.clear();
}
return result;

✅ Correctness and Bug Prevention

No functional changes introduced:

  • All transformations preserve exact same behavior
  • Return value semantics remain identical
  • Error handling paths maintain proper state management

Particularly well-handled in src/str-util.cc:

  • to_utf8_chars(): Uses utf8_chars.clear() instead of returning new vector
  • to_codepoints(): Uses cps.clear() instead of returning new vector
  • makeIdentifierValid(): Assigns to existing variable instead of immediate return

✅ Performance Implications

Positive impact:

  • Enables NRVO optimizations reducing unnecessary copy/move operations
  • Particularly beneficial for functions returning std::string and std::vector
  • No performance regressions introduced

Smart reuse of existing variables:

  • Functions like AssetResolutionResolver::resolve() reuse the result variable consistently
  • Clear separation between success/failure paths using .clear() method

✅ Security Considerations

No security implications:

  • Changes are purely structural optimizations
  • No modification to validation logic or error handling
  • Memory safety preserved through proper variable lifecycle

✅ Test Coverage

Existing tests should cover changes:

  • No new logic paths introduced
  • Behavioral equivalence maintained
  • Build verification with multiple compilers demonstrates correctness

✅ Implementation Consistency

Excellent consistency across files:

  • Same pattern applied uniformly across 8 source files: src/asset-resolution.cc:105:1, src/composition.cc:584:1, src/io-util.cc:395:1, src/prim-types.cc:469:1, src/str-util.cc:111:1, src/tydra/scene-access.cc, src/usdGeom.cc, src/usdShade.cc
  • Proper handling of both simple and complex return types
  • Clear variable names (result, paths, s) that match function purpose

Minor Observations

  1. Variable naming: Consider using more descriptive names like resolved_path instead of result in some contexts for enhanced readability
  2. Documentation: The commit message effectively explains the NRVO requirement and compiler compatibility

Overall Assessment

This is a high-quality PR that successfully addresses a specific compiler compatibility issue:

Strengths:

  • ✅ Fixes clang-21+ build errors without introducing regressions
  • ✅ Maintains code clarity and readability
  • ✅ Enables performance optimizations via NRVO
  • ✅ Consistent implementation across the codebase
  • ✅ Well-tested approach (verified with g++ 13.3, clang++-20, clang++-21)

Recommendation: Approve and merge

This PR demonstrates careful refactoring that solves a real compiler compatibility issue while improving performance characteristics. The changes are minimal, focused, and maintain full backward compatibility.


Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses clang-21 -Wnrvo build failures by refactoring several functions to consistently return a single named local variable (enabling NRVO) and avoiding patterns that trigger -Wpessimizing-move.

Changes:

  • Refactor multiple functions to use a single named return variable and return it across all paths.
  • Replace return {} / return T() in some functions with returning a named variable (often after clear()).
  • Minor whitespace/comment touch-ups while restructuring returns.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/usdShade.cc Uses a single local std::string for all return paths in SDR metadata getter.
src/usdGeom.cc Refactors get_joints() to return a single named vector across branches.
src/tydra/scene-access.cc Refactors GetBlendShapes() to return a single named vector across error paths.
src/str-util.cc Refactors several string/UTF helpers to return named locals consistently.
src/prim-types.cc Refactors get_colorSpace() to always return a single named token.
src/io-util.cc Refactors ExpandFilePath() empty-path early return to return named local.
src/composition.cc Refactors recursive reconstruct helper to return the named optional.
src/asset-resolution.cc Refactors resolve() to return a single named std::string result.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/str-util.cc
Comment on lines +548 to +549
utf8_chars.clear();
return utf8_chars;

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this error path, clearing and returning the local utf8_chars can move a vector with non-zero capacity to the caller (capacity is retained after clear()), which is a behavioral/perf change vs returning a fresh empty vector. Consider resetting the return variable to a truly empty vector (e.g., swap/assign-from-empty) before returning so failures don't retain potentially large allocations.

Suggested change
utf8_chars.clear();
return utf8_chars;
return {};

Copilot uses AI. Check for mistakes.
Comment thread src/str-util.cc

if ((cp > kMaxUTF8Codepoint) || (char_len == 0)) {
return std::vector<uint32_t>();
cps.clear();

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to to_utf8_chars, using cps.clear(); return cps; returns an empty vector but keeps its capacity, potentially transferring large allocations to the caller on invalid UTF-8 input. Consider resetting the named return variable to an actually-empty vector (swap/assign-from-empty) before returning.

Suggested change
cps.clear();
std::vector<uint32_t>().swap(cps);

Copilot uses AI. Check for mistakes.
Comment thread src/tydra/scene-access.cc
Comment on lines 2572 to 2576
const Prim *bsprim{nullptr};
if (!stage.find_prim_at_path(targetPath, bsprim, err)) {
return std::vector<
std::pair<std::string, const tinyusdz::BlendShape *>>{};
dst.clear();
return dst;
}

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On this failure path, dst.clear(); return dst; can return an empty vector that still retains capacity/allocations from any elements pushed earlier in the function, moving that memory to the caller. Consider resetting the return variable to a truly-empty vector (swap/assign-from-empty) before returning so error returns don't carry large retained capacity.

Copilot uses AI. Check for mistakes.
Comment thread src/usdGeom.cc

if (primvar.has_indices()) {
// indexed primvar for skel:joint is not supported
// indexed primvar for skel:joint is not supported

Copilot AI Apr 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says skel:joint but the actual property key used here is skel:joints (plural). Updating the comment to match avoids confusion when debugging/reading logs.

Suggested change
// indexed primvar for skel:joint is not supported
// indexed primvar for skel:joints is not supported

Copilot uses AI. Check for mistakes.
@syoyo
syoyo merged commit 11a2d36 into release Apr 10, 2026
10 checks passed
@syoyo
syoyo deleted the fix/nrvo-single-return-variable branch April 10, 2026 20:59
rifont added a commit to taucad/tinyusdz that referenced this pull request Apr 17, 2026
Brings in:
- Fix NRVO build errors by ensuring single named return variable per function (lighttransport#270)
- Add LightUSD-C information to README
- Update issue templates

Fork fixes preserved:
- pprinter.cc: corrected wrapS/wrapT labels and std::boolalpha for bool attrs
- usdShade.hh + render-data.cc: UsdUVTexture::st typed as float2 per UsdPreviewSurface spec
- render-data.cc: skip degenerate faces in ConvertMesh instead of erroring

Made-with: Cursor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants