mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
144 lines
5.5 KiB
ReStructuredText
144 lines
5.5 KiB
ReStructuredText
:orphan:
|
|
|
|
.. @raise litre.TestsAreMissing
|
|
|
|
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
|
|
---------------
|
|
|
|
::
|
|
|
|
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 StringLiteralConvertible, 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 StringLiteralConvertible Protocol
|
|
-------------------------------------
|
|
|
|
Here is the StringLiteralConvertible protocol as defined in the standard
|
|
library's CompilerProtocols.swift::
|
|
|
|
// NOTE: the compiler has builtin knowledge of this protocol
|
|
// Conforming types can be initialized with arbitrary string literals.
|
|
public protocol StringLiteralConvertible
|
|
: ExtendedGraphemeClusterLiteralConvertible {
|
|
|
|
typealias StringLiteralType : _BuiltinStringLiteralConvertible
|
|
// Create an instance initialized to `value`.
|
|
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 StringLiteralConvertible 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 _BuiltinStringLiteralConvertible Protocol
|
|
---------------------------------------------
|
|
|
|
CompilerProtocols.swift contains a second protocol::
|
|
|
|
// NOTE: the compiler has builtin knowledge of this protocol
|
|
public protocol _BuiltinStringLiteralConvertible
|
|
: _BuiltinExtendedGraphemeClusterLiteralConvertible {
|
|
|
|
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 _BuiltinStringLiteralConvertible is constructed from
|
|
the raw string data. This will be a standard library type.
|
|
3. Some type conforming to StringLiteralConvertible 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
|
|
StringLiteralConvertible.
|
|
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 StringLiteralConvertible 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 try to individually convert
|
|
each element of the interpolation to the type that adopts
|
|
StringInterpolationConvertible, then calls the variadic
|
|
``convertFromStringInterpolation`` to put them all together. The default type
|
|
for an interpolated literal without context is also ``StringLiteralType``.
|