SE-0550: `@noSanitize` attribute for functions

Hello, Swift community!

The review of SE-0550: @noSanitize attribute for functions begins now and runs through September 30th, 2026.

Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to me as the review manager by DM. When contacting the review manager directly, please put "SE-0550" in the subject line.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  • What is your evaluation of the proposal?
  • Is the problem being addressed significant enough to warrant a change to Swift?
  • Does this proposal fit well with the feel and direction of Swift?
  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available in the Swift evolution repository.

With thanks in advance for your consideration,

Tony Allevato
Review Manager

1 Like

noSanitize doesn’t quite feel like the right grammatical placement for How about one of these alternatives? They feel a little more inline with how other attributes like @specialized and @inlinable sound to me.

@disableSanitizers(address, thread)

@sanitizer(thread, disabled)
@sanitizer(address, disabled)

@sanitization(disabled, for: thread, address)

Separately, I’m not quite sure of the abbreviation memtag. Have you considered expanding that?

8 Likes

Of the suggested options, I like this one. My preference is for @noSanitize, however, since it is shorter and closest to the no_sanitize spelling used by Clang and Rust.

Hmm. In clang, there are three different memtag-* options, and then memtag itself implies all three. Swift only supports one of the three, memtag-stack. There is only one LLVM-IR attribute, however, which is called SanitizeMemtag -- which is added to functions when memtag-stack is passed to the driver.

Because the name of the Swift driver option is memtag-stack, I think we should probably use @noSanitize(memtagStack) . This would also allow us to maintain exactly one @noSanitize kind per -sanitize driver option, which seems like it could be desirable.

I agree with @j-f1 that the proposed @noSanitize name does not fit well with the naming of other Swift attributes.

I realize that the naming directly mirrors the attribute name in LLVM. I do not know if there is a general rule of thumb that LLVM names should be treated as a term of art and used as-is.

Of the suggested naming, I would prefer a singular spelling of @disableSanitizer(address, thread).

Since very often evolution proposals are used after the fact as documentation/reference, it would probably be useful to include a code example of using this attribute in one of its main use cases, showing conditionally adding the attribute for Embedded Swift.

I also think adding that bit of example code to the proposal helps reduce the risk that developers will add this attribute for platforms where it is not needed, removing useful profiling unnecessarily.

Something like:

#if hasFeature(Embedded)
@disableSanitizer(address)
#endif
func readsMMIO(_ p: UnsafePointer<UInt32>) -> UInt32 { p.pointee }

Overall though, this seems like a useful proposal.

4 Likes

The attribute seems to be more general than sanitizers already, and I wonder if we shouldn’t bow to that. The only problem is that the more general term is “instrumentation”, which is quite a mouthful even before you start negating anything. But it would be a better name for controlling instrumentation function-by-function in some way other than just disabling it.

I don’t find the precedent of Clang using an attribute name to be very important.

3 Likes

Perhaps @instrument() which can read a verb.

For grammatical comparison, @specialized shipped in 6.3.

1 Like

FWIW, the 'Swift'-ization most apt here is "noescape" → "nonescaping."
By that analogy, this would be @nonsanitizing.

4 Likes

Would it make sense to have an @instrumentation attribute which takes an explicit list of instrumentation types to enable: or disable:?

For the existing supported sanitizers, it would probably make sense to only support explicitly disabling them for now (they should only be enabled at the driver-level):

@instrumentation(disable: [asan, tsan, memtagStack])

But perhaps other types of instrumentation would want to re-use this attribute as a per-function enablement mechanism (e.g. XRay).

@instrumentation(enable: [xray])

This does seem to open the question of how a function with enabled instrumentation would interact with inlining, but perhaps we can punt that decision until the first instrumentation type is added that wishes to use per-function enablement.

I find this odd, only because the verb "escape" is used to describe a specific behavior of a function (i.e. a closure argument either escapes a function or it doesn't), whereas "sanitize" describes a behavior of the compiler (i.e. a compiler can choose to sanitize a function, or not) and doesn't say anything about the function at all. To say a function is "nonsanitizing" sounds like it would be a statement about the function's behavior, but it's really just a directive to the compiler.

I think @instrumented would be the analogue? If we wanted a single attribute for enabling/disabling instrumentation, perhaps we could do something like @instrumented(with: [], without: [])but I'm not sure that's Swift-like at all.

3 Likes

Hey folks,

The Language Steering Group has discussed this proposal. We are supportive of adding this attribute to Swift, but have requested some modifications:

  • We felt, as did many community members who reviewed the proposal, that the spelling @noSanitize was not the right fit for Swift. Our preference among the spellings suggested in the review thread was @instrumentation(disable: names...). Likewise, the compiler condition has been renamed to #if instrumentation(sanitizerName).

  • To ensure that the names of sanitizers passed to this attribute are consistent and predictable, we would like to apply an algorithmic transformation based on the names of the sanitizers as they are passed on the command line. The name used in an attribute is the lowerCamelCase form of that flag value: address, thread, and memtagStack. (coverage remains unchanged as it is not controlled by the same flag set.)

  • For consistency with SE-0537 (function sections), we believe that the effects of the @instrumentation flag should propagate to nested closures. This means that there should be an escape hatch to return to the original behavior if needed. For that, we can again follow SE-0537's precedent and spell this as @instrumentation(default).

The author has made these changes to the proposal (view the diff).

We would like to give the community another chance to comment on these changes, so we are extending the review for one week (until October 8th).

Thank you!
—Tony Allevato, review manager

5 Likes

Does the new nesting behavior also apply to nested function declarations, or just closures?

@instrumentation(disable: address)
func outer() {
    func inner() {
      // Is inner instrumented?
    }
}

There are also rarely used patterns using nested types like the following which have inconsistently handled attributes in the past.

@instrumentation(disable: address)
func outer() {
  struct Nested {
    func inner() {
      // Is inner instrumented?
    }
  }
}

Since the intent is to mirror the behavior of @section in SE-0537, we should ensure that it applies in the same scenarios as described here, where it is "inferred on accessors, closures, and local functions".

3 Likes

Hi all,

I'm concerned that the interaction between the @noSanitize attribute and the #if sanitized compile-time conditional. Consider this code:

@noSanitize(address)
func f() {
#if sanitized(address)
#error("shouldn't get here")
#endif
}

Does this produce an error when building the module with Address Sanitizer? I would like the answer to be "no", because the code in f never has Address Sanitizer enabled due to @noSanitize(address).

However, that's not really what falls out of the implementation. The #if evaluation only understands module-scope information, so it's going to produce an error. That seems surprising and incorrect.

Availability has a model that might make more sense here. The @available attribute provides availability, and within that declaration that availability holds. We have the if #available(...) formulation that is correctly context-sensitive.

I'm inclined to say that we shouldn't have the #if sanitized(...) capability at all if it can't be context-sensitive. The justification for having #if sanitized(...) seems to be entirely about inlining, so perhaps we won't need it if we address inlining some other way.

Doug

3 Likes

I generally agree. One difficulty, though, is that #if can be used in positions that if # cannot, some of which have dependency problems for resolving the contextually-correct answer — in particular, attribute lists. There might be a reasonable solution to that, though.