Skip to content

Commit 7b9e5de

Browse files
joewillsherDougGregor
authored andcommitted
[SE-0095] simplifyTypeExpr for composition expressions
Also adds: - Any is caught before doing an unconstrained lookup, and the protocol<> type is emitted - composition expressions can be handled by `PreCheckExpression::simplifyTypeExpr` to so you can do lookups like (P & Q).self - Fixits corrected & new tests added - Typeref lowering cases should have been optional - This fixes a failing test case.
1 parent a6dad00 commit 7b9e5de

23 files changed

+2088
-81
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@ Note: This is in reverse chronological order, so newer entries are added to the
33
Swift 3.0
44
---------
55

6+
* [SE-0095](https://github.com/apple/swift-evolution/blob/master/proposals/0095-any-as-existential.md):
7+
The `protocol<...>` composition construct has been removed. In its place, an infix type operator `&` has been introduced.
8+
9+
```swift
10+
let a: Foo & Bar
11+
let b = value as? A & B & C
12+
func foo<T : Foo & Bar>(x: T) { }
13+
func bar(x: Foo & Bar) { }
14+
typealias G = GenericStruct<Foo & Bar>
15+
```
16+
17+
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.
18+
619
* [SE-0091](https://github.com/apple/swift-evolution/blob/master/proposals/0091-improving-operators-in-protocols.md):
720
Operators can now be defined within types or extensions thereof. For example:
821

@@ -93,6 +106,7 @@ Swift 3.0
93106
Similarly, the new `RecoverableError` and `CustomNSError` protocols
94107
allow additional control over the handling of the error.
95108

109+
96110
* [SE-0060](https://github.com/apple/swift-evolution/blob/master/proposals/0060-defaulted-parameter-order.md):
97111
Function parameters with default arguments must now be specified in
98112
declaration order. A call site must always supply the arguments it provides

include/swift/AST/ASTContext.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -443,8 +443,6 @@ class ASTContext {
443443
/// Retrieve the declaration of Swift.Void.
444444
TypeAliasDecl *getVoidDecl() const;
445445

446-
TypeAliasDecl *getAnyDecl() const;
447-
448446
/// Retrieve the declaration of ObjectiveC.ObjCBool.
449447
StructDecl *getObjCBoolDecl();
450448

include/swift/AST/DiagnosticsParse.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ ERROR(disallowed_protocol_composition,PointsToFirstBadToken,
656656
"protocol composition is neither allowed nor needed here", ())
657657

658658
WARNING(deprecated_protocol_composition,PointsToFirstBadToken,
659-
"'protocol<...>' composition syntax is deprecated; use infix '&' instead", ())
659+
"'protocol<...>' composition syntax is deprecated; join the protocols using '&'", ())
660660
WARNING(deprecated_any_composition,PointsToFirstBadToken,
661661
"'protocol<>' syntax is deprecated; use 'Any' instead", ())
662662

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ ERROR(reserved_member_name,none,
517517
ERROR(invalid_redecl,none,"invalid redeclaration of %0", (DeclName))
518518
NOTE(invalid_redecl_prev,none,
519519
"%0 previously declared here", (DeclName))
520-
ERROR(invalid_redecl_any,none,"invalid redeclaration of 'Any'; cannot override type keyword", ())
520+
ERROR(invalid_redecl_any,none,"invalid redeclaration of 'Any'; cannot overload type keyword", ())
521521

522522
ERROR(ambiguous_type_base,none,
523523
"%0 is ambiguous for type lookup in this context", (Identifier))

lib/AST/ASTContext.cpp

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -802,24 +802,6 @@ TypeAliasDecl *ASTContext::getVoidDecl() const {
802802
return Impl.VoidDecl;
803803
}
804804

805-
TypeAliasDecl *ASTContext::getAnyDecl() const {
806-
if (Impl.VoidDecl) {
807-
return Impl.VoidDecl;
808-
}
809-
810-
// Go find 'Void' in the Swift module.
811-
SmallVector<ValueDecl *, 1> results;
812-
lookupInSwiftModule("Any", results);
813-
for (auto result : results) {
814-
if (auto typeAlias = dyn_cast<TypeAliasDecl>(result)) {
815-
Impl.VoidDecl = typeAlias;
816-
return typeAlias;
817-
}
818-
}
819-
820-
return Impl.VoidDecl;
821-
}
822-
823805
StructDecl *ASTContext::getObjCBoolDecl() {
824806
if (!Impl.ObjCBoolDecl) {
825807
SmallVector<ValueDecl *, 1> results;

lib/Parse/ParseDecl.cpp

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2435,41 +2435,36 @@ ParserStatus Parser::parseInheritance(SmallVectorImpl<TypeLoc> &Inherited,
24352435

24362436
auto usesDeprecatedCompositionSyntax = Tok.is(tok::kw_protocol) && startsWithLess(peekToken());
24372437

2438-
auto compositionResult = parseTypeIdentifierOrTypeComposition();
2439-
Status |= compositionResult;
2438+
auto ParsedTypeResult = parseTypeIdentifierOrTypeComposition();
2439+
Status |= ParsedTypeResult;
24402440

2441-
if (auto composition = dyn_cast_or_null<ProtocolCompositionTypeRepr>(compositionResult.getPtrOrNull())) {
2441+
// cannot inherit from composition
2442+
if (auto Composition = dyn_cast_or_null<ProtocolCompositionTypeRepr>(ParsedTypeResult.getPtrOrNull())) {
24422443
// Record the protocols inside the composition.
2443-
Inherited.append(composition->getProtocols().begin(),
2444-
composition->getProtocols().end());
2444+
Inherited.append(Composition->getProtocols().begin(),
2445+
Composition->getProtocols().end());
24452446
if (usesDeprecatedCompositionSyntax) {
24462447
// Provide fixits to remove the composition, leaving the types intact.
2447-
auto compositionRange = composition->getCompositionRange();
2448-
diagnose(composition->getStartLoc(),
2448+
auto compositionRange = Composition->getCompositionRange();
2449+
diagnose(Composition->getStartLoc(),
24492450
diag::disallowed_protocol_composition)
2450-
.highlight({composition->getStartLoc(), compositionRange.End})
2451-
.fixItRemove({composition->getStartLoc(), compositionRange.Start})
2451+
.highlight({Composition->getStartLoc(), compositionRange.End})
2452+
.fixItRemove({Composition->getStartLoc(), compositionRange.Start})
24522453
.fixItRemove(startsWithGreater(L->getTokenAt(compositionRange.End))
24532454
? compositionRange.End
24542455
: SourceLoc());
24552456
} else {
2456-
diagnose(composition->getStartLoc(),
2457+
diagnose(Composition->getStartLoc(),
24572458
diag::disallowed_protocol_composition)
2458-
.highlight(composition->getCompositionRange());
2459+
.highlight(Composition->getCompositionRange());
24592460
// TODO: Decompose 'A & B & C' list to 'A, B, C'
24602461
}
2461-
continue;
24622462
} else {
2463-
// Parse the inherited type (which must be a protocol).
2464-
ParserResult<TypeRepr> Ty = compositionResult;
2465-
Status |= Ty;
2466-
// Record the type.
2467-
if (Ty.isNonNull())
2468-
Inherited.push_back(Ty.get());
2463+
// Record the type if its a single type.
2464+
if (ParsedTypeResult.isNonNull())
2465+
Inherited.push_back(ParsedTypeResult.get());
24692466
}
24702467

2471-
2472-
24732468
// Check for a ',', which indicates that there are more protocols coming.
24742469
} while (consumeIf(tok::comma, prevComma));
24752470

lib/Parse/ParseType.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -419,8 +419,7 @@ ParserResult<TypeRepr> Parser::parseTypeIdentifierOrTypeComposition() {
419419
// Check for empty protocol composition.
420420
if (startsWithGreater(Tok)) {
421421
SourceLoc RAngleLoc = consumeStartingGreater();
422-
423-
auto AnyRange = SourceRange(LAngleLoc, RAngleLoc);
422+
auto AnyRange = SourceRange(ProtocolLoc, RAngleLoc);
424423

425424
// warn that 'protocol<>' is depreacted and offer to
426425
// repalace with the 'Any' keyword
@@ -470,10 +469,9 @@ ParserResult<TypeRepr> Parser::parseTypeIdentifierOrTypeComposition() {
470469

471470
Diag.highlight({ProtocolLoc, RAngleLoc});
472471
for (auto Comma : Commas) // remove commas and '<'
473-
Diag.fixItReplace(Comma, " & ");
474-
Diag // remove 'protocol<'
475-
.fixItRemove(ProtocolLoc)
476-
.fixItRemove(LAngleLoc)
472+
Diag.fixItReplace(Comma, " &");
473+
Diag
474+
.fixItRemove({ProtocolLoc, LAngleLoc}) // remove 'protocol<'
477475
.fixItRemove(RAngleLoc); // remove '>'
478476

479477
return makeParserResult(Status, composition);
@@ -501,7 +499,7 @@ ParserResult<TypeRepr> Parser::parseTypeIdentifierOrTypeComposition() {
501499
Protocols.push_back(Protocol.get());
502500
};
503501

504-
return makeParserResult(Status, ProtocolCompositionTypeRepr::create(Context, Protocols, FirstTypeLoc, {FirstTypeLoc, Tok.getLoc()}));
502+
return makeParserResult(Status, ProtocolCompositionTypeRepr::create(Context, Protocols, FirstTypeLoc, {FirstTypeLoc, PreviousLoc}));
505503
}
506504
}
507505

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,13 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) {
435435
DeclName Name = UDRE->getName();
436436
SourceLoc Loc = UDRE->getLoc();
437437

438+
// 'Any' is the empty protocol composition; resolve it before doing an
439+
// unqualified lookup
440+
if (Name.getBaseName() == Context.Id_Any) {
441+
auto *TypeRepr = new (Context) ProtocolCompositionTypeRepr(ArrayRef<IdentTypeRepr *>(), Loc, UDRE->getSourceRange());
442+
return new (Context) TypeExpr(TypeLoc(TypeRepr));
443+
}
444+
438445
// Perform standard value name lookup.
439446
NameLookupOptions LookupOptions = defaultUnqualifiedLookupOptions;
440447
if (isa<AbstractFunctionDecl>(DC))
@@ -1126,7 +1133,49 @@ TypeExpr *PreCheckExpression::simplifyTypeExpr(Expr *E) {
11261133
ResultTypeRepr);
11271134
return new (TC.Context) TypeExpr(TypeLoc(NewTypeRepr, Type()));
11281135
}
1129-
1136+
1137+
// Fold 'P & Q' into a composition type
1138+
if (auto *binaryExpr = dyn_cast<BinaryExpr>(E))
1139+
if (auto d = dyn_cast<OverloadedDeclRefExpr>(binaryExpr->getFn()))
1140+
for (auto decl : d->getDecls()) {
1141+
// for a composition
1142+
if (decl->getNameStr() == "&") {
1143+
// The protocols we are composing
1144+
SmallVector<IdentTypeRepr *, 4> Protocols;
1145+
1146+
auto lhsExpr = binaryExpr->getArg()->getElement(0);
1147+
if (auto *lhs = dyn_cast_or_null<TypeExpr>(lhsExpr)) {
1148+
if (auto repr = dyn_cast<IdentTypeRepr>(lhs->getTypeRepr()))
1149+
Protocols.push_back(repr);
1150+
}
1151+
// if the lhs is another binary expression, we have a multi element composition:
1152+
// 'A & B & C' is parsed as ((A & B) & C), so we get the protocols from the lhs here
1153+
else if (isa<BinaryExpr>(lhsExpr)) {
1154+
if (auto *expr = simplifyTypeExpr(lhsExpr)) {
1155+
if (auto* repr = dyn_cast<ProtocolCompositionTypeRepr>(expr->getTypeRepr()))
1156+
for (auto proto : repr->getProtocols())
1157+
Protocols.push_back(proto);
1158+
} else {
1159+
return nullptr;
1160+
}
1161+
} else {
1162+
return nullptr;
1163+
}
1164+
// add the rhs
1165+
auto rhs = dyn_cast_or_null<TypeExpr>(binaryExpr->getArg()->getElement(1));
1166+
if (!rhs) return nullptr;
1167+
1168+
if (auto repr = dyn_cast<IdentTypeRepr>(rhs->getTypeRepr()))
1169+
Protocols.push_back(repr);
1170+
1171+
auto CompositionRepr =
1172+
new (TC.Context) ProtocolCompositionTypeRepr(TC.Context.AllocateCopy(Protocols),
1173+
binaryExpr->getLoc(),
1174+
binaryExpr->getSourceRange());
1175+
return new (TC.Context) TypeExpr(TypeLoc(CompositionRepr, Type()));
1176+
}
1177+
}
1178+
11301179
return nullptr;
11311180
}
11321181

lib/Sema/TypeCheckDecl.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -868,13 +868,12 @@ static void checkRedeclaration(TypeChecker &tc, ValueDecl *current) {
868868
if (!currentFile || currentDC->isLocalContext())
869869
return;
870870

871-
// Cannot define a global type called 'Any'
872-
if (current->getFullName() == tc.Context.getIdentifier("Any")) {
873-
// TODO: Diagnose this as an error; current workaround needs this capability though
874-
// tc.diagnose(current, diag::invalid_redecl_any);
875-
// return;
871+
// Cannot define a type called 'Any'
872+
if (current->getFullName() == tc.Context.Id_Any) {
873+
tc.diagnose(current, diag::invalid_redecl_any);
874+
return;
876875
}
877-
876+
878877
ReferencedNameTracker *tracker = currentFile->getReferencedNameTracker();
879878
bool isCascading = true;
880879
if (current->hasAccessibility())

lib/Sema/TypeCheckType.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,7 @@ resolveTopLevelIdentTypeComponent(TypeChecker &TC, DeclContext *DC,
824824

825825
// if it is an 'Any' type, return the unbounded composition type
826826
} else if (comp->getIdentifier() == TC.Context.Id_Any) {
827-
return ProtocolCompositionType::get(TC.Context, ArrayRef<Type>());
827+
return TC.Context.TheAnyType;
828828
}
829829

830830
// Resolve the first component, which is the only one that requires

0 commit comments

Comments
 (0)