Skip to content

Write a header's definitions so it can be included from more than one translation unit [patch] - #457

Open
matt-edmondson wants to merge 1 commit into
mainfrom
fix/header-inline-definitions-423
Open

matt-edmondson wants to merge 1 commit into
mainfrom
fix/header-inline-definitions-423

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #423

What was wrong

A SourceFile with IsHeader = true only gained #pragma once. That guard works within one translation unit but not across units. A header holding a free function, a function lowered from a method (C), or a mutable file-scope variable defined each one with external linkage in every unit that included it. Linking then failed with multiple definition of 'twice', 'limit', 'P_get', 'geo::half(int)' and so on.

Change

CFamilyGenerator.WritingHeader reports whether the file being generated is a header. The C and C++ generators check it:

C++ header C header
Free function / function in a namespace inline static inline
Function lowered from a method (already inline, defined in the class) static inline
Mutable file- or namespace-scope variable inline (C++17 inline variable) static: one copy per translation unit

For the C mutable global the issue offered two options. I chose static, which is what the C generator already does for a type's static fields (static int P_count). The alternative, extern plus a definition somewhere, would need a translation unit that a header-only output doesn't have. The choice is documented in the code and in CLAUDE.md.

These stay as they were:

  • Prototypes: abstract and defaulted declarations in C.
  • Class members, friends, constexpr and abstract functions in C++. These are already inline or have no definition.
  • Everything in a file that isn't a header.

Tests

HeaderLinkageTests builds a header with a mutable variable and a function at file scope, a struct with an instance method, a static method and a static field, and a namespace holding a variable and a function.

  • Two translation units include it and are compiled, linked and run. C is built with -std=c11 -Wall -Wextra -pedantic-errors, C++ with -std=c++17 -Wall -Wextra -pedantic-errors.
  • Separate tests assert the emitted inline / static inline / static, and that output for a file that isn't a header is unchanged.

I reverted the generator change and four of the five tests failed with the linker errors from the issue. With the change back in place the full suite passes: 1669 tests.


Generated by Claude Code

… translation unit [patch]

A SourceFile marked IsHeader only gained #pragma once, so a free function, a function a method
is lowered to, or a mutable file-scope variable in it was defined with external linkage in
every translation unit that included it, and the program failed to link with "multiple
definition". C++ now writes free functions and namespace-scope variables inline. C writes
functions static inline and mutable variables static, one copy per translation unit, since C
has no inline variable. Output that is not a header is unchanged.

Fixes #423

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW4Rc5sqSEVHr6NgrkT
@sonarqubecloud

sonarqubecloud Bot commented Nov 5, 2026

Copy link
Copy Markdown

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.

C and C++ headers define free functions and mutable file-scope variables without inline/static, so including the header twice fails to link

2 participants