Special forms shadow C identifiers silently, with no escape hatch #36
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
semenandfmt-c-writerdispatch on a form's head symbol. Any C function whosename matches a Sex special form is therefore unreachable — and the shadowing is
silent: the call disappears and the program still compiles.
Reproduction
emits
The call is gone,
nis read uninitialized, andccreports nothing. Thedeclaration is right there in the same file and is ignored.
Affected names
Forms dispatched by head symbol today:
case default do enum extern for if switch while— Ckeywords, so no library can declare them. Sex borrowed C's reserved words and
inherited their reservation.
castcommentdefinefnimportincludelambdapubvar, plusdot-accessc-orc-bit-or. All legal C identifiers.The failure mode varies.
commentvanishes silently, as above.(cast origin dir)emits(dir)originand dies inccwith an error pointingat generated C rather than at the Sex line.
defmacrohas the same property by design —macro?is a plist lookup, so amacro named
printfshadows the C function. That is intentional and out ofscope here.
Why now
It came up while naming a
compoundform for C99 compound literals.compoundwould be the tenth such name. Rather than bikeshed one name, the general case
deserves a decision.
Variant A — diagnose at the declaration site
When a name reaches the compiler through a declaration —
(extern fn comment ...),an imported
pub fn, or eventually anything a C header parser produces — checkit against the special-form table and refuse:
(include foo.h)andcall away. That case stays silent until headers are parsed.
Variant B — let a declaration rename the C symbol
The standard FFI escape hatch (Rust's
#[link_name], Haskell'sforeign import ccall "..."):unkebabifymust leavethe C string alone.
They compose — A says there is a problem, B lets you fix it. A alone is the cheap
half and already kills the silent-miscompile class.
Not proposed
Renaming existing forms (
var,cast,fn, …). The churn is not worth it, andthe C-keyword forms show the collision is survivable as long as the name is
reserved.
The case that prompted this — a
compoundform for C99 compound literals — wasresolved by giving it syntax instead of a name, so it never joins the list.
:inside#(...)ends a type and makes the rest a compound literal, and aleading
.names a field:Neither
:nor.fieldcan be a C identifier, so neither can be shadowed, andthe type needs no parentheses because
:is what ends it. The ambiguity thatrules out bare
#((struct point) 0 0)—(* p)is both a dereference and apointer type,
(¤ a i)both a subscript and an array type, and a bare symbolboth a variable and a typedef — does not arise, because the separator is a
syntactic delimiter rather than a type-vs-expression guess.
The general principle worth keeping: where C expresses something as syntax,
Sex should express it as syntax too. C has no
compoundkeyword; a named formwould have been the tenth shadowable name for no gain.
This does not close the issue. The other nine stand —
castcommentdefinefnimportincludelambdapubvar— and Variants A and B areunchanged. It does remove the deadline: nothing new is being added to the list
right now.