Skip to content
Open
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
11 changes: 3 additions & 8 deletions lib/symboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8690,7 +8690,6 @@ template<class F>
static Result accumulateStructMembers(const Scope* scope, F f, ValueType::Accuracy accuracy, const Settings& settings)
{
size_t total = 0;
std::set<const Scope*> anonScopes;
for (const Variable& var : scope->varlist) {
if (var.isStatic())
continue;
Expand All @@ -8701,12 +8700,7 @@ static Result accumulateStructMembers(const Scope* scope, F f, ValueType::Accura
const MathLib::bigint dim = std::accumulate(var.dimensions().cbegin(), var.dimensions().cend(), MathLib::bigint(1), [](MathLib::bigint i1, const Dimension& dim) {
return i1 * dim.num;
});
if (var.nameToken()->scope() != scope && var.nameToken()->scope()->definedType) { // anonymous union
const auto ret = anonScopes.insert(var.nameToken()->scope());
if (ret.second)
total = f(total, *vt, dim, bits);
}
else if (vt->container && vt->container->startPattern == "std :: array <") {
if (vt->container && vt->container->startPattern == "std :: array <") {

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.

AI review

Removing the anonScopes handling means every member of an anonymous union is now passed to f. getAlignOf is fine with this because it takes the max. In getSizeOf, though, these members are now added together unless the new isUnion heuristic matches, and it doesn't match plain-typed members. That causes the regressions described on line 8837.

const ValueType vtElement = ValueType::parseDecl(vt->containerTypeToken, settings);
total = f(total, vtElement, dim, bits);
}
Expand Down Expand Up @@ -8840,7 +8834,8 @@ size_t ValueType::getSizeOf( const Settings& settings, Accuracy accuracy, SizeOf
currentBitfieldAlloc = 0;
currentBitCount = 0;
}
return typeScope->type == ScopeType::eUnion ? std::max(total, n) : total + padding + n;
const bool isUnion = typeScope->type == ScopeType::eUnion || (vt2.typeScope && vt2.typeScope->nestedIn->type == ScopeType::eUnion && vt2.typeScope->nestedIn->className.empty());

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.

AI review

This only gives union semantics to members whose own type is an unnamed struct declared inside an anonymous union. Any other member of an anonymous union is summed like a struct member: int/double members, arrays, and members of named types.

  • struct { union { int i; double d; }; } gives 16 (real size 8).
  • struct { union { char c[24]; int i; }; } gives 28. Base gave the correct 24.
  • struct { union { char c[16]; int i; }; } now triggers a false passedByValue.

The condition also takes std::max with the running total of the enclosing struct, so the anonymous union overlaps members declared before it. struct D { int x; union { int i; struct { char a[24]; } y; }; } gives 24 instead of 28.

The anonymous union scope should be sized once, as the max of its members. That size should then be added with padding to the enclosing struct.

return isUnion ? std::max(total, n) : total + padding + n;
};
Result result = accumulateStructMembers(typeScope, accHelper, accuracy, settings);
size_t total = result.total;
Expand Down
11 changes: 11 additions & 0 deletions test/testother.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2787,6 +2787,17 @@ class TestOther : public TestFixture {
"}\n");
ASSERT_EQUALS("", errout_str());

check("struct S {\n" // #15130
" union {\n"
" int i;\n"
" struct {\n"
" char a[24];\n"
" } x;\n"
" };\n"
"};\n"
"int f(S s) { return s.i; }\n");
ASSERT_EQUALS("[test.cpp:9:9]: (performance) Function parameter 's' should be passed by const reference. [passedByValue]\n", errout_str());

/*const*/ Settings settingsWin64 = settingsBuilder().platform(Platform::Type::Win64).build();
check("using ui64 = unsigned __int64;\n"
"ui64 Test(ui64 one, ui64 two) { return one + two; }\n",
Expand Down
14 changes: 14 additions & 0 deletions test/testvalueflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1862,6 +1862,20 @@ class TestValueFlow : public TestFixture {
ASSERT_EQUALS(1U, values.size());
ASSERT_EQUALS(settings.platform.sizeof_int, values.back().intvalue);
ASSERT_EQUALS_ENUM(ValueFlow::Value::ValueKind::Known, values.back().valueKind);

code = "struct S {\n"
" union {\n"
" int i;\n"
" struct {\n"
" char a[24];\n"
" } x;\n"
" };\n"
"};\n"
"int f() { return sizeof(S); }\n";
values = tokenValues(code, "( S");
ASSERT_EQUALS(1U, values.size());
ASSERT_EQUALS(24, values.back().intvalue);
ASSERT_EQUALS_ENUM(ValueFlow::Value::ValueKind::Known, values.back().valueKind);
}

void valueFlowComma()
Expand Down
Loading