The compiler never validates annotation names. An annotation it does not recognize is silently accepted and does nothing — including a typo of one that matters for correctness.
Repro
@totally_bogus_annotation
@debbug // typo for @debug
fun helper(): Int { 42 }
Compiles with no error and no warning.
Why
The parser takes @ plus any non-type identifier and stores annotations as raw strings in an SSet (SkipParser.sk:886-901; the comment at 880-885 notes "we continue to store annos as simple strings"). Every consumer then does an exact string match via annotations.contains(name) (annotations.sk:20, 79-81). There is no whitelist and no "unknown annotation" diagnostic anywhere.
Why it matters
Several annotations are load-bearing for correctness, and a typo silently removes the effect with no signal:
Both fail open, and neither produces a diagnostic.
The tension
Unknown annotations cannot simply be rejected: the openness is load-bearing. The compiler indexes every function by the annotations it carries, and #forEachFunction (@foo, #f, #name) enumerates them (skipNaming.sk:3806-3859, funsByAnnotationDir). That is how a library defines its own annotation with no compiler support — sktest's @test is exactly this (skiplang/sktest/src/harness.sk:36) and is unknown to the compiler.
So a plain whitelist would break user-defined annotations.
Possible directions
- Warn on an annotation that is neither compiler-known nor referenced by any
#forEachFunction in the compilation — this keeps the extension point working while catching typos, since a user-defined annotation nobody enumerates is dead anyway.
- Or: warn only on names within a small edit distance of a compiler-known annotation, reusing the existing
SkipDidYouMean machinery (which already backs @synonym).
- At minimum, validate the annotations the compiler itself defines.
Documented as a caveat for now in #1340.
— Claude Opus 4.8 (1M context)
The compiler never validates annotation names. An annotation it does not recognize is silently accepted and does nothing — including a typo of one that matters for correctness.
Repro
Compiles with no error and no warning.
Why
The parser takes
@plus any non-type identifier and stores annotations as raw strings in anSSet(SkipParser.sk:886-901; the comment at 880-885 notes "we continue to store annos as simple strings"). Every consumer then does an exact string match viaannotations.contains(name)(annotations.sk:20, 79-81). There is no whitelist and no "unknown annotation" diagnostic anywhere.Why it matters
Several annotations are load-bearing for correctness, and a typo silently removes the effect with no signal:
@debbuginstead of@debugsilently restores CSE — which is exactly the miscompilation Mark side-effecting stdlib native funs @debug so they aren't CSE'd #1337 fixed (two identical calls to a side-effecting function merged into one), and SKStore.genSym can hand out duplicate IDs: CSE merges identical calls #1342.@may_allocleaves the function at the unsafe default (assumed to allocate nothing) — see @may_alloc/@no_throw/@no_return: unsafe default, and silently dead on functions with a body #1346.Both fail open, and neither produces a diagnostic.
The tension
Unknown annotations cannot simply be rejected: the openness is load-bearing. The compiler indexes every function by the annotations it carries, and
#forEachFunction (@foo, #f, #name)enumerates them (skipNaming.sk:3806-3859,funsByAnnotationDir). That is how a library defines its own annotation with no compiler support — sktest's@testis exactly this (skiplang/sktest/src/harness.sk:36) and is unknown to the compiler.So a plain whitelist would break user-defined annotations.
Possible directions
#forEachFunctionin the compilation — this keeps the extension point working while catching typos, since a user-defined annotation nobody enumerates is dead anyway.SkipDidYouMeanmachinery (which already backs@synonym).Documented as a caveat for now in #1340.
— Claude Opus 4.8 (1M context)