Repository navigation
Conversation
c17 accepted the -Werror family and ignored it, so a configure probe that relies on a warning failing the compile got the wrong answer. - diag: a Werror switch folded in command-line order (-Werror / -Wno-error for every warning; -Werror=X / -Wno-error=X give group X its own verdict, and -Werror=X also enables X). Every warning goes through one emit path, give_warning: a shown warning that -Werror covers becomes an error tagged [-Werror] or [-Werror=<group>], and the unit ends with gcc's "all/some warnings being treated as errors" line. -w and system headers are never promoted. The named-group sites call group_warning(group, ..), so the group is known at emit time. -Werror=pedantic enables -Wpedantic. - -Wno-X is now order-aware: a later -WX or -Werror=X turns X back on. - System headers: a header found in a system directory (or beside a system header that included it) and the bundled headers are system streams, as in gcc; before, only `# N "f" 1 3` linemarkers made one, so libc's warnings were shown and -Werror would have failed on them. - Driver: the unrecognized -f/-g option warnings honour -w and -Werror wherever they appear; under -Werror the three cases gcc refuses outright (unknown option, -o with -c or -E and several files) are errors. Other driver warnings stay warnings, as gcc's do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rs refuses them 40 plain warning sites in cc/token and cc/parse are constraint violations gcc 13 warns about by default and makes errors under -pedantic-errors. They now go through diag::pedwarn_default: the same warning text by default, an error under -pedantic-errors. Preprocessor: missing terminating quote, #if shift overflow, __VA_OPT__ and __VA_ARGS__ outside a variadic macro, extra tokens after a directive, no whitespace after a macro name, macro redefinition. Parser: declaration does not declare anything, excess initializer elements, initializer-string too long, inline/_Noreturn on a parameter or variable, inline main, conditional and comparison pointer mismatches, __func__ at file scope, a constant so large it is unsigned, K&R parameter defaulting to int, empty attribute declarations, misplaced fallthrough, the non-fatal assignment/argument/return conversions, and -fpermissive's implicit int and implicit function declaration. Each was swept against gcc 13 with and without -pedantic-errors; sites where gcc keeps a plain warning are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rnal error loc_to_gp_operand printed a thread-local as Local Exec (MemAddr::TlsLocalExec), the inline-asm raw moves to and from a location as %fs:sym@TPOFF, and loc_to_asm_string / loc_to_asm_operand as a plain sym(%rip), whatever the TLS model. None is reached: under the static ELF models register operands arrive loaded and memory operands go through global_mem, and under the call-based models ir::tls removes thread-local operands. Each now reports through report_tls_operand, the internal error check_tls_reached_only_by_address already gave, factored out so both share one implementation. MemAddr::TlsLocalExec stays: the load, store and global_mem paths build it when Local Exec is the right model. Unit tests show the guard firing at all five sites; an e2e test runs inline asm on local and extern thread-locals of every constraint class at -O0, -O2, -fPIE and -fPIC, and an asm test checks FreeBSD shared objects use Initial Exec for them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An operand whose suffix names no language (an object under another name,
a GNU ld linker script, a version script) was warned about as an
"unrecognized file type" and dropped from the link. It is now a linker
input in its command-line place, exactly like a .o, with no warning.
Suffixes gcc gives another front end are refused with an error naming
the language ("c17 does not compile C++", Objective-C, Objective-C++,
Fortran, Go, D, Ada, Modula-2) instead of reaching ld. A .h is
preprocessed under -E/-M/-MM and otherwise refused ("c17 does not write
precompiled headers"). .sx is preprocessed assembly, as .S.
When nothing is linked, every linker input (objects included) gets gcc's
"linker input file unused because linking not done" warning, which, as
in gcc, -w does not silence and -Werror does not promote. -M and -MM now
count as not linking: with an object operand they used to run the link.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
c17 accepted every -W<name> in silence, so configure probes (AX_CHECK_COMPILE_FLAG, meson has_argument, cmake check_c_compiler_flag) concluded it supports any warning flag. - New cc/warn_options.rs: one table classifying warning names. 12 are c17's own groups/switches (attributes, overflow, pedantic, error, c17-dialect, ...); 187 are gcc C names accepted with no effect, drawn from Debian's dpkg-buildflags, the autoconf-archive warning macros, meson's warning levels, the c17 tests, the torture suite's dg-options and the projects c17 builds, each verified against gcc 13 (except -Wcompare-distinct-pointer-types, gcc 14's, which the torture suite passes). 21 stems take a value (-Wformat=N, -Wlarger-than=SIZE, -Wnormalized=WORD, ...), checked with gcc's range/size-unit/choice text. - Driver: an unknown -W<name> is "unrecognized command-line option", and an unknown -Werror=X / -Wno-error=X is "'-Werror=X': no option '-WX'" (reported only when the driver-level checks pass), exit 1 before any compile. -Wl,, -Wa, and -Wp, are untouched; bare -W stays -Wextra. - -Wno-<unknown> is accepted silently; if a translation unit shows any diagnostic, each gets gcc's "may have been intended to silence earlier diagnostics" note, last first, ahead of the -Werror closing line. diag::report_promoted_warnings becomes diag::finish_unit, which gives both. - test_compile's apply_flags refuses an unknown name the same way. -Wpedantic-errors is refused, as gcc refuses it: -pedantic-errors is folded in among the -W options internally but is not a -W name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ves; warn on #if overflow, as gcc does - `\x` with no hex digit is an error where the literal is decoded (string, character constant, #if, asm), via a new Escaped::EmptyHex; the lexer no longer warns, so -E and skipped groups are silent like gcc. - A comment reaching EOF is the error "unterminated comment" at its `/*`. - A literal missing its quote keeps gcc's default pedwarn (now also at EOF) and is marked Spelling::Unterminated: -E and `#` spell it without an invented quote, and compile_tokens refuses one that reaches the parser with "missing terminating X character". - `#foo`, `#!`, `#"x"` are "invalid preprocessing directive #..." errors and `# 12abc` is '"12abc" after # is not a positive integer', except in skipped groups and assembly. - #if signed +, -, *, /, unary - and lossy left shifts give gcc's default pedwarn "integer overflow in preprocessor expression"; shifts take gcc's values (negative count reverses, >= 64 shifts everything out) and only warn when signed bits are lost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-fpic, -fPIC, -fpie, -fPIE and -fno-pic/-fno-PIC/-fno-pie/-fno-PIE are one option to gcc: the last one given wins, and every -fno- spelling means position-dependent code, PIE included. c17 kept three independent booleans: -fno-pic, -fno-PIC and -fno-PIE were "unrecognized option, ignored", -fPIC stuck once seen, and -fPIC -fno-pie still defined __PIC__. PositionIndependence is now an enum (Absolute, Pic(level), Pie(level)) that the driver folds from the family in command-line order, defaulting to a large-model PIE on Linux as Debian's gcc does. It drives the macros and code generation: - __PIC__/__pic__ and __PIE__/__pie__ are 1 for -fpic/-fpie and 2 for -fPIC/-fPIE, matching gcc 13; any -fno- form leaves them undefined (Mach-O still defines __PIC__ 2, as clang does). - -pie, -no-pie, -static-pie and -shared are link options again and no longer change the macros or the code. - -fno-pic generates the position-dependent code -fno-pie always did (direct RIP-relative access on x86-64, no GOT for globals), which links into both -no-pie and -pie executables. A shared object still gets shared-object code whatever the family says. - The link is a PIE as the last of -pie/-no-pie says, or else as the code was compiled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-ftls-model=global-dynamic|local-dynamic|initial-exec|local-exec was "unrecognized option, ignored". gcc treats it as a floor: each access takes the stronger of that model and the one the code can prove. So an executable keeps Local Exec for its own thread-locals whatever is asked, initial-exec takes -fPIC code off the dynamic call, and local-exec is Local Exec for every access, a thread-local defined elsewhere included. The last one given wins; an unknown name is gcc's "unknown TLS model" error with its list of valid arguments, and an empty one is "missing argument to '-ftls-model='". The codegen's shared_mode flag becomes a TlsPolicy (shared code plus the floor), which Target::tls_access and the backends' Initial Exec test both read, so ir::tls and the backends still agree on the model. c17 has no Local Dynamic sequence; local-dynamic gets the descriptor call, a valid Global Dynamic access, as gcc's aarch64 code does. Mach-O keeps its single TLV access, as clang does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cc's -Woverflow `char z = 300;` was emitted as `.byte 300`, and every narrow static initializer -- file scope and block-scope static, scalar, member, array element, bit-field, enumerated object -- carried the unconverted value to the assembler, which truncated it with a warning of its own. One function, convert_integer_constant, now converts an integer constant to its target type (modulo 2^N, as gcc defines C17 6.3.1.3p3) for the static initializer and gives gcc's -Woverflow when the conversion changes the value in the cases gcc reports: "overflow in conversion", "unsigned conversion" and "conversion from '...' to '...' changes value from 'A' to 'B'", naming a bit-field as gcc does (`signed char:3`). The same check runs on every implicit conversion in code: initialization, assignment (to a bit-field too), `return` and a prototyped argument. The floating saturation warning shares the wording and now names types with gcc's spelling (`short int`, `long unsigned int`). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ake an overflowed array size variable `int x = 2147483647 + 1;` compiled silently. gcc 13 says "integer overflow in expression of type 'int' results in '-2147483648'" [-Woverflow] at the operator, for `+`, `-`, `*`, `/` and `%` on signed constants and "integer overflow in expression '-2147483648' of type 'int' results in '-2147483648'" for negating the minimum -- once, at the operation that overflowed, and not in an operand that is never evaluated: of sizeof, _Alignof or typeof, the controlling expression of _Generic, or an arm a constant `?:`, `&&` or `||` condition discards. The constant walk now notes a signed operation that overflows (eval_noting_overflow), and the parser reports it where the operation is built. An array size whose computation overflows is no integer constant expression to gcc, so it is now a VLA here too: refused at file scope. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cc does
`int x __attribute__((mode(XF)));` was accepted and the declaration
silently became a `long double`. gcc 13 refuses every mismatch, and so
does c17 now, in gcc's words and order:
unknown machine mode 'FOO' no mode of the target (`XF` on aarch64)
unable to emulate 'BLK' condition-code, block, one-bit,
fixed-point, over-wide integer modes
invalid pointer mode 'SI' a pointer at other than its width
cannot use mode 'SF' for enumerated types
mode 'SF' applied to inappropriate type
a mode of another class: floating on
an integer type, integer on a floating
one, anything on _Bool or an aggregate
`mode` with no argument is "wrong number of arguments specified for
'mode' attribute", one that is not a name is ignored with gcc's warning,
and only a `__` on both sides is stripped, so `_SI_` names no mode. A
pointer declared at the pointer width (`int *p __attribute__((mode(DI)))`)
now stays a pointer instead of becoming a `long`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n as gcc does
`static struct S { int a; };`, `const struct S {...};`, `_Alignas(8)
struct S {...};`, `_Thread_local`/`__thread`, `extern`, `typedef`,
`volatile` and `_Atomic` on a tag declaration with no declarator were
silent: check_declares_something returned as soon as it saw a struct,
union or enum type, before looking at any specifier. `register` or
`auto` at file scope, `inline`, `_Noreturn` and `restrict` there were
accepted too.
The check now follows gcc 13: an error for `inline`, `_Noreturn`,
file-scope `auto`/`register` and `restrict` ("invalid use of
'restrict'"); for a reference to a tag already declared, the pedwarn
"empty declaration with storage class specifier|type qualifier|'_Alignas'
does not redeclare tag"; otherwise the warning "useless storage class
specifier", "useless '_Thread_local'" (or '__thread', as spelled),
"useless type qualifier" or "useless '_Alignas'" in empty declaration.
A typedef name for a struct type is a type name, not a tag specifier:
`static T;` is "useless type name in empty declaration", and an untagged
struct reached through a typedef no longer says "unnamed struct/union".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
318b2fe refuses XF and XC as unknown modes on aarch64, as gcc does, but diagnostics_implemented_modes_are_silent compiles for the host and listed them unconditionally, so it failed on an aarch64 host (linux-aarch64 CI). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Plain -Werror made every -f option c17 did not know an error, including ones gcc accepts and real builds pass: -fstack-clash-protection (Debian amd64 default flags), -fdiagnostics-color=always (meson's default), -flto=auto and -ffat-lto-objects (Ubuntu's defaults). cc/f_options.rs classifies every -f name c17 accepts, sourced from distro build flags, cmake and meson defaults, the build files of several real projects, c17's own tests and the torture suite's dg-options, each checked against gcc 13: - implemented: what the driver parses; - accepted, silent: options whose effect c17 already has or that change nothing observable (optimisation passes, diagnostics presentation, LTO, CFI always emitted, frame pointer always kept, no TBAA, no signed-overflow folding, -fno-common), each with its reason; - unsupported: gcc would do something observable that c17 does not (-fstack-protector*, -fexceptions, -ftrapv, -fpack-struct, -fopenmp, -fsanitize=*, ...): "c17: warning: '-fX' is not supported; ignored", in the group c17-unsupported-option, which -w and -Wno- silence and which plain -Werror does not promote (-Werror=c17-unsupported-option does); - unknown: gcc's "unrecognized command-line option" error, as gcc gives it. Bad values get gcc's wording. -fstack-clash-protection is warned per function, at its line, only where gcc would emit probes (a frame step of 4 KiB or more on x86-64, 63 KiB on aarch64, or alloca/VLA); every other function already meets it. -fstack-protector* used to be ignored silently though c17 emits no canary. -fuse-ld= is passed to the link. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… revisions
-std=gnu99 or -std=c11 printed "'-std=X' ignored; c17 compiles C17 only",
which gcc does not, so a configure probe under AC_LANG_WERROR concluded the
flag failed. A C99 or C11 program is a C17 program, so those spellings
(c9x, c99, c1x, c11, the gnu* forms, iso9899:199x/1999/2011) now pass
without comment, like the C17 ones. C90 (c89, c90, gnu89, gnu90,
iso9899:1990/199409, -ansi) keeps the warning: C17 refuses its implicit int
and implicit declarations. A revision after C17 (c2x, c23, c2y, gnu2x,
gnu23, gnu2y, iso9899:2024) stays an error, so a probe for C23 support is
told no. StdRequest::{C17, Older} becomes {Compiled, Ignored}.
NONPOSIX.md said __STDC_VERSION__ was 201112L; it is 201710L.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
The change set spans compiler overflow semantics, PIC/TLS codegen policy, and diagnostic behavior across the front end and backends, which warrants final human review despite the extensive passing tests.
Review effort: Balanced
Findings: None
What changed in this PR
This PR advances the cc crate (the in-tree C17 compiler) toward closer gcc‑13 behavioral parity across its front-end diagnostics and some codegen policy. It reworks preprocessor #if integer arithmetic to report "integer overflow in preprocessor expression" exactly where gcc's default pedwarn does (add/sub/mul/div, unary negation, and bit-losing signed left shifts) while giving out-of-range shifts gcc's actual values. It also refactors position-independence handling into a PositionIndependence/PicLevel enum, adds gcc-style type spellings for conversion diagnostics, and tightens the lexer's treatment of unterminated literals/comments and empty \x escapes (deferring the error to the literal decoder).
Changes:
- Preprocessor
#ifnow computes signed results exactly in a 128-bit carrier and flagsintmax_toverflow via a new default pedwarn helper; shifts use a dedicatedshift()matching gcc's values. - New
TypeTable::gcc_type_name/gcc_bitfield_type_nameproduce gcc's full integer spellings (e.g.short unsigned int,signed char:3) for conversion warnings. - Documentation (
NONPOSIX.md) updated for the-fpicfamily,-ftls-model=,-std=/C17 dialect, and-f*option classification; extensive new tests for directives, overflow, shifts, unterminated literals, and system-header diagnostics.
| File | Description |
|---|---|
| NONPOSIX.md | Documents -fpic family, -std=/C17 dialect, and -Wc17-unsupported-option option classification; consistent with f_options.rs. |
| cc/types.rs | Adds gcc_type_name/gcc_bitfield_type_name for gcc-style integer/bit-field spellings in diagnostics. |
| cc/token/preprocess.rs | Adds checked/shift/overflow helpers and rewires #if add/sub/mul/div/shift/neg to gcc-matching overflow pedwarns and shift values. |
| cc/token/test_preprocess.rs | New tests for invalid directives, linemarkers, #if overflow, shift values, and system-header stream classification; PIC-macro test updated to the new enum. |
| cc/token/test_literal.rs | New test that empty \x escapes error at decode time in strings/char/#if. |
| cc/token/test_lexer.rs | Updated comments/tests: unterminated comment is an error at its open; empty \x left to the decoder; unterminated literals marked and spelled as written. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…arwin option on macOS Every c17 executable link on macOS led with -no-pie, and Apple's clang answers it with "argument unused during compilation: '-no-pie'" -- a Mach-O executable on arm64 is always position independent and the driver takes neither -pie nor -no-pie. It had done so since link_mode_flag was added; the first test to require an empty stderr from a macOS link (linker_input_unknown_suffix_is_linked) caught it. link_mode_flag now answers Option, None for any macOS executable; -shared is passed everywhere, and a -static, -static-pie, -pie or -no-pie the user wrote still reaches the driver among the other linker flags. Linux and FreeBSD links are unchanged. linker_options_are_untouched passed -Wl,-z,now, which Apple ld rejects (it has no -z); on macOS it now uses -Wl,-dead_strip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No description provided.