Skip to content

Implement SE-0095: Replace protocol<P1, P2> syntax with P1 & P2 #3293

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,23 @@ Note: This is in reverse chronological order, so newer entries are added to the
Swift 3.0
---------

* [SE-0095](https://github.com/apple/swift-evolution/blob/master/proposals/0095-any-as-existential.md):
The `protocol<...>` composition construct has been removed. In its
place, an infix type operator `&` has been introduced.

```swift
let a: Foo & Bar
let b = value as? A & B & C
func foo<T : Foo & Bar>(x: T) { … }
func bar(x: Foo & Bar) { … }
typealias G = GenericStruct<Foo & Bar>
```

The empty protocol composition, the `Any` type, was previously
defined as being `protocol<>`. This has been removed from the
standard library and `Any` is now a keyword with the same behaviour.


* [SE-0099](https://github.com/apple/swift-evolution/blob/master/proposals/0099-conditionclauses.md):
Condition clauses in `if`, `guard`, and `while` statements now use a more
regular syntax. Each pattern or optional binding must be prefixed with `case`
Expand Down Expand Up @@ -66,6 +83,7 @@ Swift 3.0
Similarly, the new `RecoverableError` and `CustomNSError` protocols
allow additional control over the handling of the error.


* [SE-0060](https://github.com/apple/swift-evolution/blob/master/proposals/0060-defaulted-parameter-order.md):
Function parameters with default arguments must now be specified in
declaration order. A call site must always supply the arguments it provides
Expand Down
15 changes: 7 additions & 8 deletions docs/ABI.rst
Original file line number Diff line number Diff line change
Expand Up @@ -259,9 +259,9 @@ enum in declaration order.
Existential Container Layout
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Values of protocol type, protocol composition type, or "any" type
(``protocol<>``) are laid out using **existential containers** (so-called
because these types are "existential types" in type theory).
Values of protocol type, protocol composition type, or ``Any`` type are laid
out using **existential containers** (so-called because these types are
"existential types" in type theory).

Opaque Existential Containers
`````````````````````````````
Expand Down Expand Up @@ -346,8 +346,7 @@ All metadata records share a common header, with the following fields:
* `Tuple metadata`_ has a kind of **9**.
* `Function metadata`_ has a kind of **10**.
* `Protocol metadata`_ has a kind of **12**. This is used for
protocol types, for protocol compositions, and for the "any" type
``protocol<>``.
protocol types, for protocol compositions, and for the ``Any`` type.
* `Metatype metadata`_ has a kind of **13**.
* `Class metadata`_, instead of a kind, has an *isa pointer* in its kind slot,
pointing to the class's metaclass record. This isa pointer is guaranteed
Expand Down Expand Up @@ -462,9 +461,9 @@ contain the following fields:
`protocol descriptor`_ records, but are pre-calculated for convenience.

- The **number of protocols** that make up the protocol composition is stored at
**offset 2**. For the "any" types ``protocol<>`` or ``protocol<class>``, this
**offset 2**. For the "any" types ``Any`` or ``Any : class``, this
is zero. For a single-protocol type ``P``, this is one. For a protocol
composition type ``protocol<P, Q, ...>``, this is the number of protocols.
composition type ``P & Q & ...``, this is the number of protocols.

- The **protocol descriptor vector** begins at **offset 3**. This is an inline
array of pointers to the `protocol descriptor`_ for every protocol in the
Expand Down Expand Up @@ -564,7 +563,7 @@ for ``T``, ``U``, and ``V`` in succession, as if laid out in a C struct::
};

If we add protocol requirements to the parameters, for example,
``<T: Runcible, U: protocol<Fungible, Ansible>, V>``, then the type's generic
``<T: Runcible, U: Fungible & Ansible, V>``, then the type's generic
parameter vector contains witness tables for those protocols, as if laid out::

struct GenericParameterVector {
Expand Down
15 changes: 6 additions & 9 deletions docs/Generics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -473,20 +473,17 @@ create a new protocol aggregating those protocols::

However, this only makes sense when the resulting protocol is a useful
abstraction. A SerializableDocument may or may not be a useful abstraction. When
it is not useful, one can instead use protocol<> types to compose different
protocols, e.g.,::
it is not useful, one can instead use '&' types to compose different protocols, e.g.,::

var doc : protocol<Document, Serializable>
var doc : Document & Serializable

Here, doc has an existential type that is known to conform to both the Document
and Serializable protocols. This gives rise to a natural "top" type, such that
every type in the language is a subtype of "top". Java has java.lang.Object, C#
has object, Objective-C has "id" (although "id" is weird, because it is also
convertible to everything; it's best not to use it as a model). In Swift, the
"top" type is simply an empty protocol composition::
"top" type is simply an empty protocol composition: ``Any``::

typealias Any = protocol<>

var value : Any = 17 // an any can hold an integer
value = "hello" // or a String
value = (42, "hello", Red) // or anything else
Expand Down Expand Up @@ -858,9 +855,9 @@ is effectively parsed as::

by splitting the '>>' operator token into two '>' operator tokens.

However, this is manageable, and is already implemented for protocol composition
(protocol<>). The larger problem occurs at expression context, where the parser
cannot disambiguate the tokens::
However, this is manageable, and is already implemented for the (now depreacted)
protocol composition syntax (protocol<>). The larger problem occurs at expression
context, where the parser cannot disambiguate the tokens::

Matrix<Double>(10, 10)

Expand Down
2 changes: 1 addition & 1 deletion docs/TypeChecker.rst
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ types formed by the conversion relationship, e.g., there is an edge
``A -> B`` in the latter if ``A`` is convertible to ``B``. ``B`` would
therefore be higher in the lattice than ``A``, and the topmost element
of the lattice is the element to which all types can be converted,
``protocol<>`` (often called "top").
``Any`` (often called "top").

The concrete types "above" and "below" a given type variable provide
bounds on the possible concrete types that can be assigned to that
Expand Down
23 changes: 10 additions & 13 deletions docs/archive/LangRef.html
Original file line number Diff line number Diff line change
Expand Up @@ -1324,16 +1324,14 @@ <h3 id="type-optional">Optional Types</h3>
<!-- _____________________________________________________________________ -->
<h3 id="type-composition">Protocol Composition Types</h3>
<pre class="grammar">
type-composition ::= 'protocol' '&lt;' type-composition-list? '&gt;'

type-composition-list ::= <a href="#type-identifier">type-identifier</a> (',' <a href="#type-identifier">type-identifier</a>)*
type-composition ::= <a href="#type-identifier">type-identifier</a> ('&amp;' <a href="#type-identifier">type-identifier</a>)*
</pre>

<p>A protocol composition type composes together a number of
protocols to describe a type that meets the requirements of each of
those protocols. A protocol composition type <code>protocol&lt;A,
B&gt;</code> is similar to an explicitly-defined protocol that
inherits both <code>A</code> and <code>B</code></p>
those protocols. A protocol composition type <code>A &amp; B</code>
is similar to an explicitly-defined protocol that inherits both
<code>A</code> and <code>B</code></p>

<pre class="example">
protocol C : A, B { }
Expand All @@ -1348,18 +1346,17 @@ <h3 id="type-composition">Protocol Composition Types</h3>
conform to that name.
</div>

<p>Each of the types named in the
<code>type-composition-list</code> shall refer to either a protocol
or to a protocol composition. The list may be empty, in which case
every type conforms to the empty protocol composition. This is how
the <code>Any</code> type is defined in the standard library.</p>
<p>Each of the types named in the <code>type-composition</code> shall
refer to either a protocol or to a protocol composition. The empty
protocol composition is the keyword <code>Any</code> and every
type conforms to it.

<pre class="example">
<i>// A value that represents any type</i>
var any : protocol&lt;&gt; = 17
var any : Any = 17

<i>// A value that conforms to both the Document and Enumerator protocols</i>
var doc : protocol&lt;Document,Enumerator&gt;
var doc : Document &amp; Enumerator
doc.isEmpty() <i>// uses Enumerator.isEmpty()</i>
doc.title = "Hello" <i>// uses Document.title</i>
</pre>
Expand Down
6 changes: 2 additions & 4 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -443,9 +443,6 @@ class ASTContext {
/// Retrieve the declaration of Swift.Void.
TypeAliasDecl *getVoidDecl() const;

/// Retrieve the declaration of Swift.Any.
TypeAliasDecl *getAnyDecl() const;

/// Retrieve the declaration of ObjectiveC.ObjCBool.
StructDecl *getObjCBoolDecl();

Expand Down Expand Up @@ -568,7 +565,8 @@ class ASTContext {
// Builtin type and simple types that are used frequently.
const CanType TheErrorType; /// This is the ErrorType singleton.
const CanType TheUnresolvedType; /// This is the UnresolvedType singleton.
const CanType TheEmptyTupleType; /// This is "()", aka Void
const CanType TheEmptyTupleType; /// This is '()', aka Void
const CanType TheAnyType; /// This is 'Any', the empty protocol composition
const CanType TheNativeObjectType; /// Builtin.NativeObject
const CanType TheBridgeObjectType; /// Builtin.BridgeObject
const CanType TheUnknownObjectType; /// Builtin.UnknownObject
Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,11 @@ ERROR(expected_rangle_protocol,PointsToFirstBadToken,
ERROR(disallowed_protocol_composition,PointsToFirstBadToken,
"protocol composition is neither allowed nor needed here", ())

WARNING(deprecated_protocol_composition,none,
"'protocol<...>' composition syntax is deprecated; join the protocols using '&'", ())
WARNING(deprecated_any_composition,none,
"'protocol<>' syntax is deprecated; use 'Any' instead", ())

//------------------------------------------------------------------------------
// Pattern parsing diagnostics
//------------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1378,7 +1378,7 @@ ERROR(circular_protocol_def,none,
NOTE(protocol_here,none,
"protocol %0 declared here", (Identifier))
ERROR(protocol_composition_not_protocol,none,
"non-protocol type %0 cannot be used within 'protocol<...>'", (Type))
"non-protocol type %0 cannot be used within a protocol composition", (Type))
ERROR(objc_protocol_inherits_non_objc_protocol,none,
"@objc protocol %0 cannot refine non-@objc protocol %1", (Type, Type))

Expand Down Expand Up @@ -2674,7 +2674,7 @@ ERROR(objc_convention_invalid,none,
ERROR(function_type_no_parens,none,
"single argument function types require parentheses", ())
NOTE(not_objc_empty_protocol_composition,none,
"'protocol<>' is not considered '@objc'; use 'AnyObject' instead", ())
"'Any' is not considered '@objc'; use 'AnyObject' instead", ())
NOTE(not_objc_protocol,none,
"protocol %0 is not '@objc'", (Type))
NOTE(not_objc_empty_tuple,none,
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/KnownIdentifiers.def
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
IDENTIFIER(alloc)
IDENTIFIER(allocWithZone)
IDENTIFIER(allZeros)
IDENTIFIER(Any)
IDENTIFIER(atIndexedSubscript)
IDENTIFIER_(bridgeToObjectiveC)
IDENTIFIER_WITH_NAME(code_, "_code")
Expand Down
32 changes: 19 additions & 13 deletions include/swift/AST/TypeRepr.h
Original file line number Diff line number Diff line change
Expand Up @@ -613,38 +613,44 @@ class NamedTypeRepr : public TypeRepr {

/// \brief A protocol composite type.
/// \code
/// protocol<Foo, Bar>
/// Foo & Bar
/// \endcode
class ProtocolCompositionTypeRepr : public TypeRepr {
ArrayRef<IdentTypeRepr *> Protocols;
SourceLoc ProtocolLoc;
SourceRange AngleBrackets;
SourceLoc FirstTypeLoc;
SourceRange CompositionRange;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these redundant? I suggest keeping the locations of each & instead. The range of the composition is just the start of the first protocol to the end of the last protocol, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, in particular I think it makes sense for diagnostics to point to the & rather than the first type, like they do for binary operator expressions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is indeed redundant — I have kept both so the old syntax uses the FirstTypeLoc as the protocol loc, and the range as the angle bracket range, for example for this fixit. I would hope to change this once we remove support for the old synax and do not need this fixit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I think we should update the result of getSourceLoc, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jrose-apple I’m sorry, I’m not sure what you mean

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I meant getLocImpl, sorry. I think getLocImpl should point to the (first) & rather than the first protocol.


public:
ProtocolCompositionTypeRepr(ArrayRef<IdentTypeRepr *> Protocols,
SourceLoc ProtocolLoc,
SourceRange AngleBrackets)
SourceLoc FirstTypeLoc,
SourceRange CompositionRange)
: TypeRepr(TypeReprKind::ProtocolComposition), Protocols(Protocols),
ProtocolLoc(ProtocolLoc), AngleBrackets(AngleBrackets) {
FirstTypeLoc(FirstTypeLoc), CompositionRange(CompositionRange) {
}

ArrayRef<IdentTypeRepr *> getProtocols() const { return Protocols; }
SourceLoc getProtocolLoc() const { return ProtocolLoc; }
SourceRange getAngleBrackets() const { return AngleBrackets; }
SourceLoc getSourceLoc() const { return FirstTypeLoc; }
SourceRange getCompositionRange() const { return CompositionRange; }

static ProtocolCompositionTypeRepr *create(ASTContext &C,
ArrayRef<IdentTypeRepr*> Protocols,
SourceLoc ProtocolLoc,
SourceRange AngleBrackets);

SourceLoc FirstTypeLoc,
SourceRange CompositionRange);

static ProtocolCompositionTypeRepr *createEmptyComposition(ASTContext &C,
SourceLoc AnyLoc) {
return ProtocolCompositionTypeRepr::create(C, {}, AnyLoc, {AnyLoc, AnyLoc});
}

static bool classof(const TypeRepr *T) {
return T->getKind() == TypeReprKind::ProtocolComposition;
}
static bool classof(const ProtocolCompositionTypeRepr *T) { return true; }

private:
SourceLoc getStartLocImpl() const { return ProtocolLoc; }
SourceLoc getEndLocImpl() const { return AngleBrackets.End; }
SourceLoc getStartLocImpl() const { return FirstTypeLoc; }
SourceLoc getLocImpl() const { return CompositionRange.Start; }
SourceLoc getEndLocImpl() const { return CompositionRange.End; }
void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const;
friend class TypeRepr;
};
Expand Down
2 changes: 1 addition & 1 deletion include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -3476,7 +3476,7 @@ END_CAN_TYPE_WRAPPER(ProtocolType, NominalType)
/// \code
/// protocol P { /* ... */ }
/// protocol Q { /* ... */ }
/// var x : protocol<P, Q>
/// var x : P & Q
/// \endcode
///
/// Here, the type of x is a composition of the protocols 'P' and 'Q'.
Expand Down
12 changes: 7 additions & 5 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -414,8 +414,7 @@ class Parser {
}

SourceLoc consumeIdentifier(Identifier *Result = nullptr) {
assert(Tok.is(tok::identifier) || Tok.is(tok::kw_self) ||
Tok.is(tok::kw_Self) || Tok.is(tok::kw_throws));
assert(Tok.isAny(tok::identifier, tok::kw_self, tok::kw_Self, tok::kw_throws));
if (Result)
*Result = Context.getIdentifier(Tok.getText());
return consumeToken();
Expand Down Expand Up @@ -877,9 +876,11 @@ class Parser {
bool parseGenericArguments(SmallVectorImpl<TypeRepr*> &Args,
SourceLoc &LAngleLoc,
SourceLoc &RAngleLoc);
ParserResult<IdentTypeRepr> parseTypeIdentifier();

ParserResult<ProtocolCompositionTypeRepr> parseTypeComposition();
ParserResult<TypeRepr> parseTypeIdentifier();
ParserResult<TypeRepr> parseTypeIdentifierOrTypeComposition();
ParserResult<ProtocolCompositionTypeRepr> parseAnyType();

ParserResult<TupleTypeRepr> parseTypeTupleBody();
ParserResult<TypeRepr> parseTypeArray(TypeRepr *Base);

Expand Down Expand Up @@ -1082,7 +1083,8 @@ class Parser {

bool canParseType();
bool canParseTypeIdentifier();
bool canParseTypeComposition();
bool canParseTypeIdentifierOrTypeComposition();
bool canParseOldStyleProtocolComposition();
bool canParseTypeTupleBody();
bool canParseTypeAttribute();
bool canParseGenericArguments();
Expand Down
1 change: 1 addition & 0 deletions include/swift/Parse/Tokens.def
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ STMT_KEYWORD(catch)

// Expression keywords.
KEYWORD(as)
KEYWORD(Any)
KEYWORD(dynamicType)
KEYWORD(false)
KEYWORD(is)
Expand Down
21 changes: 1 addition & 20 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ ASTContext::ASTContext(LangOptions &langOpts, SearchPathOptions &SearchPathOpts,
TheUnresolvedType(new (*this, AllocationArena::Permanent)
UnresolvedType(*this)),
TheEmptyTupleType(TupleType::get(ArrayRef<TupleTypeElt>(), *this)),
TheAnyType(ProtocolCompositionType::get(*this, ArrayRef<Type>())),
TheNativeObjectType(new (*this, AllocationArena::Permanent)
BuiltinNativeObjectType(*this)),
TheBridgeObjectType(new (*this, AllocationArena::Permanent)
Expand Down Expand Up @@ -801,26 +802,6 @@ TypeAliasDecl *ASTContext::getVoidDecl() const {
return Impl.VoidDecl;
}


TypeAliasDecl *ASTContext::getAnyDecl() const {
if (Impl.AnyDecl) {
return Impl.AnyDecl;
}

// Go find 'Any' in the Swift module.
SmallVector<ValueDecl *, 1> results;
lookupInSwiftModule("Any", results);
for (auto result : results) {
if (auto typeAlias = dyn_cast<TypeAliasDecl>(result)) {
Impl.AnyDecl = typeAlias;
break;
}
}

return Impl.AnyDecl;
}


StructDecl *ASTContext::getObjCBoolDecl() {
if (!Impl.ObjCBoolDecl) {
SmallVector<ValueDecl *, 1> results;
Expand Down
Loading