Skip to content

Cleanup inheritance clause parsing #11781

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

Merged
Merged
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
8 changes: 1 addition & 7 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -685,8 +685,6 @@ ERROR(expected_identifier_for_type,PointsToFirstBadToken,
"expected identifier for type name", ())
ERROR(expected_rangle_generic_arg_list,PointsToFirstBadToken,
"expected '>' to complete generic argument list", ())
ERROR(expected_ident_type_in_inheritance,none,
"inheritance from non-named type %0", (TypeLoc))


// Function types
Expand Down Expand Up @@ -747,8 +745,6 @@ ERROR(tuple_type_multiple_labels,none,
// Protocol Types
ERROR(expected_rangle_protocol,PointsToFirstBadToken,
"expected '>' to complete protocol-constrained type", ())
ERROR(disallowed_protocol_composition,PointsToFirstBadToken,
"protocol-constrained type is neither allowed nor needed here", ())

WARNING(swift3_deprecated_protocol_composition,none,
"'protocol<...>' composition syntax is deprecated; join the protocols using '&'", ())
Expand Down Expand Up @@ -1418,7 +1414,7 @@ ERROR(expected_generics_parameter_name,PointsToFirstBadToken,
ERROR(unexpected_class_constraint,none,
"'class' constraint can only appear on protocol declarations", ())
NOTE(suggest_anyobject,none,
"did you mean to constrain %0 with the 'AnyObject' protocol?", (Identifier))
"did you mean to write an 'AnyObject' constraint?", ())
ERROR(expected_generics_type_restriction,none,
"expected a class type or protocol-constrained type restricting %0",
(Identifier))
Expand All @@ -1427,8 +1423,6 @@ ERROR(requires_single_equal,none,
ERROR(expected_requirement_delim,none,
"expected ':' or '==' to indicate a conformance or same-type requirement",
())
ERROR(invalid_class_requirement,none,
"'class' requirement only applies to protocols", ())
ERROR(redundant_class_requirement,none,
"redundant 'class' requirement", ())
ERROR(late_class_requirement,none,
Expand Down
17 changes: 2 additions & 15 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,8 @@ class Parser {
ParserResult<ImportDecl> parseDeclImport(ParseDeclOptions Flags,
DeclAttributes &Attributes);
ParserStatus parseInheritance(SmallVectorImpl<TypeLoc> &Inherited,
bool allowClassRequirement);
bool allowClassRequirement,
bool allowAnyObject);
ParserStatus parseDeclItem(bool &PreviousHadSemi,
Parser::ParseDeclOptions Options,
llvm::function_ref<void(Decl*)> handler);
Expand Down Expand Up @@ -881,20 +882,6 @@ class Parser {
bool HandleCodeCompletion = true,
bool IsSILFuncDecl = false);

/// \brief Parse any type, but diagnose all types except type-identifier or
/// type-composition with non-type-identifier.
///
/// In some places the grammar allows only type-identifier, but when it is
/// not ambiguous, we want to parse any type for recovery purposes.
///
/// \param MessageID a generic diagnostic for a syntax error in the type
/// \param NonIdentifierTypeMessageID a diagnostic for a non-identifier type
///
/// \returns null, IdentTypeRepr, CompositionTypeRepr or ErrorTypeRepr.
ParserResult<TypeRepr>
parseTypeForInheritance(Diag<> MessageID,
Diag<TypeLoc> NonIdentifierTypeMessageID);

ParserResult<TypeRepr> parseTypeSimpleOrComposition();
ParserResult<TypeRepr>
parseTypeSimpleOrComposition(Diag<> MessageID,
Expand Down
7 changes: 7 additions & 0 deletions lib/AST/ConformanceLookupTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,13 @@ void ConformanceLookupTable::inheritConformances(ClassDecl *classDecl,
superclassLoc = inherited.getSourceRange().Start;
return superclassLoc;
}
if (inheritedType->isExistentialType()) {
auto layout = inheritedType->getExistentialLayout();
if (layout.superclass) {
superclassLoc = inherited.getSourceRange().Start;
return superclassLoc;
}
}
}
}

Expand Down
76 changes: 30 additions & 46 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2651,7 +2651,8 @@ ParserResult<ImportDecl> Parser::parseDeclImport(ParseDeclOptions Flags,
/// type-identifier
/// \endverbatim
ParserStatus Parser::parseInheritance(SmallVectorImpl<TypeLoc> &Inherited,
bool allowClassRequirement) {
bool allowClassRequirement,
bool allowAnyObject) {
Scope S(this, ScopeKind::InheritanceClause);
consumeToken(tok::colon);

Expand All @@ -2665,9 +2666,16 @@ ParserStatus Parser::parseInheritance(SmallVectorImpl<TypeLoc> &Inherited,
// If we aren't allowed to have a class requirement here, complain.
auto classLoc = consumeToken();
if (!allowClassRequirement) {
SourceLoc endLoc = Tok.is(tok::comma) ? Tok.getLoc() : classLoc;
diagnose(classLoc, diag::invalid_class_requirement)
.fixItRemove(SourceRange(classLoc, endLoc));
diagnose(classLoc, diag::unexpected_class_constraint);

// Note that it makes no sense to suggest fixing
// 'struct S : class' to 'struct S : AnyObject' for
// example; in that case we just complain about
// 'class' being invalid here.
if (allowAnyObject) {
diagnose(classLoc, diag::suggest_anyobject)
.fixItReplace(classLoc, "AnyObject");
}
continue;
}

Expand Down Expand Up @@ -2697,44 +2705,9 @@ ParserStatus Parser::parseInheritance(SmallVectorImpl<TypeLoc> &Inherited,
continue;
}

bool usesDeprecatedCompositionSyntax =
Tok.is(tok::kw_protocol) && startsWithLess(peekToken());
bool isAny = Tok.is(tok::kw_Any); // We allow (redundant) inheritance from Any

auto ParsedTypeResult = parseTypeForInheritance(
diag::expected_identifier_for_type,
diag::expected_ident_type_in_inheritance);
auto ParsedTypeResult = parseType();
Status |= ParsedTypeResult;

// Recover and emit nice diagnostic for composition.
if (auto Composition = dyn_cast_or_null<CompositionTypeRepr>(
ParsedTypeResult.getPtrOrNull())) {
// Record the protocols inside the composition.
Inherited.append(Composition->getTypes().begin(),
Composition->getTypes().end());
// We can inherit from Any
if (!isAny) {
if (usesDeprecatedCompositionSyntax) {
// Provide fixits to remove the composition, leaving the types intact.
auto compositionRange = Composition->getCompositionRange();
auto token = Lexer::getTokenAtLocation(SourceMgr, compositionRange.End);
diagnose(Composition->getSourceLoc(),
diag::disallowed_protocol_composition)
.highlight({Composition->getStartLoc(), compositionRange.End})
.fixItRemove({Composition->getSourceLoc(), compositionRange.Start})
.fixItRemove(startsWithGreater(token)
? compositionRange.End
: SourceLoc());
} else {
diagnose(Composition->getStartLoc(),
diag::disallowed_protocol_composition)
.highlight(Composition->getSourceRange());
// TODO: Decompose 'A & B & C' list to 'A, B, C'
}
}
continue;
}

// Record the type if its a single type.
if (ParsedTypeResult.isNonNull())
Inherited.push_back(ParsedTypeResult.get());
Expand Down Expand Up @@ -2957,7 +2930,9 @@ Parser::parseDeclExtension(ParseDeclOptions Flags, DeclAttributes &Attributes) {
// Parse optional inheritance clause.
SmallVector<TypeLoc, 2> Inherited;
if (Tok.is(tok::colon))
status |= parseInheritance(Inherited, /*allowClassRequirement=*/false);
status |= parseInheritance(Inherited,
/*allowClassRequirement=*/false,
/*allowAnyObject=*/false);

// Parse the optional where-clause.
TrailingWhereClause *trailingWhereClause = nullptr;
Expand Down Expand Up @@ -3296,7 +3271,9 @@ ParserResult<TypeDecl> Parser::parseDeclAssociatedType(Parser::ParseDeclOptions
// FIXME: Allow class requirements here.
SmallVector<TypeLoc, 2> Inherited;
if (Tok.is(tok::colon))
Status |= parseInheritance(Inherited, /*allowClassRequirement=*/false);
Status |= parseInheritance(Inherited,
/*allowClassRequirement=*/false,
/*allowAnyObject=*/true);

ParserResult<TypeRepr> UnderlyingTy;
if (Tok.is(tok::equal)) {
Expand Down Expand Up @@ -5036,7 +5013,9 @@ ParserResult<EnumDecl> Parser::parseDeclEnum(ParseDeclOptions Flags,
// Parse optional inheritance clause within the context of the enum.
if (Tok.is(tok::colon)) {
SmallVector<TypeLoc, 2> Inherited;
Status |= parseInheritance(Inherited, /*allowClassRequirement=*/false);
Status |= parseInheritance(Inherited,
/*allowClassRequirement=*/false,
/*allowAnyObject=*/false);
ED->setInherited(Context.AllocateCopy(Inherited));
}

Expand Down Expand Up @@ -5296,7 +5275,9 @@ ParserResult<StructDecl> Parser::parseDeclStruct(ParseDeclOptions Flags,
// Parse optional inheritance clause within the context of the struct.
if (Tok.is(tok::colon)) {
SmallVector<TypeLoc, 2> Inherited;
Status |= parseInheritance(Inherited, /*allowClassRequirement=*/false);
Status |= parseInheritance(Inherited,
/*allowClassRequirement=*/false,
/*allowAnyObject=*/false);
SD->setInherited(Context.AllocateCopy(Inherited));
}

Expand Down Expand Up @@ -5381,7 +5362,9 @@ ParserResult<ClassDecl> Parser::parseDeclClass(SourceLoc ClassLoc,
// Parse optional inheritance clause within the context of the class.
if (Tok.is(tok::colon)) {
SmallVector<TypeLoc, 2> Inherited;
Status |= parseInheritance(Inherited, /*allowClassRequirement=*/false);
Status |= parseInheritance(Inherited,
/*allowClassRequirement=*/false,
/*allowAnyObject=*/false);
CD->setInherited(Context.AllocateCopy(Inherited));
}

Expand Down Expand Up @@ -5469,7 +5452,8 @@ parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes) {
if (Tok.is(tok::colon)) {
colonLoc = Tok.getLoc();
Status |= parseInheritance(InheritedProtocols,
/*allowClassRequirement=*/true);
/*allowClassRequirement=*/true,
/*allowAnyObject=*/true);
}

TrailingWhereClause *TrailingWhere = nullptr;
Expand Down
11 changes: 4 additions & 7 deletions lib/Parse/ParseGeneric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,10 @@ Parser::parseGenericParameters(SourceLoc LAngleLoc) {
ParserResult<TypeRepr> Ty;

if (Tok.isAny(tok::identifier, tok::code_complete, tok::kw_protocol, tok::kw_Any)) {
Ty = parseTypeForInheritance(diag::expected_identifier_for_type,
diag::expected_ident_type_in_inheritance);
Ty = parseType();
} else if (Tok.is(tok::kw_class)) {
diagnose(Tok, diag::unexpected_class_constraint);
diagnose(Tok, diag::suggest_anyobject, Name)
diagnose(Tok, diag::suggest_anyobject)
.fixItReplace(Tok.getLoc(), "AnyObject");
consumeToken();
Invalid = true;
Expand All @@ -92,7 +91,7 @@ Parser::parseGenericParameters(SourceLoc LAngleLoc) {
Inherited.push_back(Ty.get());
}

// We always create generic type parameters with a depth of zero.
// We always create generic type parameters with an invalid depth.
// Semantic analysis fills in the depth when it processes the generic
// parameter list.
auto Param = new (Context) GenericTypeParamDecl(
Expand Down Expand Up @@ -289,9 +288,7 @@ ParserStatus Parser::parseGenericWhereClause(
}
} else {
// Parse the protocol or composition.
ParserResult<TypeRepr> Protocol =
parseTypeForInheritance(diag::expected_identifier_for_type,
diag::expected_ident_type_in_inheritance);
ParserResult<TypeRepr> Protocol = parseType();

if (Protocol.isNull()) {
Status.setIsParseError();
Expand Down
36 changes: 0 additions & 36 deletions lib/Parse/ParseType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,42 +470,6 @@ ParserResult<TypeRepr> Parser::parseType(Diag<> MessageID,
specifierLoc));
}

ParserResult<TypeRepr> Parser::parseTypeForInheritance(
Diag<> MessageID, Diag<TypeLoc> NonIdentifierTypeMessageID) {
ParserResult<TypeRepr> Ty = parseTypeSimpleOrComposition(MessageID);

if (Ty.hasCodeCompletion())
return makeParserCodeCompletionResult<TypeRepr>();

if (Ty.isParseError() || isa<ErrorTypeRepr>(Ty.get()))
return Ty;

if (isa<IdentTypeRepr>(Ty.get()))
return Ty;

if (auto Comp = dyn_cast<CompositionTypeRepr>(Ty.get())) {
bool hasNonIdent = false;
for (auto T : Comp->getTypes()) {
if (isa<IdentTypeRepr>(T))
continue;
hasNonIdent = true;
diagnose(T->getLoc(), NonIdentifierTypeMessageID, T)
.highlight(T->getSourceRange());
}
// IdentType only composition are allowed.
if (!hasNonIdent)
return Ty;
} else {
diagnose(Ty.get()->getLoc(), NonIdentifierTypeMessageID, Ty.get())
.highlight(Ty.get()->getSourceRange());
}

return makeParserErrorResult(
new (Context) ErrorTypeRepr(Ty.get()->getSourceRange()));

return Ty;
}

bool Parser::parseGenericArguments(SmallVectorImpl<TypeRepr*> &Args,
SourceLoc &LAngleLoc,
SourceLoc &RAngleLoc) {
Expand Down
22 changes: 19 additions & 3 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,8 +490,9 @@ void TypeChecker::checkInheritanceClause(Decl *decl,
if (inheritedTy->isExistentialType()) {
auto layout = inheritedTy->getExistentialLayout();

// Classes and extensions cannot inherit from subclass
// existentials or AnyObject.
// Protocols, generic parameters and associated types can inherit
// from subclass existentials, which are "exploded" into their
// corresponding requirements.
if (isa<ProtocolDecl>(decl) ||
isa<AbstractTypeParamDecl>(decl) ||
(!layout.hasExplicitAnyObject &&
Expand All @@ -503,7 +504,22 @@ void TypeChecker::checkInheritanceClause(Decl *decl,
continue;
}

// Swift 3 compatibility:
// Classes can inherit from subclass existentials as long as they
// do not contain an explicit AnyObject member.
if (isa<ClassDecl>(decl) &&
!layout.hasExplicitAnyObject) {
for (auto proto : layout.getProtocols()) {
auto *protoDecl = proto->getDecl();
allProtocols.insert(protoDecl);
}

// Superclass inheritance is handled below.
inheritedTy = layout.superclass;
if (!inheritedTy)
continue;
}

// Swift 3 compatibility -- a class inheriting from AnyObject is a no-op.
if (Context.LangOpts.isSwiftVersion3() && isa<ClassDecl>(decl) &&
inheritedTy->isAnyObject()) {
auto classDecl = cast<ClassDecl>(decl);
Expand Down
2 changes: 1 addition & 1 deletion test/Parse/invalid.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ prefix operator %
prefix func %<T>(x: T) -> T { return x } // No error expected - the < is considered an identifier but is peeled off by the parser.

struct Weak<T: class> { // expected-error {{'class' constraint can only appear on protocol declarations}}
// expected-note@-1 {{did you mean to constrain 'T' with the 'AnyObject' protocol?}} {{16-21=AnyObject}}
// expected-note@-1 {{did you mean to write an 'AnyObject' constraint?}} {{16-21=AnyObject}}
weak let value: T // expected-error {{'weak' must be a mutable variable, because it may change at runtime}} expected-error {{'weak' variable should have optional type 'T?'}} expected-error {{'weak' may not be applied to non-class-bound 'T'; consider adding a protocol conformance that has a class bound}}
}

Expand Down
2 changes: 1 addition & 1 deletion test/attr/attr_specialize.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public func funcWithTwoGenericParameters<X, Y>(x: X, y: Y) {
@_specialize(kind: partial, exported: true, where X == Int, Y == Int)
@_specialize(kind: partial, kind: partial, where X == Int, Y == Int) // expected-error{{parameter 'kind' was already defined in '_specialize' attribute}}

@_specialize(where X == Int, Y == Int, exported: true, kind: partial) // expected-error{{use of undeclared type 'exported'}} expected-error{{use of undeclared type 'kind'}} expected-error{{use of undeclared type 'partial'}} expected-error{{expected identifier for type name}}
@_specialize(where X == Int, Y == Int, exported: true, kind: partial) // expected-error{{use of undeclared type 'exported'}} expected-error{{use of undeclared type 'kind'}} expected-error{{use of undeclared type 'partial'}} expected-error{{expected type}}
public func anotherFuncWithTwoGenericParameters<X: P, Y>(x: X, y: Y) {
}

Expand Down
20 changes: 20 additions & 0 deletions test/decl/class/classes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,23 @@ class HDerived : H {
override func f(_ x: Int) { }
override class func f(_ x: Int) { }
}

// Subclass existentials in inheritance clause
protocol P {}
protocol Q {}
protocol R {}
class Base : Q & R {}

class Derived : P & Base {}

func f(_: P) {}
func g(_: Base) {}
func h(_: Q) {}
func i(_: R) {}

func testSubclassExistentialsInInheritanceClause() {
f(Derived())
g(Derived())
h(Derived())
i(Derived())
}
Loading