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
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`main`, the release pipeline automatically replaces `[current]` with the next
version number before tagging the release.

## [current]

### Fixed
- `--emit=lib`'s `aether_lib_meta()` catalog entry point is now emitted
weak (`__attribute__((weak))` under GCC/Clang), so linking N lib TUs
into one artifact — aeb's regen shape, one `--emit=lib` C per module
and one final link — no longer dies with N-1 "multiple definition of
`aether_lib_meta`" errors or forces `-Wl,--allow-multiple-definition`
back onto the link line (the GNU-only escape hatch the 0.539 multi-TU
static-clone model retired; ld64 rejects it) (#1590). Lone-`.so`
consumers are unchanged: one definition, same dlsym contract; in a
multi-TU link the first TU's catalog wins. The
`multi_tu_import_link` regression now has an `--emit=lib` phase that
links three catalog-bearing TUs with no escape hatch and pins the
weak linkage via nm.

## [0.540.0]

### Added
Expand Down
24 changes: 22 additions & 2 deletions compiler/codegen/codegen.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions docs/emit-lib.md
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,15 @@ source file, source line, in a layout-stable, allocation-free form
that any FFI consumer (Python ctypes, Java Panama, Ruby Fiddle,
Node-API, hand-rolled `dlsym`) can walk directly.

The entry point is emitted **weak** (`__attribute__((weak))` under
GCC/Clang, #1590), so an orchestrator that links several `--emit=lib`
TUs into one artifact (aeb's regen shape: one C per module, one final
link) needs no `-Wl,--allow-multiple-definition` — the multi-TU
static-clone link model of 0.539 holds for lib TUs too, on ld64 as well
as GNU ld. In a lone `.so` nothing changes: one definition, same dlsym
contract. In a deliberate multi-TU link the first TU's catalog wins;
treat `aether_lib_meta` as meaningful only on single-module artifacts.

```c
typedef struct {
const char* aether_name; /* "double_int", "std.fs.copy" */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,62 @@ if command -v nm >/dev/null 2>&1; then
fi
fi

echo " [PASS] multi_tu_import_link: 4 TUs, no dedup flags, runs (42 43)"
# ---- Phase 2: the SAME shape with --emit=lib TUs (#1590) ------------------
# aeb regen TUs are emitted with --emit=lib, and every lib TU carries the
# aether_lib_meta() reflection catalog (issue #403). Before #1590 that
# symbol had external linkage, so this exact link died with N-1
# "multiple definition of `aether_lib_meta'" errors — a hole the plain-
# emission phase above cannot see. The catalog entry point is now weak.
TMP2="$TMP/libtu"
mkdir -p "$TMP2"
for m in helper/module a/module b/module; do
out="$TMP2/$(echo "$m" | tr '/' '_').c"
( cd "$SCRIPT_DIR" && "$AETHERC" --emit=lib "$m.ae" "$out" ) >"$TMP2/cc.log" 2>&1 \
|| fail "aetherc --emit=lib failed on $m.ae" "$TMP2/cc.log"
done
# The driver stays plain emission (it has main()).
( cd "$SCRIPT_DIR" && "$AETHERC" main.ae "$TMP2/main.c" ) >"$TMP2/cc.log" 2>&1 \
|| fail "aetherc failed on main.ae (lib phase)" "$TMP2/cc.log"

for c in "$TMP2"/*.c; do
$CC -c "$c" -o "${c%.c}.o" $CFLAGS_ALL 2>"$TMP2/gcc.log" \
|| fail "lib-TU compile failed: $c" "$TMP2/gcc.log"
done

# Load-bearing: three catalogs, one link, still NO escape hatch.
if ! $CC "$TMP2"/*.o -o "$TMP2/repro_lib" $LIBS_ALL 2>"$TMP2/link.log"; then
if grep -qi "aether_lib_meta" "$TMP2/link.log"; then
fail "duplicate aether_lib_meta across --emit=lib TUs (#1590 regression)" "$TMP2/link.log"
fi
if grep -qiE "duplicate symbol|multiple definition" "$TMP2/link.log"; then
fail "duplicate symbols across --emit=lib TUs" "$TMP2/link.log"
fi
fail "lib-TU link failed" "$TMP2/link.log"
fi

out=$("$TMP2/repro_lib" 2>&1)
[ "$out" = "42 43" ] || fail "wrong output from lib-TU build: '$out'"

# Belt: the catalog entry point is emitted weak, not strong. The nm
# spelling is format-specific: ELF/COFF nm marks a weak definition
# "W"/"w", but Mach-O nm prints it as a plain "T" and only `nm -m`
# reveals the "weak external" flag — grepping for T there would
# false-fail the exact platform this model exists for (ld64).
if command -v nm >/dev/null 2>&1; then
a_lib_o=$(ls "$TMP2"/a_module.o)
case "$(uname -s)" in
Darwin)
nm -m "$a_lib_o" | grep -q "weak external _aether_lib_meta" \
|| fail "aether_lib_meta missing/not weak in a lib TU (nm -m)"
;;
*)
if nm "$a_lib_o" | grep -qE " T _?aether_lib_meta"; then
fail "aether_lib_meta has strong external linkage in a lib TU (#1590)"
fi
nm "$a_lib_o" | grep -qE " [Ww] _?aether_lib_meta" \
|| fail "aether_lib_meta missing/not weak in a lib TU"
;;
esac
fi

echo " [PASS] multi_tu_import_link: 4 TUs, no dedup flags, runs (42 43); --emit=lib TUs link too (#1590)"
Loading