Skip to content

ImportC C23: [[...]] attribute-specifier-sequences #23224

Description

@PetarKirov

Part of #23223 (M1 — standard-independent).

Summary

Parse C23 [[...]] attribute-specifier-sequences in ImportC. Modern C headers use them
pervasively, so without this, parsing aborts on real-world input. This is the largest
M1 item but needs no C-standard-selection mechanism: [[ is not valid C11 in these
positions, so accepting it is purely additive (and matches GCC/Clang, which
accept [[...]] as an extension before -std=c23).

Spec

N3220 6.7.13 Attributes — introduction 6.7.13.1, general/syntax 6.7.13.2.
Per-attribute subclauses: 6.7.13.3 nodiscard, 6.7.13.4 maybe_unused, 6.7.13.5
deprecated, 6.7.13.6 fallthrough, 6.7.13.7 noreturn/_Noreturn, 6.7.13.8
unsequenced/reproducible. See also the C attributes reference.

Grammar (N3220 6.7.13.2; opt = optional)

attribute-specifier-sequence:
    attribute-specifier-sequence(opt) attribute-specifier
attribute-specifier:
    [ [ attribute-list ] ]
attribute-list:
    attribute(opt)
    attribute-list , attribute(opt)
attribute:
    attribute-token attribute-argument-clause(opt)
attribute-token:
    standard-attribute
    attribute-prefixed-token
standard-attribute:
    identifier
attribute-prefixed-token:
    attribute-prefix :: identifier
attribute-prefix:
    identifier
attribute-argument-clause:
    ( balanced-token-sequence(opt) )
balanced-token-sequence:
    balanced-token
    balanced-token-sequence balanced-token
balanced-token:
    ( balanced-token-sequence(opt) )
    [ balanced-token-sequence(opt) ]
    { balanced-token-sequence(opt) }
    any token other than a parenthesis, a bracket, or a brace

Constraints (6.7.13.2/2) — a standard-attribute identifier shall be one of:
deprecated, fallthrough, maybe_unused, nodiscard, noreturn, _Noreturn,
unsequenced, reproducible.

Semantics (6.7.13.2/3) relevant to the parser:

  • An attribute-specifier containing no attributes ([[]]) has no effect; order within
    an attribute-list is not significant.
  • A standard attribute attr and the form __attr__ behave identically (6.7.13.1/4) —
    normalize surrounding __ before dispatch.
  • A keyword used as an attribute-token is treated as an identifier (so [[_Noreturn]]
    is well-formed).
  • The argument clause is a balanced-token-sequence, not an expression — skip it by
    paren/bracket/brace balancing (reuse cparseParens()), reading the actual contents
    only for deprecated/nodiscard message strings.

Approach

Mirror the existing GNU parser. cparseGnuAttributes/cparseGnuAttribute (cparse.d:3618)
already feed the Specifier struct (cparse.d:4655) which applySpecifier/
specifiersToSTC (cparse.d:4683/4970) turn into D AST nodes — reuse that machinery
end to end (no new AST nodes).

Add:

  • cparseCAttributes(ref Specifier) — loop while the next two tokens are [ [
    (the lexer emits two TOK.leftBracket; use peekNext() so a lone [ for array
    subscripting is never misparsed), parse a comma-separated list (trailing comma
    allowed), consume the closing ] ].
  • cparseCAttribute(ref Specifier) — one attribute-token [ ( balanced-tokens ) ].
    Accept namespaced prefix :: name (gnu::, clang::, msvc::). Normalize standard
    attributes spelled with surrounding __ (__deprecated__ -> deprecated).

New Id entries (id.d) for the attribute names that don't exist yet
(maybe_unused, nodiscard, fallthrough, unsequenced, reproducible, and the
gnu/clang/msvc prefixes); deprecated/noreturn already exist.

Attribute -> ImportC mapping

Attribute N3220 § Handling
deprecated / deprecated("msg") 6.7.13.5 -> Specifier._deprecated / depMsg -> DeprecatedDeclaration
noreturn / _Noreturn 6.7.13.7 -> SCW.x_Noreturn
nodiscard / nodiscard("msg") 6.7.13.3 accept + ignore (no D equivalent; candidate future warning)
maybe_unused 6.7.13.4 accept + ignore
fallthrough 6.7.13.6 accept + ignore (statement attribute)
unsequenced / reproducible 6.7.13.8 accept + ignore
namespaced (gnu::..., clang::...) / unknown 6.7.13.1 scan + ignore (consume balanced ( ... ))

Every code site and test carries a // C23 6.7.13.x clause comment.

Injection points

Accept [[...]] everywhere GNU __attribute__ is already consumed — mirror each
existing call site:

  • declaration start (cparseDeclarationSpecifiers, ~cparse.d:2516) and the
    startsDeclarationSpecifiers/statement dispatch guards
  • after * in a declarator (~cparse.d:2927)
  • init-declarator suffix (~cparse.d:1892)
  • function parameters (~cparse.d:3268)
  • struct/union tags & members (~cparse.d:4037/4079/4241)
  • enum tags & enumerators (~cparse.d:3905/3953/3971/3989)
  • statement-leading [[fallthrough]]; / [[maybe_unused]] int x; (cparseStatement)

This satisfies the hard requirement that [[...]] parse anywhere __attribute__ does.

Out of scope / deferred (note here as they come up)

  • Rarer attribute positions (e.g. mid specifier-qualifier-list in sizeof(T [[...]]),
    after the declarator-id inside array brackets) — defer; call them out in this issue.
  • Mapping nodiscard to an actual D diagnostic — future enhancement.

Acceptance criteria

  • compilable test exercising each mapped/ignored attribute in each injection
    point above, each with its // C23 6.7.13.x comment.
  • fail_compilation test (compiled with -de) confirming [[deprecated]] /
    [[deprecated("msg")]] fire the deprecation message.
  • Changelog entry (changelog/dmd.importc-c23-attributes.dd).
  • No regression for existing GNU __attribute__ tests.

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions