File: Literals.md

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (159 lines) | stat: -rw-r--r-- 6,126 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
Literals
========

*What happens when a literal expression is used?*

The complicated case is for integer, floating-point, character, and string
literals, so let's look at those.


High-Level View
---------------

```swift
window.setTitle("Welcome to Xcode")
```

In this case, we have a string literal and an enclosing context. If `window`
is an NSWindow, there will only be one possible method named `setTitle`,
which takes an NSString. Therefore, we want the string literal expression to
end up being an NSString.

Fortunately, NSString implements ExpressibleByStringLiteral, so the type checker
will indeed be able to choose NSString as the type of the string literal. All
is well.

In the case of integers or floating-point literals, the value effectively has
infinite precision. Once the type has been chosen, the value is checked to see
if it is in range for that type.


The ExpressibleByStringLiteral Protocol
---------------------------------------

Here is the `ExpressibleByStringLiteral` protocol as defined in the standard
library's CompilerProtocols.swift::

```swift
/// A type that can be initialized with a string literal.
///
/// The `String` and `StaticString` types conform to the
/// `ExpressibleByStringLiteral` protocol. You can initialize a variable or
/// constant of either of these types using a string literal of any length.
///
///     let picnicGuest = "Deserving porcupine"
///
/// Conforming to ExpressibleByStringLiteral
/// ========================================
///
/// To add `ExpressibleByStringLiteral` conformance to your custom type,
/// implement the required initializer.
public protocol ExpressibleByStringLiteral
  : ExpressibleByExtendedGraphemeClusterLiteral {

  /// A type that represents a string literal.
  ///
  /// Valid types for `StringLiteralType` are `String` and `StaticString`.
  associatedtype StringLiteralType: _ExpressibleByBuiltinStringLiteral

  /// Creates an instance initialized to the given string value.
  ///
  /// - Parameter value: The value of the new instance.
  init(stringLiteral value: StringLiteralType)
}
```

Curiously, the protocol is not defined in terms of primitive types, but in
terms of any StringLiteralType that the implementer chooses. In most cases,
this will be Swift's own native String type, which means users can implement
their own ExpressibleByStringLiteral types while still dealing with a high-level
interface.

(Why is this not hardcoded? A String *must* be a valid Unicode string, but
if the string literal contains escape sequences, an invalid series of code
points could be constructed...which may be what's desired in some cases.)


The _ExpressibleByBuiltinStringLiteral Protocol
-----------------------------------------------

`CompilerProtocols.swift` contains a second protocol::

```swift
// NOTE: the compiler has builtin knowledge of this protocol
public protocol _ExpressibleByBuiltinStringLiteral
  : _ExpressibleByBuiltinExtendedGraphemeClusterLiteral {

  init(
      _builtinStringLiteral start: Builtin.RawPointer,
      utf8CodeUnitCount: Builtin.Word,
      isASCII: Builtin.Int1)
}
```

The use of builtin types makes it clear that this is *only* for use in the
standard library. This is the actual primitive function that is used to
construct types from string literals: the compiler knows how to emit raw
data from the literal, and the arguments describe that raw data.

So, the general runtime behavior is now clear:

1. The compiler generates raw string data.
2. Some type conforming to _ExpressibleByBuiltinStringLiteral is constructed from
   the raw string data. This will be a standard library type.
3. Some type conforming to ExpressibleByStringLiteral is constructed from the
   object constructed in step 2. This may be a user-defined type. This is the
   result.


The Type-Checker's Algorithm
----------------------------

In order to make this actually happen, the type-checker has to do some fancy
footwork. Remember, at this point all we have is a string literal and an
expected type; if the function were overloaded, we would have to try all the
types.

This algorithm can go forwards or backwards, since it's actually defined in
terms of constraints, but it's easiest to understand as a linear process.

1. Filter the types provided by the context to only include those that are
   ExpressibleByStringLiteral.
2. Using the associated StringLiteralType, find the appropriate
   `_convertFromBuiltinStringLiteral`.
3. Using the type from step 1, find the appropriate
   `convertFromStringLiteral`.
4. Build an expression tree with the appropriate calls.

How about cases where there is no context? ::

  var str = "abc"

Here we have nothing to go on, so instead the type checker looks for a global
type named `StringLiteralType` in the current module-scope context, and uses
that type if it is actually a ExpressibleByStringLiteral type. This both allows
different standard libraries to set different default literal types, and allows
a user to *override* the default type in their own source file.

The real story is even more complicated because of implicit conversions:
the type expected by `setTitle` might not actually be literal-convertible,
but something else that *is* literal-convertible can then implicitly convert
to the proper type. If this makes your head spin, don't worry about it.


Arrays, Dictionaries, and Interpolation
---------------------------------------

Array and dictionary literals don't have a Builtin*Convertible form. Instead,
they just always use a variadic list of elements (`T...`) in the array case
and (key, value) tuples in the dictionary case. A variadic list is always
exposed using the standard library's Array type, so there is no separate step
to jump through.

The default array literal type is always Array, and the default dictionary
literal type is always Dictionary.

String interpolations are a bit different: they create an instance of 
`T.StringInterpolation` and append each segment to it, then initialize
an instance of `T` with that instance. The default type
for an interpolated literal without context is also `StringLiteralType`.