Skip to content

[ntuple] Fail cleanly if a field value cannot be constructed - #23806

Open
SamdaniSayam wants to merge 1 commit into
root-project:masterfrom
SamdaniSayam:fix/13910-rntuple-makefield-construction
Open

SamdaniSayam wants to merge 1 commit into
root-project:masterfrom
SamdaniSayam:fix/13910-rntuple-makefield-construction

Conversation

@SamdaniSayam

Copy link
Copy Markdown

Summary

RClassField::ConstructValue() ignored failures from TClass::New(). For an abstract class, or a class without a default constructor, TClass::New() first JITs a constructor wrapper that fails to compile and then returns nullptr. RNTuple ignored that return value, so MakeField<T>() returned a non-null std::shared_ptr<T> to unconstructed memory. Destroying that value crashes in RClassDeleter. The failed JIT compilation is also the trigger for the Cling state corruption reported in #13910.

This PR:

  • checks kIsAbstract and HasDefaultConstructor(kTRUE) before calling TClass::New(), throws RException with a useful message otherwise, and checks the TClass::New() return value;
  • applies the same guard to RSoAField and RStreamerField;
  • frees the value buffer in RFieldBase::CreateObjectRawPtr() if construction throws;
  • constructs the value in REntry::AddValue() before recording the field name/type, so a failed MakeField() leaves the default entry/model unchanged;
  • adds a roottest regression for abstract types, no-default-constructor types, partial-entry corruption, and continued use of the same model/interpreter.

There are no public API changes: the new behavior is a thrown RException where a broken object used to be returned.

Validation

  • g++ -fsyntax-only passes for the modified translation units against 6.40.04 headers.
  • Before the patch, MakeField<Abstract>() emitted the Cling error, returned a non-null pointer to unconstructed memory, and teardown segfaulted in RClassDeleter.
  • After the patch, both the abstract and no-default-constructor cases throw RException; the same model still accepts std::vector<float> and a concrete derived class with an abstract base.
  • With the guard but the stock REntry::AddValue, the regression's HasField checks catch the partially added field; with the reordered AddValue, they pass.
  • The new roottest macro was run through ACLiC against 6.40.04 with the patched header/guard: all MakeField failure checks passed.

I could not run a full ROOT rebuild in this environment (8 cores, limited memory), so CI will be the first full-build run.

Related to #13910 (this addresses the RNTuple side; the Cling transaction/rollback discussion may still need a separate fix).

RClassField::ConstructValue() ignored TClass::New() failures, so MakeField<Abstract>() or MakeField<T>() for a class without a default constructor returned a pointer to unconstructed memory. For interpreted classes, TClass::New() also JITs a constructor wrapper; the failed compilation is what triggered the Cling state corruption from root-project#13910.

Check kIsAbstract and HasDefaultConstructor() before calling TClass::New(), throw an RException if construction fails, and free the allocated buffer. Also make REntry::AddValue() construct the value before recording the field name/type so a failed MakeField() leaves the entry unchanged.

Add a roottest regression covering abstract classes, classes without a default constructor, and continued model/interpreter use afterwards.
@dpiparo

dpiparo commented Oct 25, 2026

Copy link
Copy Markdown
Member

Did you have a look to https://github.com/root-project/root/blob/master/CONTRIBUTING.md#ai-assisted-coding-disclosure ?
Also, the configuration you mention is perfectly fine for building and testing root: could you please honour this requirement before we inspect the code?

@SamdaniSayam

Copy link
Copy Markdown
Author

AI-assisted coding disclosure (per CONTRIBUTING.md):

  • This PR's code was drafted with LLM assistance and has been thoroughly reviewed and understood by me; I take full responsibility for it.
  • Validation done locally: g++ -fsyntax-only passes for the modified TUs against 6.40.04 headers; roottest regression run through ACLiC. Full ROOT rebuild was not possible in this environment (8 cores, limited memory) — CI will be the first full build.
  • Re: the reviewer's note on configuration: I'll enable dev=ON (warnings-as-errors) and re-run the build/test before the code is inspected.

This branch has not been deployed

No deployments
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