Skip to content

Sema: Reject 'any P' in inheritance clause #41537

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 2 commits into from
Feb 24, 2022
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
6 changes: 4 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2784,9 +2784,11 @@ WARNING(anyobject_class_inheritance_deprecated,Deprecation,
ERROR(multiple_inheritance,none,
"multiple inheritance from classes %0 and %1", (Type, Type))
ERROR(inheritance_from_non_protocol_or_class,none,
"inheritance from non-protocol, non-class type %0", (Type))
"inheritance from non-protocol, non-class type '%0'", (StringRef))
ERROR(inheritance_from_non_protocol,none,
"inheritance from non-protocol type %0", (Type))
"inheritance from non-protocol type '%0'", (StringRef))
ERROR(inheritance_from_anyobject,none,
"only protocols can inherit from 'AnyObject'", ())
ERROR(inheritance_from_parameterized_protocol,none,
"cannot inherit from protocol type with generic argument %0", (Type))
ERROR(superclass_not_first,none,
Expand Down
33 changes: 19 additions & 14 deletions lib/Sema/TypeCheckDeclPrimary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,15 @@ static void checkInheritanceClause(
inheritedAnyObject = { i, inherited.getSourceRange() };
}

if (inheritedTy->isExistentialType()) {
if (inheritedTy->is<ParameterizedProtocolType>()) {
if (!isa<ProtocolDecl>(decl)) {
decl->diagnose(diag::inheritance_from_parameterized_protocol,
inheritedTy);
}
continue;
}

if (inheritedTy->isConstraintType()) {
auto layout = inheritedTy->getExistentialLayout();

// Subclass existentials are not allowed except on classes and
Expand All @@ -227,10 +235,7 @@ static void checkInheritanceClause(
// AnyObject is not allowed except on protocols.
if (layout.hasExplicitAnyObject &&
!isa<ProtocolDecl>(decl)) {
decl->diagnose(canHaveSuperclass
? diag::inheritance_from_non_protocol_or_class
: diag::inheritance_from_non_protocol,
inheritedTy);
decl->diagnose(diag::inheritance_from_anyobject);
continue;
}

Expand All @@ -249,14 +254,6 @@ static void checkInheritanceClause(
inheritedTy = layout.explicitSuperclass;
}

if (inheritedTy->is<ParameterizedProtocolType>()) {
if (!isa<ProtocolDecl>(decl)) {
decl->diagnose(diag::inheritance_from_parameterized_protocol,
inheritedTy);
}
continue;
}

// If this is an enum inheritance clause, check for a raw type.
if (isa<EnumDecl>(decl)) {
// Check if we already had a raw type.
Expand Down Expand Up @@ -338,11 +335,19 @@ static void checkInheritanceClause(
}
}

// FIXME: The inherited type is printed directly here because
// the current default is to not print `any` for existential
// types, but this error message is super confusing without `any`
// if the user wrote it explicitly.
PrintOptions options;
options.PrintExplicitAny = true;
auto inheritedTyString = inheritedTy.getString(options);

// We can't inherit from a non-class, non-protocol type.
decl->diagnose(canHaveSuperclass
? diag::inheritance_from_non_protocol_or_class
: diag::inheritance_from_non_protocol,
inheritedTy);
inheritedTyString);
// FIXME: Note pointing to the declaration 'inheritedTy' references?
}
}
Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/TypeCheckRequestFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ Type EnumRawTypeRequest::evaluate(Evaluator &evaluator,
auto &inheritedType = *inheritedTypeResult;
if (!inheritedType) continue;

// Skip existential types.
if (inheritedType->isExistentialType()) continue;
// Skip protocol conformances.
if (inheritedType->isConstraintType()) continue;

// We found a raw type; return it.
return inheritedType;
Expand Down
31 changes: 17 additions & 14 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2612,6 +2612,23 @@ TypeResolver::resolveAttributedType(TypeAttributes &attrs, TypeRepr *repr,
}
}

if (attrs.has(TAK_unchecked)) {
ty = resolveType(repr, options);
if (!ty || ty->hasError()) return ty;

if (!options.is(TypeResolverContext::Inherited) ||
getDeclContext()->getSelfProtocolDecl()) {
diagnoseInvalid(repr, attrs.getLoc(TAK_unchecked),
diag::unchecked_not_inheritance_clause);
} else if (!ty->isConstraintType()) {
diagnoseInvalid(repr, attrs.getLoc(TAK_unchecked),
diag::unchecked_not_existential, ty);
}

// Nothing to record in the type. Just clear the attribute.
attrs.clearAttribute(TAK_unchecked);
}

auto instanceOptions = options;
instanceOptions.setContext(None);

Expand Down Expand Up @@ -2778,20 +2795,6 @@ TypeResolver::resolveAttributedType(TypeAttributes &attrs, TypeRepr *repr,
}
}

if (attrs.has(TAK_unchecked)) {
if (!options.is(TypeResolverContext::Inherited) ||
getDeclContext()->getSelfProtocolDecl()) {
diagnoseInvalid(repr, attrs.getLoc(TAK_unchecked),
diag::unchecked_not_inheritance_clause);
} else if (!ty->isExistentialType()) {
diagnoseInvalid(repr, attrs.getLoc(TAK_unchecked),
diag::unchecked_not_existential, ty);
}

// Nothing to record in the type. Just clear the attribute.
attrs.clearAttribute(TAK_unchecked);
}

for (unsigned i = 0; i != TypeAttrKind::TAK_Count; ++i)
if (attrs.has((TypeAttrKind)i)) {
diagnoseInvalid(repr, attrs.getLoc((TypeAttrKind)i),
Expand Down
16 changes: 8 additions & 8 deletions test/decl/protocol/conforms/placement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,20 +105,20 @@ extension ExplicitSub1 : P1 { } // expected-error{{redundant conformance of 'Exp
// ---------------------------------------------------------------------------
// Suppression of synthesized conformances
// ---------------------------------------------------------------------------
class SynthesizedClass1 : AnyObject { } // expected-error{{inheritance from non-protocol, non-class type 'AnyObject'}}
class SynthesizedClass1 : AnyObject { } // expected-error{{only protocols can inherit from 'AnyObject'}}

class SynthesizedClass2 { }
extension SynthesizedClass2 : AnyObject { } // expected-error{{inheritance from non-protocol type 'AnyObject'}}
extension SynthesizedClass2 : AnyObject { } // expected-error{{only protocols can inherit from 'AnyObject'}}

class SynthesizedClass3 : AnyObjectRefinement { }

class SynthesizedClass4 { }
extension SynthesizedClass4 : AnyObjectRefinement { }

class SynthesizedSubClass1 : SynthesizedClass1, AnyObject { } // expected-error{{inheritance from non-protocol, non-class type 'AnyObject'}}
class SynthesizedSubClass1 : SynthesizedClass1, AnyObject { } // expected-error{{only protocols can inherit from 'AnyObject'}}

class SynthesizedSubClass2 : SynthesizedClass2 { }
extension SynthesizedSubClass2 : AnyObject { } // expected-error{{inheritance from non-protocol type 'AnyObject'}}
extension SynthesizedSubClass2 : AnyObject { } // expected-error{{only protocols can inherit from 'AnyObject'}}

class SynthesizedSubClass3 : SynthesizedClass1, AnyObjectRefinement { }

Expand Down Expand Up @@ -169,12 +169,12 @@ extension MFExplicitSub1 : P1 { } // expected-error{{redundant conformance of 'M
// ---------------------------------------------------------------------------
class MFSynthesizedClass1 { }

extension MFSynthesizedClass2 : AnyObject { } // expected-error{{inheritance from non-protocol type 'AnyObject'}}
extension MFSynthesizedClass2 : AnyObject { } // expected-error{{only protocols can inherit from 'AnyObject'}}

class MFSynthesizedClass4 { }
extension MFSynthesizedClass4 : AnyObjectRefinement { }

extension MFSynthesizedSubClass2 : AnyObject { } // expected-error{{inheritance from non-protocol type 'AnyObject'}}
extension MFSynthesizedSubClass2 : AnyObject { } // expected-error{{only protocols can inherit from 'AnyObject'}}

extension MFSynthesizedSubClass3 : AnyObjectRefinement { }

Expand All @@ -198,7 +198,7 @@ extension MMSuper1 : MMP1 { } // expected-warning{{conformance of 'MMSuper1' to
extension MMSuper1 : MMP2a { } // expected-warning{{conformance of 'MMSuper1' to protocol 'MMP2a' was already stated in the type's module 'placement_module_A'}}
extension MMSuper1 : MMP3b { } // okay

extension MMSub1 : AnyObject { } // expected-error{{inheritance from non-protocol type 'AnyObject'}}
extension MMSub1 : AnyObject { } // expected-error{{only protocols can inherit from 'AnyObject'}}

extension MMSub2 : MMP1 { } // expected-warning{{conformance of 'MMSub2' to protocol 'MMP1' was already stated in the type's module 'placement_module_A'}}
extension MMSub2 : MMP2a { } // expected-warning{{conformance of 'MMSub2' to protocol 'MMP2a' was already stated in the type's module 'placement_module_A'}}
Expand All @@ -209,7 +209,7 @@ extension MMSub2 : MMAnyObjectRefinement { } // okay
extension MMSub3 : MMP1 { } // expected-warning{{conformance of 'MMSub3' to protocol 'MMP1' was already stated in the type's module 'placement_module_A'}}
extension MMSub3 : MMP2a { } // expected-warning{{conformance of 'MMSub3' to protocol 'MMP2a' was already stated in the type's module 'placement_module_A'}}
extension MMSub3 : MMP3b { } // okay
extension MMSub3 : AnyObject { } // expected-error{{inheritance from non-protocol type 'AnyObject'}}
extension MMSub3 : AnyObject { } // expected-error{{only protocols can inherit from 'AnyObject'}}

extension MMSub4 : MMP1 { } // expected-warning{{conformance of 'MMSub4' to protocol 'MMP1' was already stated in the type's module 'placement_module_B'}}
extension MMSub4 : MMP2a { } // expected-warning{{conformance of 'MMSub4' to protocol 'MMP2a' was already stated in the type's module 'placement_module_B'}}
Expand Down
21 changes: 21 additions & 0 deletions test/type/explicit_existential.swift
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,24 @@ func testConstraintAlias(x: Constraint) {} // expected-warning{{'Constraint' (ak

typealias Existential = any Input
func testExistentialAlias(x: Existential, y: any Constraint) {}

// Reject explicit existential types in inheritance clauses
protocol Empty {}

struct S : any Empty {} // expected-error {{inheritance from non-protocol type 'any Empty'}}
class C : any Empty {} // expected-error {{inheritance from non-protocol, non-class type 'any Empty'}}

// FIXME: Diagnostics are not great in the enum case because we confuse this with a raw type

enum E : any Empty { // expected-error {{raw type 'Empty' is not expressible by a string, integer, or floating-point literal}}
// expected-error@-1 {{'E' declares raw type 'Empty', but does not conform to RawRepresentable and conformance could not be synthesized}}
// expected-error@-2 {{RawRepresentable conformance cannot be synthesized because raw type 'Empty' is not Equatable}}
case hack
}

enum EE : Equatable, any Empty { // expected-error {{raw type 'Empty' is not expressible by a string, integer, or floating-point literal}}
// expected-error@-1 {{'EE' declares raw type 'Empty', but does not conform to RawRepresentable and conformance could not be synthesized}}
// expected-error@-2 {{RawRepresentable conformance cannot be synthesized because raw type 'Empty' is not Equatable}}
// expected-error@-3 {{raw type 'Empty' must appear first in the enum inheritance clause}}
case hack
}