Implement semantic analysis for abstract initializers.

Swift SVN r14221
This commit is contained in:
Doug Gregor
2014-02-21 19:41:14 +00:00
parent af33bfd135
commit 0da11a8b0b
14 changed files with 269 additions and 21 deletions

View File

@@ -55,6 +55,7 @@ TYPE_ATTR(callee_guaranteed)
TYPE_ATTR(objc_metatype)
TYPE_ATTR(opened)
ATTR(abstract)
ATTR(assignment)
ATTR(class_protocol)
ATTR(conversion)

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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.
>;

View File

@@ -592,6 +592,9 @@ namespace {
void visitConstructorDecl(ConstructorDecl *CD) {
printCommonAFD(CD, "constructor_decl");
if (CD->isAbstract())
OS << " abstract";
printAbstractFunctionDecl(CD);
OS << ')';
}

View File

@@ -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());

View File

@@ -82,4 +82,6 @@ void DeclAttributes::print(ASTPrinter &Printer) const {
Printer << "@mutating ";
if (MutatingAttr && !MutatingAttr.getValue())
Printer << "@!mutating ";
if (isAbstract())
Printer << "@abstract ";
}

View File

@@ -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)

View File

@@ -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.

View File

@@ -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
};

View File

@@ -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;
}

View File

@@ -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);

View 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}}
}