Pitch: Introduce custom attributes

Thanks everybody for the feedback. Here's a second version of the pitch.

I've come to agree with the sentiment that including custom attributes that are available at runtime might be a bit much. Therefore, I'll try to make a first proposal only for compile-time attribtues. I don't really understand the steps in the Swift compiler, but it seems to me that attributes should at least be available in libSyntax and SourceKit so that tools can officially deal with them. Any clarifications here are appreciated.

(As an aside, I've never done this before. Would it be better if I hosted the pitch in github or in a gist somewhere as it evolves?)

Introduce custom attributes

Introduction

Swift currently supports marking declarations with attributes, such as @objc and @deprecated. The compiler uses these attributes to change the way it interprets the corresponding code. However, there is no support for declaring and using new attributes to suit the needs of a project or library. This proposal aims to change that.

Previous discussions on the subject:

Motivation

Adding custom attributes to Swift would enable programmers to explore and use new methods of meta-programming. For instance, someone writing a linter might allow users to mark declarations that the linter should ignore, while someone writing a JSON parser might let users mark their properties directly with the right coding keys. This proposal would allow for such use cases and many others that haven't yet been thought of.

Proposed solution

This document proposes adding the following to the language and the compiler:

  • A syntax for declaring new attributes, including:
    • A list of the types of declarations on which they can be used.
    • Associated values and default values (optionally).
  • The ability to use these new attributes to annotate declarations.

It deliberately does not include support for inspecting the attributes in runtime (see Alternatives Considered).

Detailed design

Syntax

Custom attributes would be declared using the following syntax:

<accessLevel> attribute[(listOfSupportedDeclarations)] @<attributeName>[(<associatedValues>)]

And used as in the examples:

// Defines a public attribute called codingKey
internal attribute(.variables) @codingKey(_ key: String = "key")

// Usage
import JSONFramework

class NetworkHandler {
	@codingKey("url_path") var url: URL
}
// Defines an internal attribute called ignoreRules
internal attribute(.functions, .variables, .classes) @ignoreRules(_ rules: [String], printWarnings: Bool = false)

// Usage
import LinterAttributes

@ignoreRules(["cyclomatic_complexity", "line_count"])
func a() {
	// ...
}

Usage

The declared attributes should be made public by both libSyntax and SourceKit, allowing developers to use them to access the attribute information.

Namespacing

Name conflicts can be solved with simple namespacing when needed:

import JSONFrameworkA
import JSONFrameworkB

struct Circle {
	@JSONFrameworkA.codingKey("circle_radius") var radius: Double
	@JSONFrameworkB.codingKey("color_name") var color: Color
}

Annotating types

The Swift compiler supports annotations for types, as it enables the compiler to provide extra guarantees and features regarding those types. However, it isn't clear at the moment how custom attributes for types would be useful, and therefore it is out of the scope of this proposal.

Source compatibility

This proposal is purely additive.

Effect on ABI stability

No effect.

Effect on API resilience

No effect.

Alternatives considered

Currently available workarounds

There's been discussion before on emulating some of this behavior using protocols, an approach that's more limited but currently feasible.

Runtime support

An initial version of this proposal suggested that attributes should be made available at runtime through the reflection API. This idea was positively received in the discussions but was ultimately left for a future proposal, which makes this proposal smaller and allows time to improve the reflection APIs before runtime attributes are considered.

7 Likes