mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Implement semantic analysis for abstract initializers.
Swift SVN r14221
This commit is contained in:
@@ -55,6 +55,7 @@ TYPE_ATTR(callee_guaranteed)
|
||||
TYPE_ATTR(objc_metatype)
|
||||
TYPE_ATTR(opened)
|
||||
|
||||
ATTR(abstract)
|
||||
ATTR(assignment)
|
||||
ATTR(class_protocol)
|
||||
ATTR(conversion)
|
||||
|
||||
@@ -335,6 +335,7 @@ public:
|
||||
bool requiresStoredPropertyInits() const {
|
||||
return has(AK_requires_stored_property_inits);
|
||||
}
|
||||
bool isAbstract() const { return has(AK_abstract); }
|
||||
|
||||
bool hasMutating() const { return has(AK_mutating); }
|
||||
Optional<bool> getMutating() const {
|
||||
|
||||
@@ -238,8 +238,11 @@ class alignas(8) Decl {
|
||||
/// of the definition of the constructor that is useful only to semantic
|
||||
/// analysis and SIL generation.
|
||||
unsigned ComputedBodyInitKind : 3;
|
||||
|
||||
/// Whether this initializer is abstract.
|
||||
unsigned Abstract : 1;
|
||||
};
|
||||
enum { NumConstructorDeclBits = NumAbstractFunctionDeclBits + 3 };
|
||||
enum { NumConstructorDeclBits = NumAbstractFunctionDeclBits + 4 };
|
||||
static_assert(NumConstructorDeclBits <= 32, "fits in an unsigned");
|
||||
|
||||
class TypeDeclBitfields {
|
||||
@@ -2937,6 +2940,9 @@ public:
|
||||
AbstractFunctionDeclBits.HasSelectorStyleSignature = true;
|
||||
}
|
||||
|
||||
/// Retrieve the Objective-C selector that names this method.
|
||||
StringRef getObjCSelector(SmallVectorImpl<char> &buffer) const;
|
||||
|
||||
/// Determine the default argument kind and type for the given argument index
|
||||
/// in this declaration, which must be a function or constructor.
|
||||
///
|
||||
@@ -3455,6 +3461,10 @@ class ConstructorDecl : public AbstractFunctionDecl {
|
||||
/// inserted at the end of the initializer by SILGen.
|
||||
Expr *CallToSuperInit = nullptr;
|
||||
|
||||
/// The constructor this overrides, which only makes sense when
|
||||
/// both the overriding and the overridden constructors are abstract.
|
||||
ConstructorDecl *OverriddenDecl = nullptr;
|
||||
|
||||
public:
|
||||
ConstructorDecl(Identifier NameHack, SourceLoc ConstructorLoc,
|
||||
Pattern *SelfArgParam, Pattern *ArgParams,
|
||||
@@ -3521,6 +3531,17 @@ public:
|
||||
BodyInitKind getDelegatingOrChainedInitKind(DiagnosticEngine *diags,
|
||||
Expr **init = nullptr);
|
||||
|
||||
/// Whether this constructor is abstract,
|
||||
bool isAbstract() const { return ConstructorDeclBits.Abstract; }
|
||||
|
||||
/// Set whether this constructor is abstract.
|
||||
void setAbstract(bool abstract) {
|
||||
ConstructorDeclBits.Abstract = abstract;
|
||||
}
|
||||
|
||||
ConstructorDecl *getOverriddenDecl() const { return OverriddenDecl; }
|
||||
void setOverriddenDecl(ConstructorDecl *over) { OverriddenDecl = over; }
|
||||
|
||||
static bool classof(const Decl *D) {
|
||||
return D->getKind() == DeclKind::Constructor;
|
||||
}
|
||||
@@ -3557,6 +3578,11 @@ public:
|
||||
SourceLoc getStartLoc() const { return getDestructorLoc(); }
|
||||
SourceRange getSourceRange() const;
|
||||
|
||||
/// Retrieve the Objective-C selector associated with the destructor.
|
||||
///
|
||||
/// This is always "dealloc".
|
||||
StringRef getObjCSelector(SmallVectorImpl<char> &buffer) const;
|
||||
|
||||
static bool classof(const Decl *D) {
|
||||
return D->getKind() == DeclKind::Destructor;
|
||||
}
|
||||
|
||||
@@ -553,8 +553,12 @@ ERROR(non_delegating_init_outside_module,sema_tce,none,
|
||||
"(with 'self.init')", (Type, Identifier))
|
||||
|
||||
ERROR(dynamic_construct_class,sema_tce,none,
|
||||
"cannot construct an object of class type %0 with a metatype value",
|
||||
(Type))
|
||||
"constructing an object of class type %0 with a metatype value requires "
|
||||
"an abstract initializer", (Type))
|
||||
NOTE(note_nonabstract_initializer,sema_tce,none,
|
||||
"selected non-abstract initializer declared here", ())
|
||||
NOTE(note_nonabstract_implicit_initializer,sema_tce,none,
|
||||
"selected implicit initializer with type %0", (Type))
|
||||
|
||||
// Operators
|
||||
ERROR(unknown_binop,sema_tce,none,
|
||||
@@ -790,7 +794,21 @@ ERROR(invalid_weak_ownership_not_optional,attribute_parsing,none,
|
||||
|
||||
// requires_stored_property_inits
|
||||
ERROR(requires_stored_property_inits_nonclass,attribute_parsing,none,
|
||||
"'requires_stored_property_inits' attribute can only be applied to a class", ())
|
||||
"'requires_stored_property_inits' attribute can only be applied to a "
|
||||
"class", ())
|
||||
|
||||
// abstract
|
||||
ERROR(abstract_non_initializer,attribute_parsing,none,
|
||||
"'abstract' attribute can only be applied to an initializer", ())
|
||||
ERROR(abstract_initializer_nonclass,attribute_parsing,none,
|
||||
"'abstract' initializer in non-class type %0", (Type))
|
||||
ERROR(abstract_initializer_in_extension,attribute_parsing,none,
|
||||
"'abstract' initializer must be declared directly in class %0"
|
||||
" (not in an extension)", (Type))
|
||||
ERROR(abstract_incomplete_implementation,sema_tcd,none,
|
||||
"class %0 does not implement its superclass's abstract members", (Type))
|
||||
NOTE(abstract_initializer_not_overridden,attribute_parsing,none,
|
||||
"'abstract' initializer with type %0 not overridden", (Type))
|
||||
|
||||
// Functions
|
||||
ERROR(attribute_requires_function_type,attribute_parsing,none,
|
||||
|
||||
@@ -37,7 +37,7 @@ const uint16_t VERSION_MAJOR = 0;
|
||||
/// Serialized module format minor version number.
|
||||
///
|
||||
/// When the format changes IN ANY WAY, this number should be incremented.
|
||||
const uint16_t VERSION_MINOR = 6;
|
||||
const uint16_t VERSION_MINOR = 7;
|
||||
|
||||
using DeclID = Fixnum<31>;
|
||||
using DeclIDField = BCFixed<31>;
|
||||
@@ -607,8 +607,10 @@ namespace decls_block {
|
||||
BCFixed<1>, // has selector-style signature?
|
||||
BCFixed<1>, // objc?
|
||||
BCFixed<1>, // transparent?
|
||||
BCFixed<1>, // abstract?
|
||||
TypeIDField, // type (signature)
|
||||
TypeIDField // type (interface)
|
||||
TypeIDField, // type (interface)
|
||||
DeclIDField // overridden decl
|
||||
// Trailed by its generic parameters, if any, followed by the parameter
|
||||
// patterns.
|
||||
>;
|
||||
|
||||
@@ -592,6 +592,9 @@ namespace {
|
||||
|
||||
void visitConstructorDecl(ConstructorDecl *CD) {
|
||||
printCommonAFD(CD, "constructor_decl");
|
||||
if (CD->isAbstract())
|
||||
OS << " abstract";
|
||||
|
||||
printAbstractFunctionDecl(CD);
|
||||
OS << ')';
|
||||
}
|
||||
|
||||
@@ -844,6 +844,11 @@ void PrintAST::visitConstructorDecl(ConstructorDecl *decl) {
|
||||
recordDeclLoc(decl);
|
||||
printAttributes(decl->getAttrs());
|
||||
printImplicitObjCNote(decl);
|
||||
if (!Options.SkipImplicit && decl->isAbstract() &&
|
||||
!decl->getAttrs().isAbstract()) {
|
||||
Printer << "/* @abstract(inferred) */ ";
|
||||
}
|
||||
|
||||
Printer << "init";
|
||||
if (decl->isGeneric()) {
|
||||
printGenericParams(decl->getGenericParams());
|
||||
|
||||
@@ -82,4 +82,6 @@ void DeclAttributes::print(ASTPrinter &Printer) const {
|
||||
Printer << "@mutating ";
|
||||
if (MutatingAttr && !MutatingAttr.getValue())
|
||||
Printer << "@!mutating ";
|
||||
if (isAbstract())
|
||||
Printer << "@abstract ";
|
||||
}
|
||||
|
||||
@@ -577,6 +577,8 @@ ValueDecl *ValueDecl::getOverriddenDecl() const {
|
||||
return fd->getOverriddenDecl();
|
||||
if (auto sdd = dyn_cast<AbstractStorageDecl>(this))
|
||||
return sdd->getOverriddenDecl();
|
||||
if (auto cd = dyn_cast<ConstructorDecl>(this))
|
||||
return cd->getOverriddenDecl();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1623,6 +1625,17 @@ SourceRange AbstractFunctionDecl::getBodySourceRange() const {
|
||||
}
|
||||
}
|
||||
|
||||
StringRef AbstractFunctionDecl::getObjCSelector(
|
||||
SmallVectorImpl<char> &buffer) const {
|
||||
if (auto func = dyn_cast<FuncDecl>(this))
|
||||
return func->getObjCSelector(buffer);
|
||||
if (auto ctor = dyn_cast<ConstructorDecl>(this))
|
||||
return ctor->getObjCSelector(buffer);
|
||||
if (auto dtor = dyn_cast<DestructorDecl>(this))
|
||||
return dtor->getObjCSelector(buffer);
|
||||
llvm_unreachable("Unhandled AbstractFunctionDecl subclass");
|
||||
}
|
||||
|
||||
/// Set the DeclContext of any VarDecls in P to the specified DeclContext.
|
||||
static void setDeclContextOfPatternVars(Pattern *P, DeclContext *DC) {
|
||||
if (!P) return;
|
||||
@@ -1747,6 +1760,7 @@ ConstructorDecl::ConstructorDecl(Identifier NameHack, SourceLoc ConstructorLoc,
|
||||
setBodyParams(SelfBodyParam, BodyParams);
|
||||
|
||||
ConstructorDeclBits.ComputedBodyInitKind = 0;
|
||||
ConstructorDeclBits.Abstract = 0;
|
||||
}
|
||||
|
||||
void ConstructorDecl::setArgParams(Pattern *selfPattern, Pattern *argParams) {
|
||||
@@ -2073,6 +2087,10 @@ ConstructorDecl::getDelegatingOrChainedInitKind(DiagnosticEngine *diags,
|
||||
return finder.Kind;
|
||||
}
|
||||
|
||||
StringRef DestructorDecl::getObjCSelector(SmallVectorImpl<char> &buffer) const {
|
||||
return "dealloc";
|
||||
}
|
||||
|
||||
SourceRange DestructorDecl::getSourceRange() const {
|
||||
if (getBodyKind() == BodyKind::Unparsed ||
|
||||
getBodyKind() == BodyKind::Skipped)
|
||||
|
||||
@@ -3539,12 +3539,21 @@ Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
|
||||
declRef->setImplicit(apply->isImplicit());
|
||||
apply->setFn(declRef);
|
||||
|
||||
// If we're constructing a class object, the metatype must be statically
|
||||
// derived (rather than an arbitrary value of metatype type).
|
||||
// If we're constructing a class object, either the metatype must be
|
||||
// statically derived (rather than an arbitrary value of metatype type) or
|
||||
// the referenced constructor must be abstract.
|
||||
if ((ty->getClassOrBoundGenericClass() || ty->is<DynamicSelfType>()) &&
|
||||
!isStaticallyDerivedMetatype(fn)) {
|
||||
!isStaticallyDerivedMetatype(fn) &&
|
||||
!cast<ConstructorDecl>(decl)->isAbstract()) {
|
||||
tc.diagnose(apply->getLoc(), diag::dynamic_construct_class, ty)
|
||||
.highlight(fn->getSourceRange());
|
||||
auto ctor = cast<ConstructorDecl>(decl);
|
||||
// FIXME: Better description of the initializer than just it's type.
|
||||
if (ctor->isImplicit())
|
||||
tc.diagnose(decl, diag::note_nonabstract_implicit_initializer,
|
||||
ctor->getArgumentType());
|
||||
else
|
||||
tc.diagnose(decl, diag::note_nonabstract_initializer);
|
||||
}
|
||||
|
||||
// Tail-recur to actually call the constructor.
|
||||
|
||||
@@ -1930,6 +1930,45 @@ public:
|
||||
if (!IsSecondPass) {
|
||||
TC.addImplicitConstructors(CD);
|
||||
TC.addImplicitDestructor(CD);
|
||||
|
||||
// Check whether the superclass has any abstract initializers and whether
|
||||
// they have been implemented.
|
||||
if (auto superclassTy = CD->getSuperclass()) {
|
||||
// Collect the set of constructors we override in the base class.
|
||||
llvm::SmallPtrSet<ConstructorDecl *, 4> overriddenCtors;
|
||||
for (auto member : TC.lookupConstructors(CD->getDeclaredTypeInContext(),
|
||||
CD)) {
|
||||
auto ctor = cast<ConstructorDecl>(member);
|
||||
if (auto overridden = ctor->getOverriddenDecl())
|
||||
overriddenCtors.insert(overridden);
|
||||
}
|
||||
|
||||
// Diagnose any abstract constructors from our superclass that have
|
||||
// not been overridden.
|
||||
bool diagnosed = false;
|
||||
for (auto superclassMember : TC.lookupConstructors(superclassTy, CD)) {
|
||||
// We only care about abstract constructors.
|
||||
auto superclassCtor = cast<ConstructorDecl>(superclassMember);
|
||||
if (!superclassCtor->isAbstract())
|
||||
continue;
|
||||
|
||||
// If we have an override for this constructor, it's okay.
|
||||
if (overriddenCtors.count(superclassCtor) > 0)
|
||||
continue;
|
||||
|
||||
// Complain that we don't have an overriding constructor.
|
||||
if (!diagnosed) {
|
||||
TC.diagnose(CD, diag::abstract_incomplete_implementation,
|
||||
CD->getDeclaredInterfaceType());
|
||||
diagnosed = true;
|
||||
}
|
||||
|
||||
// FIXME: Using the type here is awful. We want to use the selector
|
||||
// name and provide a nice Fix-It with that declaration.
|
||||
TC.diagnose(superclassCtor, diag::abstract_initializer_not_overridden,
|
||||
superclassCtor->getArgumentType());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!IsFirstPass) {
|
||||
checkExplicitConformance(CD, CD->getDeclaredTypeInContext());
|
||||
@@ -2511,6 +2550,8 @@ public:
|
||||
overridingVar->setOverriddenDecl(cast<VarDecl>(overridden));
|
||||
} else if (auto overridingSubscript = dyn_cast<SubscriptDecl>(overriding)) {
|
||||
overridingSubscript->setOverriddenDecl(cast<SubscriptDecl>(overridden));
|
||||
} else if (auto overridingCtor = dyn_cast<ConstructorDecl>(overriding)) {
|
||||
overridingCtor->setOverriddenDecl(cast<ConstructorDecl>(overridden));
|
||||
} else {
|
||||
llvm_unreachable("Unexpected decl");
|
||||
}
|
||||
@@ -2518,10 +2559,29 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Determine which methods this method overrides.
|
||||
/// Perform basic checking to determine whether a function can override a
|
||||
/// parent function.
|
||||
static bool areOverrideCompatibleSimple(AbstractFunctionDecl *afd,
|
||||
AbstractFunctionDecl *parentAFD) {
|
||||
if (auto func = dyn_cast<FuncDecl>(afd)) {
|
||||
// Specific checking for methods.
|
||||
auto parentFunc = cast<FuncDecl>(parentAFD);
|
||||
if (func->isStatic() != parentFunc->isStatic())
|
||||
return false;
|
||||
} else if (isa<ConstructorDecl>(afd)) {
|
||||
// Specific checking for constructors.
|
||||
auto parentCtor = cast<ConstructorDecl>(parentAFD);
|
||||
if (!parentCtor->isAbstract())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Determine which methods this function overrides (if any).
|
||||
///
|
||||
/// \returns true if an error occurred.
|
||||
bool checkMethodOverrides(FuncDecl *method) {
|
||||
bool checkMethodOverrides(AbstractFunctionDecl *method) {
|
||||
if (method->isInvalid())
|
||||
return false;
|
||||
|
||||
@@ -2554,22 +2614,29 @@ public:
|
||||
|
||||
// Look for members with the same name and matching types as this
|
||||
// one.
|
||||
LookupResult members;
|
||||
if (isa<ConstructorDecl>(method)) {
|
||||
members = TC.lookupConstructors(superclass, method);
|
||||
} else {
|
||||
auto superclassMetaTy = MetatypeType::get(superclass, TC.Context);
|
||||
auto members = TC.lookupMember(superclassMetaTy, method->getName(), method,
|
||||
members = TC.lookupMember(superclassMetaTy, method->getName(), method,
|
||||
/*allowDynamicLookup=*/false);
|
||||
}
|
||||
|
||||
unsigned numExactMatches = false;
|
||||
SmallVector<llvm::PointerIntPair<FuncDecl *, 1, bool>, 2> matches;
|
||||
FuncDecl *exactMatch = nullptr;
|
||||
SmallVector<llvm::PointerIntPair<AbstractFunctionDecl *, 1, bool>, 2> matches;
|
||||
AbstractFunctionDecl *exactMatch = nullptr;
|
||||
for (auto member : members) {
|
||||
if (member->isInvalid())
|
||||
continue;
|
||||
|
||||
auto parentMethod = dyn_cast<FuncDecl>(member);
|
||||
auto parentMethod = dyn_cast<AbstractFunctionDecl>(member);
|
||||
if (!parentMethod)
|
||||
continue;
|
||||
|
||||
// Class and non-class methods are different.
|
||||
if (method->isStatic() != parentMethod->isStatic())
|
||||
// Check whether there are any obvious reasons why the two given
|
||||
// functions do not have an overriding relationship.
|
||||
if (!areOverrideCompatibleSimple(method, parentMethod))
|
||||
continue;
|
||||
|
||||
// If both are Objective-C, then match based on selectors and
|
||||
@@ -2901,6 +2968,15 @@ public:
|
||||
isObjC = false;
|
||||
CD->setIsObjC(isObjC);
|
||||
}
|
||||
|
||||
// Check whether this constructor overrides a constructor in its base class.
|
||||
// This only makes sense when the overridden constructor is abstract.
|
||||
checkMethodOverrides(CD);
|
||||
|
||||
// Determine whether this constructor is abstract.
|
||||
bool isAbstract = CD->getAttrs().isAbstract() ||
|
||||
(CD->getOverriddenDecl() && CD->getOverriddenDecl()->isAbstract());
|
||||
CD->setAbstract(isAbstract);
|
||||
}
|
||||
|
||||
void visitDestructorDecl(DestructorDecl *DD) {
|
||||
@@ -3806,6 +3882,36 @@ static void validateAttributes(TypeChecker &TC, Decl *D) {
|
||||
D->getMutableAttrs().clearAttribute(AK_requires_stored_property_inits);
|
||||
}
|
||||
|
||||
if (Attrs.isAbstract()) {
|
||||
// The abstract attribute only applies to constructors.
|
||||
if (auto ctor = dyn_cast<ConstructorDecl>(D)) {
|
||||
if (auto parentTy = ctor->getExtensionType()) {
|
||||
// Only classes can have abstract constructors.
|
||||
if (parentTy->getClassOrBoundGenericClass()) {
|
||||
// The constructor must be declared within the class itself.
|
||||
if (!isa<ClassDecl>(ctor->getDeclContext())) {
|
||||
TC.diagnose(ctor, diag::abstract_initializer_in_extension, parentTy)
|
||||
.highlight(Attrs.getLoc(AK_abstract));
|
||||
D->getMutableAttrs().clearAttribute(AK_abstract);
|
||||
}
|
||||
} else {
|
||||
if (!parentTy->is<ErrorType>()) {
|
||||
TC.diagnose(ctor, diag::abstract_initializer_nonclass, parentTy)
|
||||
.highlight(Attrs.getLoc(AK_abstract));
|
||||
}
|
||||
D->getMutableAttrs().clearAttribute(AK_abstract);
|
||||
}
|
||||
} else {
|
||||
// Constructor outside of nominal type context; just clear the
|
||||
// attribute; we've already complained elsewhere.
|
||||
D->getMutableAttrs().clearAttribute(AK_abstract);
|
||||
}
|
||||
} else {
|
||||
TC.diagnose(Attrs.getLoc(AK_abstract), diag::abstract_non_initializer);
|
||||
D->getMutableAttrs().clearAttribute(AK_abstract);
|
||||
}
|
||||
}
|
||||
|
||||
static const AttrKind InvalidAttrs[] = {
|
||||
AK_exported, AK_noreturn
|
||||
};
|
||||
|
||||
@@ -1414,13 +1414,16 @@ Decl *ModuleFile::getDecl(DeclID DID, Optional<DeclContext *> ForcedContext,
|
||||
case decls_block::CONSTRUCTOR_DECL: {
|
||||
DeclID parentID;
|
||||
bool isImplicit, hasSelectorStyleSignature, isObjC, isTransparent;
|
||||
bool isAbstract;
|
||||
TypeID signatureID;
|
||||
TypeID interfaceID;
|
||||
DeclID overriddenID;
|
||||
|
||||
decls_block::ConstructorLayout::readRecord(scratch, parentID, isImplicit,
|
||||
hasSelectorStyleSignature,
|
||||
isObjC, isTransparent,
|
||||
signatureID, interfaceID);
|
||||
isAbstract, signatureID,
|
||||
interfaceID, overriddenID);
|
||||
auto parent = getDeclContext(parentID);
|
||||
if (declOrOffset.isComplete())
|
||||
break;
|
||||
@@ -1490,6 +1493,11 @@ Decl *ModuleFile::getDecl(DeclID DID, Optional<DeclContext *> ForcedContext,
|
||||
ctor->setIsObjC(isObjC);
|
||||
if (isTransparent)
|
||||
ctor->getMutableAttrs().setAttr(AK_transparent, SourceLoc());
|
||||
if (isAbstract)
|
||||
ctor->setAbstract(true);
|
||||
if (auto overridden
|
||||
= dyn_cast_or_null<ConstructorDecl>(getDecl(overriddenID)))
|
||||
ctor->setOverriddenDecl(overridden);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1493,8 +1493,10 @@ void Serializer::writeDecl(const Decl *D) {
|
||||
ctor->hasSelectorStyleSignature(),
|
||||
ctor->isObjC(),
|
||||
ctor->isTransparent(),
|
||||
ctor->isAbstract(),
|
||||
addTypeRef(ctor->getType()),
|
||||
addTypeRef(ctor->getInterfaceType()));
|
||||
addTypeRef(ctor->getInterfaceType()),
|
||||
addDeclRef(ctor->getOverriddenDecl()));
|
||||
|
||||
writeGenericParams(ctor->getGenericParams(), DeclTypeAbbrCodes);
|
||||
assert(ctor->getArgParamPatterns().size() == 2);
|
||||
|
||||
47
test/basic/attr/attr_abstract.swift
Normal file
47
test/basic/attr/attr_abstract.swift
Normal file
@@ -0,0 +1,47 @@
|
||||
// RUN: %swift -parse %s -verify
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Restrictions on where @abstract can appear
|
||||
// -------------------------------------------------------------------------
|
||||
@abstract class AC { } // expected-error{{'abstract' attribute can only be applied to an initializer}}
|
||||
|
||||
class C {
|
||||
@abstract init withString(s: String) { } // expected-note{{'abstract' initializer with type '(withString: String)' not overridden}}
|
||||
|
||||
@abstract var s: String // expected-error{{'abstract' attribute can only be applied to an initializer}}
|
||||
|
||||
@abstract func f() { } // expected-error{{'abstract' attribute can only be applied to an initializer}}
|
||||
}
|
||||
|
||||
struct S {
|
||||
@abstract init() { } // expected-error{{'abstract' initializer in non-class type 'S'}}
|
||||
}
|
||||
|
||||
enum E {
|
||||
@abstract init() { } // expected-error{{'abstract' initializer in non-class type 'E'}}
|
||||
}
|
||||
|
||||
extension C {
|
||||
@abstract init withString(s: String) { } // expected-error{{'abstract' initializer must be declared directly in class 'C' (not in an extension)}}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Subclass requirements for @abstract
|
||||
// -------------------------------------------------------------------------
|
||||
class C2 : C { // expected-error{{class 'C2' does not implement its superclass's abstract members}}
|
||||
}
|
||||
|
||||
class C3 : C {
|
||||
@abstract init withString(s: String) { }
|
||||
}
|
||||
|
||||
class C4 : C3 {
|
||||
// implicitly @abstract
|
||||
init withString(s: String) { } // expected-note{{'abstract' initializer with type '(withString: String)' not overridden}}
|
||||
}
|
||||
|
||||
class C5 : C4 { // expected-error{{class 'C5' does not implement its superclass's abstract members}}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user