Sema: Move constructor synthesis code from TypeCheckDecl.cpp to CodeSynthesis.cpp

This was split between the two files; consolidate it in CodeSynthesis.cpp.
This commit is contained in:
Slava Pestov
2019-08-06 17:52:28 -04:00
parent 18ddb959f2
commit c3b6fd1d55
4 changed files with 628 additions and 603 deletions

View File

@@ -18,16 +18,17 @@
#include "ConstraintSystem.h"
#include "TypeChecker.h"
#include "TypeCheckDecl.h"
#include "TypeCheckObjC.h"
#include "TypeCheckType.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/ASTPrinter.h"
#include "swift/AST/Availability.h"
#include "swift/AST/Expr.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/GenericSignatureBuilder.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/PropertyWrappers.h"
#include "swift/AST/PrettyStackTrace.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/Basic/Defer.h"
@@ -169,6 +170,18 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var,
arg->setDefaultArgumentKind(DefaultArgumentKind::StoredProperty);
}
/// Describes the kind of implicit constructor that will be
/// generated.
enum class ImplicitConstructorKind {
/// The default constructor, which default-initializes each
/// of the instance variables.
Default,
/// The memberwise constructor, which initializes each of
/// the instance variables from a parameter of the same type and
/// name.
Memberwise
};
/// Create an implicit struct or class constructor.
///
/// \param decl The struct or class for which a constructor will be created.
@@ -176,12 +189,11 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var,
///
/// \returns The newly-created constructor, which has already been type-checked
/// (but has not been added to the containing struct or class).
ConstructorDecl *swift::createImplicitConstructor(TypeChecker &tc,
NominalTypeDecl *decl,
ImplicitConstructorKind ICK) {
static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
ImplicitConstructorKind ICK,
ASTContext &ctx) {
assert(!decl->hasClangNode());
ASTContext &ctx = tc.Context;
SourceLoc Loc = decl->getLoc();
auto accessLevel = AccessLevel::Internal;
@@ -201,7 +213,7 @@ ConstructorDecl *swift::createImplicitConstructor(TypeChecker &tc,
accessLevel = std::min(accessLevel, var->getFormalAccess());
tc.validateDecl(var);
ctx.getLazyResolver()->resolveDeclSignature(var);
auto varInterfaceType = var->getValueInterfaceType();
if (var->getAttrs().hasAttribute<LazyAttr>()) {
@@ -444,12 +456,11 @@ configureGenericDesignatedInitOverride(ASTContext &ctx,
}
static void
configureInheritedDesignatedInitAttributes(TypeChecker &tc,
ClassDecl *classDecl,
configureInheritedDesignatedInitAttributes(ClassDecl *classDecl,
ConstructorDecl *ctor,
ConstructorDecl *superclassCtor) {
ConstructorDecl *superclassCtor,
ASTContext &ctx) {
assert(ctor->getDeclContext() == classDecl);
auto &ctx = tc.Context;
AccessLevel access = classDecl->getFormalAccess();
access = std::max(access, AccessLevel::Internal);
@@ -579,13 +590,35 @@ synthesizeDesignatedInitOverride(AbstractFunctionDecl *fn, void *context) {
/*isTypeChecked=*/true };
}
ConstructorDecl *
swift::createDesignatedInitOverride(TypeChecker &tc,
ClassDecl *classDecl,
ConstructorDecl *superclassCtor,
DesignatedInitKind kind) {
auto &ctx = tc.Context;
/// The kind of designated initializer to synthesize.
enum class DesignatedInitKind {
/// A stub initializer, which is not visible to name lookup and
/// merely aborts at runtime.
Stub,
/// An initializer that simply chains to the corresponding
/// superclass initializer.
Chaining
};
/// Create a new initializer that overrides the given designated
/// initializer.
///
/// \param classDecl The subclass in which the new initializer will
/// be declared.
///
/// \param superclassCtor The superclass initializer for which this
/// routine will create an override.
///
/// \param kind The kind of initializer to synthesize.
///
/// \returns the newly-created initializer that overrides \p
/// superclassCtor.
static ConstructorDecl *
createDesignatedInitOverride(ClassDecl *classDecl,
ConstructorDecl *superclassCtor,
DesignatedInitKind kind,
ASTContext &ctx) {
// Lookup will sometimes give us initializers that are from the ancestors of
// our immediate superclass. So, from the superclass constructor, we look
// one level up to the enclosing type context which will either be a class
@@ -655,8 +688,8 @@ swift::createDesignatedInitOverride(TypeChecker &tc,
ctor->setValidationToChecked();
configureInheritedDesignatedInitAttributes(tc, classDecl, ctor,
superclassCtor);
configureInheritedDesignatedInitAttributes(classDecl, ctor,
superclassCtor, ctx);
if (kind == DesignatedInitKind::Stub) {
// Make this a stub implementation.
@@ -676,3 +709,532 @@ swift::createDesignatedInitOverride(TypeChecker &tc,
return ctor;
}
/// Diagnose a missing required initializer.
static void diagnoseMissingRequiredInitializer(
ClassDecl *classDecl,
ConstructorDecl *superInitializer,
ASTContext &ctx) {
// Find the location at which we should insert the new initializer.
SourceLoc insertionLoc;
SourceLoc indentationLoc;
for (auto member : classDecl->getMembers()) {
// If we don't have an indentation location yet, grab one from this
// member.
if (indentationLoc.isInvalid()) {
indentationLoc = member->getLoc();
}
// We only want to look at explicit constructors.
auto ctor = dyn_cast<ConstructorDecl>(member);
if (!ctor)
continue;
if (ctor->isImplicit())
continue;
insertionLoc = ctor->getEndLoc();
indentationLoc = ctor->getLoc();
}
// If no initializers were listed, start at the opening '{' for the class.
if (insertionLoc.isInvalid()) {
insertionLoc = classDecl->getBraces().Start;
}
if (indentationLoc.isInvalid()) {
indentationLoc = classDecl->getBraces().End;
}
// Adjust the insertion location to point at the end of this line (i.e.,
// the start of the next line).
insertionLoc = Lexer::getLocForEndOfLine(ctx.SourceMgr,
insertionLoc);
// Find the indentation used on the indentation line.
StringRef extraIndentation;
StringRef indentation = Lexer::getIndentationForLine(
ctx.SourceMgr, indentationLoc, &extraIndentation);
// Pretty-print the superclass initializer into a string.
// FIXME: Form a new initializer by performing the appropriate
// substitutions of subclass types into the superclass types, so that
// we get the right generic parameters.
std::string initializerText;
{
PrintOptions options;
options.PrintImplicitAttrs = false;
// Render the text.
llvm::raw_string_ostream out(initializerText);
{
ExtraIndentStreamPrinter printer(out, indentation);
printer.printNewline();
// If there is no explicit 'required', print one.
bool hasExplicitRequiredAttr = false;
if (auto requiredAttr
= superInitializer->getAttrs().getAttribute<RequiredAttr>())
hasExplicitRequiredAttr = !requiredAttr->isImplicit();
if (!hasExplicitRequiredAttr)
printer << "required ";
superInitializer->print(printer, options);
}
// Add a dummy body.
out << " {\n";
out << indentation << extraIndentation << "fatalError(\"";
superInitializer->getFullName().printPretty(out);
out << " has not been implemented\")\n";
out << indentation << "}\n";
}
// Complain.
ctx.Diags.diagnose(insertionLoc, diag::required_initializer_missing,
superInitializer->getFullName(),
superInitializer->getDeclContext()->getDeclaredInterfaceType())
.fixItInsert(insertionLoc, initializerText);
ctx.Diags.diagnose(findNonImplicitRequiredInit(superInitializer),
diag::required_initializer_here);
}
void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
// We can only synthesize implicit constructors for classes and structs.
if (!isa<ClassDecl>(decl) && !isa<StructDecl>(decl))
return;
// If we already added implicit initializers, we're done.
if (decl->addedImplicitInitializers())
return;
// Don't add implicit constructors for an invalid declaration
if (decl->isInvalid())
return;
// Don't add implicit constructors in parseable interfaces.
if (auto *SF = decl->getParentSourceFile()) {
if (SF->Kind == SourceFileKind::Interface) {
decl->setAddedImplicitInitializers();
return;
}
}
// Bail out if we're validating one of our constructors or stored properties
// already; we'll revisit the issue later.
if (isa<ClassDecl>(decl)) {
for (auto member : decl->getMembers()) {
if (auto ctor = dyn_cast<ConstructorDecl>(member)) {
validateDecl(ctor);
if (!ctor->hasValidSignature())
return;
}
}
}
if (isa<StructDecl>(decl)) {
for (auto member : decl->getMembers()) {
if (auto var = dyn_cast<VarDecl>(member)) {
if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
continue;
validateDecl(var);
if (!var->hasValidSignature())
return;
}
}
}
decl->setAddedImplicitInitializers();
// Check whether there is a user-declared constructor or an instance
// variable.
bool FoundMemberwiseInitializedProperty = false;
bool SuppressDefaultInitializer = false;
bool FoundDesignatedInit = false;
SmallVector<std::pair<ValueDecl *, Type>, 4> declaredInitializers;
llvm::SmallPtrSet<ConstructorDecl *, 4> overriddenInits;
if (decl->hasClangNode() && isa<ClassDecl>(decl)) {
// Objective-C classes may have interesting initializers in extensions.
for (auto member : decl->lookupDirect(DeclBaseName::createConstructor())) {
auto ctor = dyn_cast<ConstructorDecl>(member);
if (!ctor)
continue;
// Swift initializers added in extensions of Objective-C classes can never
// be overrides.
if (!ctor->hasClangNode())
continue;
if (auto overridden = ctor->getOverriddenDecl())
overriddenInits.insert(overridden);
}
} else {
for (auto member : decl->getMembers()) {
if (auto ctor = dyn_cast<ConstructorDecl>(member)) {
// Initializers that were synthesized to fulfill derived conformances
// should not prevent default initializer synthesis.
if (ctor->isDesignatedInit() && !ctor->isSynthesized())
FoundDesignatedInit = true;
if (isa<StructDecl>(decl))
continue;
if (!ctor->isInvalid()) {
auto type = getMemberTypeForComparison(Context, ctor, nullptr);
declaredInitializers.push_back({ctor, type});
}
if (auto overridden = ctor->getOverriddenDecl())
overriddenInits.insert(overridden);
continue;
}
if (auto var = dyn_cast<VarDecl>(member)) {
// If this is a backing storage property for a property wrapper,
// skip it.
if (var->getOriginalWrappedProperty())
continue;
if (var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) {
// Initialized 'let' properties have storage, but don't get an argument
// to the memberwise initializer since they already have an initial
// value that cannot be overridden.
if (var->isLet() && var->isParentInitialized()) {
// We cannot handle properties like:
// let (a,b) = (1,2)
// for now, just disable implicit init synthesization in structs in
// this case.
auto SP = var->getParentPattern();
if (auto *TP = dyn_cast<TypedPattern>(SP))
SP = TP->getSubPattern();
if (!isa<NamedPattern>(SP) && isa<StructDecl>(decl))
return;
continue;
}
FoundMemberwiseInitializedProperty = true;
}
continue;
}
// If a stored property lacks an initial value and if there is no way to
// synthesize an initial value (e.g. for an optional) then we suppress
// generation of the default initializer.
if (auto pbd = dyn_cast<PatternBindingDecl>(member)) {
if (pbd->hasStorage() && !pbd->isStatic()) {
for (auto entry : pbd->getPatternList()) {
if (entry.isInitialized()) continue;
// If one of the bound variables is @NSManaged, go ahead no matter
// what.
bool CheckDefaultInitializer = true;
entry.getPattern()->forEachVariable([&](VarDecl *vd) {
if (vd->getAttrs().hasAttribute<NSManagedAttr>())
CheckDefaultInitializer = false;
});
// If we cannot default initialize the property, we cannot
// synthesize a default initializer for the class.
if (CheckDefaultInitializer && !pbd->isDefaultInitializable())
SuppressDefaultInitializer = true;
}
}
continue;
}
}
}
if (auto structDecl = dyn_cast<StructDecl>(decl)) {
assert(!structDecl->hasUnreferenceableStorage() &&
"User-defined structs cannot have unreferenceable storage");
if (!FoundDesignatedInit) {
// For a struct with memberwise initialized properties, we add a
// memberwise init.
if (FoundMemberwiseInitializedProperty) {
// Create the implicit memberwise constructor.
auto ctor = createImplicitConstructor(
decl,
ImplicitConstructorKind::Memberwise,
Context);
decl->addMember(ctor);
}
// If we found a stored property, add a default constructor.
if (!SuppressDefaultInitializer)
defineDefaultConstructor(decl);
}
return;
}
// For a class with a superclass, automatically define overrides
// for all of the superclass's designated initializers.
// FIXME: Currently skipping generic classes.
auto classDecl = cast<ClassDecl>(decl);
if (Type superclassTy = classDecl->getSuperclass()) {
bool canInheritInitializers = (!SuppressDefaultInitializer &&
!FoundDesignatedInit);
// We can't define these overrides if we have any uninitialized
// stored properties.
if (SuppressDefaultInitializer && !FoundDesignatedInit &&
!classDecl->hasClangNode()) {
return;
}
auto *superclassDecl = superclassTy->getClassOrBoundGenericClass();
assert(superclassDecl && "Superclass of class is not a class?");
if (!superclassDecl->addedImplicitInitializers())
addImplicitConstructors(superclassDecl);
auto ctors = lookupConstructors(classDecl, superclassTy,
NameLookupFlags::IgnoreAccessControl);
bool canInheritConvenienceInitalizers =
!superclassDecl->hasMissingDesignatedInitializers();
SmallVector<ConstructorDecl *, 4> requiredConvenienceInitializers;
for (auto memberResult : ctors) {
auto member = memberResult.getValueDecl();
// Skip unavailable superclass initializers.
if (AvailableAttr::isUnavailable(member))
continue;
// Skip invalid superclass initializers.
auto superclassCtor = dyn_cast<ConstructorDecl>(member);
if (superclassCtor->isInvalid())
continue;
// If we have an override for this constructor, it's okay.
if (overriddenInits.count(superclassCtor) > 0)
continue;
// We only care about required or designated initializers.
if (!superclassCtor->isDesignatedInit()) {
if (superclassCtor->isRequired()) {
assert(superclassCtor->isInheritable() &&
"factory initializers cannot be 'required'");
requiredConvenienceInitializers.push_back(superclassCtor);
}
continue;
}
// Otherwise, it may no longer be safe to inherit convenience
// initializers.
canInheritConvenienceInitalizers &= canInheritInitializers;
// Everything after this is only relevant for Swift classes being defined.
if (classDecl->hasClangNode())
continue;
// If the superclass initializer is not accessible from the derived
// class, don't synthesize an override, since we cannot reference the
// superclass initializer's method descriptor at all.
//
// FIXME: This should be checked earlier as part of calculating
// canInheritInitializers.
if (!superclassCtor->isAccessibleFrom(classDecl))
continue;
// Diagnose a missing override of a required initializer.
if (superclassCtor->isRequired() && !canInheritInitializers) {
diagnoseMissingRequiredInitializer(classDecl, superclassCtor, Context);
continue;
}
// A designated or required initializer has not been overridden.
bool alreadyDeclared = false;
for (const auto &ctorAndType : declaredInitializers) {
auto *ctor = ctorAndType.first;
auto type = ctorAndType.second;
auto parentType = getMemberTypeForComparison(
Context, superclassCtor, ctor);
if (isOverrideBasedOnType(ctor, type, superclassCtor, parentType)) {
alreadyDeclared = true;
break;
}
}
// If we have already introduced an initializer with this parameter type,
// don't add one now.
if (alreadyDeclared)
continue;
// If we're inheriting initializers, create an override delegating
// to 'super.init'. Otherwise, create a stub which traps at runtime.
auto kind = canInheritInitializers
? DesignatedInitKind::Chaining
: DesignatedInitKind::Stub;
// We have a designated initializer. Create an override of it.
// FIXME: Validation makes sure we get a generic signature here.
validateDecl(classDecl);
if (auto ctor = createDesignatedInitOverride(
classDecl, superclassCtor, kind, Context)) {
classDecl->addMember(ctor);
}
}
if (canInheritConvenienceInitalizers) {
classDecl->setInheritsSuperclassInitializers();
} else {
for (ConstructorDecl *requiredCtor : requiredConvenienceInitializers)
diagnoseMissingRequiredInitializer(classDecl, requiredCtor, Context);
}
return;
}
if (!FoundDesignatedInit) {
// For a class with no superclass, automatically define a default
// constructor.
// ... unless there are uninitialized stored properties.
if (SuppressDefaultInitializer)
return;
defineDefaultConstructor(decl);
}
}
void TypeChecker::synthesizeMemberForLookup(NominalTypeDecl *target,
DeclName member) {
auto baseName = member.getBaseName();
// Checks whether the target conforms to the given protocol. If the
// conformance is incomplete, force the conformance.
//
// Returns whether the target conforms to the protocol.
auto evaluateTargetConformanceTo = [&](ProtocolDecl *protocol) {
if (!protocol)
return false;
auto targetType = target->getDeclaredInterfaceType();
if (auto ref = conformsToProtocol(
targetType, protocol, target,
ConformanceCheckFlags::SkipConditionalRequirements)) {
if (auto *conformance = dyn_cast<NormalProtocolConformance>(
ref->getConcrete()->getRootConformance())) {
if (conformance->getState() == ProtocolConformanceState::Incomplete) {
checkConformance(conformance);
}
}
return true;
}
return false;
};
if (member.isSimpleName() && !baseName.isSpecial()) {
if (baseName.getIdentifier() == Context.Id_CodingKeys) {
// CodingKeys is a special type which may be synthesized as part of
// Encodable/Decodable conformance. If the target conforms to either
// protocol and would derive conformance to either, the type may be
// synthesized.
// If the target conforms to either and the conformance has not yet been
// evaluated, then we should do that here.
//
// Try to synthesize Decodable first. If that fails, try to synthesize
// Encodable. If either succeeds and CodingKeys should have been
// synthesized, it will be synthesized.
auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable);
auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable);
if (!evaluateTargetConformanceTo(decodableProto))
(void)evaluateTargetConformanceTo(encodableProto);
}
if ((baseName.getIdentifier().str().startswith("$") ||
baseName.getIdentifier().str().startswith("_")) &&
baseName.getIdentifier().str().size() > 1) {
// $- and _-prefixed variables can be generated by properties that have
// attached property wrappers.
auto originalPropertyName =
Context.getIdentifier(baseName.getIdentifier().str().substr(1));
for (auto member : target->lookupDirect(originalPropertyName)) {
if (auto var = dyn_cast<VarDecl>(member)) {
if (var->hasAttachedPropertyWrapper()) {
auto sourceFile = var->getDeclContext()->getParentSourceFile();
if (sourceFile && sourceFile->Kind != SourceFileKind::Interface)
(void)var->getPropertyWrapperBackingPropertyInfo();
}
}
}
}
} else {
auto argumentNames = member.getArgumentNames();
if (member.isCompoundName() && argumentNames.size() != 1)
return;
if (baseName == DeclBaseName::createConstructor() &&
(member.isSimpleName() || argumentNames.front() == Context.Id_from)) {
// init(from:) may be synthesized as part of derived conformance to the
// Decodable protocol.
// If the target should conform to the Decodable protocol, check the
// conformance here to attempt synthesis.
auto *decodableProto = Context.getProtocol(KnownProtocolKind::Decodable);
(void)evaluateTargetConformanceTo(decodableProto);
} else if (!baseName.isSpecial() &&
baseName.getIdentifier() == Context.Id_encode &&
(member.isSimpleName() ||
argumentNames.front() == Context.Id_to)) {
// encode(to:) may be synthesized as part of derived conformance to the
// Encodable protocol.
// If the target should conform to the Encodable protocol, check the
// conformance here to attempt synthesis.
auto *encodableProto = Context.getProtocol(KnownProtocolKind::Encodable);
(void)evaluateTargetConformanceTo(encodableProto);
}
}
}
/// Synthesizer callback for a function body consisting of "return".
static std::pair<BraceStmt *, bool>
synthesizeSingleReturnFunctionBody(AbstractFunctionDecl *afd, void *) {
ASTContext &ctx = afd->getASTContext();
SmallVector<ASTNode, 1> stmts;
stmts.push_back(new (ctx) ReturnStmt(afd->getLoc(), nullptr));
return { BraceStmt::create(ctx, afd->getLoc(), stmts, afd->getLoc(), true),
/*isTypeChecked=*/true };
}
void TypeChecker::defineDefaultConstructor(NominalTypeDecl *decl) {
FrontendStatsTracer StatsTracer(Context.Stats, "define-default-ctor", decl);
PrettyStackTraceDecl stackTrace("defining default constructor for",
decl);
// Clang-imported types should never get a default constructor, just a
// memberwise one.
if (decl->hasClangNode())
return;
// A class is only default initializable if it's a root class.
if (auto *classDecl = dyn_cast<ClassDecl>(decl)) {
// If the class has a superclass, we should have either inherited it's
// designated initializers or diagnosed the absence of our own.
if (classDecl->getSuperclass())
return;
}
// Create the default constructor.
auto ctor = createImplicitConstructor(decl,
ImplicitConstructorKind::Default,
Context);
// Add the constructor.
decl->addMember(ctor);
// Lazily synthesize an empty body for the default constructor.
ctor->setBodySynthesizer(synthesizeSingleReturnFunctionBody);
}