mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This adds the declaration to the external declarations list only if it was part of an imported declaration, eliminating some boilerplate.
2181 lines
87 KiB
C++
2181 lines
87 KiB
C++
//===--- CodeSynthesis.cpp - Type Checking for Declarations ---------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements semantic analysis for declarations.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "CodeSynthesis.h"
|
|
|
|
#include "ConstraintSystem.h"
|
|
#include "TypeChecker.h"
|
|
#include "swift/AST/ASTWalker.h"
|
|
#include "swift/AST/Availability.h"
|
|
#include "swift/AST/Expr.h"
|
|
#include "swift/AST/GenericEnvironment.h"
|
|
#include "swift/AST/Initializer.h"
|
|
#include "swift/AST/ParameterList.h"
|
|
#include "swift/AST/ProtocolConformance.h"
|
|
#include "swift/Basic/Defer.h"
|
|
#include "swift/ClangImporter/ClangModule.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
using namespace swift;
|
|
|
|
const bool IsImplicit = true;
|
|
|
|
/// Insert the specified decl into the DeclContext's member list. If the hint
|
|
/// decl is specified, the new decl is inserted next to the hint.
|
|
static void addMemberToContextIfNeeded(Decl *D, DeclContext *DC,
|
|
Decl *Hint = nullptr) {
|
|
if (auto *ntd = dyn_cast<NominalTypeDecl>(DC)) {
|
|
ntd->addMember(D, Hint);
|
|
} else if (auto *ed = dyn_cast<ExtensionDecl>(DC)) {
|
|
ed->addMember(D, Hint);
|
|
} else {
|
|
assert((isa<AbstractFunctionDecl>(DC) || isa<FileUnit>(DC)) &&
|
|
"Unknown declcontext");
|
|
}
|
|
}
|
|
|
|
static ParamDecl *getParamDeclAtIndex(FuncDecl *fn, unsigned index) {
|
|
return fn->getParameterLists().back()->get(index);
|
|
}
|
|
|
|
static VarDecl *getFirstParamDecl(FuncDecl *fn) {
|
|
return getParamDeclAtIndex(fn, 0);
|
|
};
|
|
|
|
|
|
static ParamDecl *buildArgument(SourceLoc loc, DeclContext *DC,
|
|
StringRef name,
|
|
Type type,
|
|
Type interfaceType,
|
|
VarDecl::Specifier specifier) {
|
|
auto &context = DC->getASTContext();
|
|
auto *param = new (context) ParamDecl(specifier, SourceLoc(), SourceLoc(),
|
|
Identifier(), loc,
|
|
context.getIdentifier(name),
|
|
type, DC);
|
|
param->setImplicit();
|
|
param->setInterfaceType(interfaceType);
|
|
return param;
|
|
}
|
|
|
|
static Type getTypeOfStorage(AbstractStorageDecl *storage,
|
|
bool wantInterfaceType) {
|
|
if (auto var = dyn_cast<VarDecl>(storage)) {
|
|
auto type = (wantInterfaceType
|
|
? var->getInterfaceType()
|
|
: var->getType());
|
|
return type->getReferenceStorageReferent();
|
|
}
|
|
|
|
auto subscript = cast<SubscriptDecl>(storage);
|
|
auto type = subscript->getElementInterfaceType();
|
|
if (!wantInterfaceType)
|
|
type = storage->getDeclContext()->mapTypeIntoContext(type);
|
|
return type;
|
|
}
|
|
|
|
/// Build a parameter list which can forward the formal index parameters of a
|
|
/// declaration.
|
|
///
|
|
/// \param prefix optional arguments to be prefixed onto the index
|
|
/// forwarding pattern.
|
|
static ParameterList *
|
|
buildIndexForwardingParamList(AbstractStorageDecl *storage,
|
|
ArrayRef<ParamDecl*> prefix) {
|
|
auto &context = storage->getASTContext();
|
|
auto subscript = dyn_cast<SubscriptDecl>(storage);
|
|
|
|
// Fast path: if this isn't a subscript, just use whatever we have.
|
|
if (!subscript)
|
|
return ParameterList::create(context, prefix);
|
|
|
|
// Clone the parameter list over for a new decl, so we get new ParamDecls.
|
|
auto indices = subscript->getIndices()->clone(context,
|
|
ParameterList::Implicit|
|
|
ParameterList::WithoutTypes);
|
|
if (prefix.empty())
|
|
return indices;
|
|
|
|
|
|
// Otherwise, we need to build up a new parameter list.
|
|
SmallVector<ParamDecl*, 4> elements;
|
|
|
|
// Start with the fields we were given, if there are any.
|
|
elements.append(prefix.begin(), prefix.end());
|
|
elements.append(indices->begin(), indices->end());
|
|
return ParameterList::create(context, elements);
|
|
}
|
|
|
|
static AccessorDecl *createGetterPrototype(AbstractStorageDecl *storage,
|
|
TypeChecker &TC) {
|
|
SourceLoc loc = storage->getLoc();
|
|
|
|
// Create the parameter list for the getter.
|
|
SmallVector<ParameterList*, 2> getterParams;
|
|
|
|
// The implicit 'self' argument if in a type context.
|
|
if (storage->getDeclContext()->isTypeContext()) {
|
|
ParamDecl *selfDecl;
|
|
|
|
// For lazy properties, steal the 'self' from the initializer context.
|
|
if (storage->getAttrs().hasAttribute<LazyAttr>()) {
|
|
auto *varDecl = cast<VarDecl>(storage);
|
|
auto *bindingDecl = varDecl->getParentPatternBinding();
|
|
auto *bindingInit = cast<PatternBindingInitializer>(
|
|
bindingDecl->getPatternEntryForVarDecl(varDecl).getInitContext());
|
|
|
|
selfDecl = bindingInit->getImplicitSelfDecl();
|
|
} else {
|
|
selfDecl = ParamDecl::createSelf(loc,
|
|
storage->getDeclContext(),
|
|
/*isStatic*/false);
|
|
}
|
|
|
|
getterParams.push_back(ParameterList::create(TC.Context, selfDecl));
|
|
}
|
|
|
|
// Add an index-forwarding clause.
|
|
getterParams.push_back(buildIndexForwardingParamList(storage, {}));
|
|
|
|
SourceLoc staticLoc;
|
|
if (auto var = dyn_cast<VarDecl>(storage)) {
|
|
if (var->isStatic())
|
|
staticLoc = var->getLoc();
|
|
}
|
|
|
|
auto storageInterfaceType = getTypeOfStorage(storage, true);
|
|
|
|
auto getter = AccessorDecl::create(
|
|
TC.Context, loc, /*AccessorKeywordLoc*/ loc,
|
|
AccessorKind::IsGetter, AddressorKind::NotAddressor, storage,
|
|
staticLoc, StaticSpellingKind::None,
|
|
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
|
|
/*GenericParams=*/nullptr,
|
|
getterParams, TypeLoc::withoutLoc(storageInterfaceType),
|
|
storage->getDeclContext());
|
|
getter->setImplicit();
|
|
|
|
if (storage->isGetterMutating())
|
|
getter->setSelfAccessKind(SelfAccessKind::Mutating);
|
|
|
|
if (storage->isStatic())
|
|
getter->setStatic();
|
|
|
|
if (auto *overridden = storage->getOverriddenDecl())
|
|
if (auto *overriddenAccessor = overridden->getGetter())
|
|
getter->setOverriddenDecl(overriddenAccessor);
|
|
|
|
return getter;
|
|
}
|
|
|
|
static AccessorDecl *createSetterPrototype(AbstractStorageDecl *storage,
|
|
ParamDecl *&valueDecl,
|
|
TypeChecker &TC) {
|
|
SourceLoc loc = storage->getLoc();
|
|
|
|
// Create the parameter list for the setter.
|
|
SmallVector<ParameterList*, 2> params;
|
|
|
|
bool isStatic = storage->isStatic();
|
|
bool isMutating = storage->isSetterMutating();
|
|
|
|
// The implicit 'self' argument if in a type context.
|
|
if (storage->getDeclContext()->isTypeContext()) {
|
|
params.push_back(ParameterList::createSelf(loc,
|
|
storage->getDeclContext(),
|
|
/*isStatic*/isStatic,
|
|
/*isInOut*/isMutating));
|
|
}
|
|
|
|
// Add a "(value : T, indices...)" argument list.
|
|
auto storageType = getTypeOfStorage(storage, false);
|
|
auto storageInterfaceType = getTypeOfStorage(storage, true);
|
|
valueDecl = buildArgument(storage->getLoc(), storage->getDeclContext(),
|
|
"value", storageType, storageInterfaceType,
|
|
VarDecl::Specifier::Default);
|
|
params.push_back(buildIndexForwardingParamList(storage, valueDecl));
|
|
|
|
Type setterRetTy = TupleType::getEmpty(TC.Context);
|
|
auto setter = AccessorDecl::create(
|
|
TC.Context, loc, /*AccessorKeywordLoc*/ SourceLoc(),
|
|
AccessorKind::IsSetter, AddressorKind::NotAddressor, storage,
|
|
/*StaticLoc=*/SourceLoc(), StaticSpellingKind::None,
|
|
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
|
|
/*GenericParams=*/nullptr, params, TypeLoc::withoutLoc(setterRetTy),
|
|
storage->getDeclContext());
|
|
setter->setImplicit();
|
|
|
|
if (isMutating)
|
|
setter->setSelfAccessKind(SelfAccessKind::Mutating);
|
|
|
|
if (isStatic)
|
|
setter->setStatic();
|
|
|
|
if (auto *overridden = storage->getOverriddenDecl()) {
|
|
auto *overriddenAccessor = overridden->getSetter();
|
|
if (overriddenAccessor &&
|
|
overridden->isSetterAccessibleFrom(storage->getDeclContext())) {
|
|
setter->setOverriddenDecl(overriddenAccessor);
|
|
}
|
|
}
|
|
|
|
return setter;
|
|
}
|
|
|
|
// True if the storage is dynamic or imported from Objective-C. In these cases,
|
|
// we need to emit a static materializeForSet thunk that dynamically dispatches
|
|
// to 'get' and 'set', rather than the normal dynamically dispatched
|
|
// materializeForSet that peer dispatches to 'get' and 'set'.
|
|
static bool needsDynamicMaterializeForSet(AbstractStorageDecl *storage) {
|
|
return storage->isDynamic() || storage->hasClangNode();
|
|
}
|
|
|
|
/// Mark the accessor as transparent if we can.
|
|
///
|
|
/// If the storage is inside a fixed-layout nominal type, we can mark the
|
|
/// accessor as transparent, since in this case we just want it for abstraction
|
|
/// purposes (i.e., to make access to the variable uniform and to be able to
|
|
/// put the getter in a vtable).
|
|
///
|
|
/// If the storage is for a global stored property or a stored property of a
|
|
/// resilient type, we are synthesizing accessors to present a resilient
|
|
/// interface to the storage and they should not be transparent.
|
|
static void maybeMarkTransparent(FuncDecl *accessor,
|
|
AbstractStorageDecl *storage,
|
|
TypeChecker &TC) {
|
|
auto *DC = storage->getDeclContext();
|
|
auto *nominalDecl = DC->getAsNominalTypeOrNominalTypeExtensionContext();
|
|
|
|
// Global variable accessors are not @_transparent.
|
|
if (!nominalDecl)
|
|
return;
|
|
|
|
// Accessors for resilient properties are not @_transparent.
|
|
if (storage->isResilient())
|
|
return;
|
|
|
|
// Accessors for protocol storage requirements are never @_transparent
|
|
// since they do not have bodies.
|
|
//
|
|
// FIXME: Revisit this if we ever get 'real' default implementations.
|
|
if (isa<ProtocolDecl>(nominalDecl))
|
|
return;
|
|
|
|
// Accessors for classes with @objc ancestry are not @_transparent,
|
|
// since they use a field offset variable which is not exported.
|
|
if (auto *classDecl = dyn_cast<ClassDecl>(nominalDecl))
|
|
if (classDecl->checkObjCAncestry() != ObjCClassKind::NonObjC)
|
|
return;
|
|
|
|
accessor->getAttrs().add(new (TC.Context) TransparentAttr(IsImplicit));
|
|
}
|
|
|
|
static AccessorDecl *
|
|
createMaterializeForSetPrototype(AbstractStorageDecl *storage,
|
|
FuncDecl *getter,
|
|
FuncDecl *setter,
|
|
TypeChecker &TC) {
|
|
auto &ctx = storage->getASTContext();
|
|
SourceLoc loc = storage->getLoc();
|
|
|
|
// Create the parameter list:
|
|
SmallVector<ParameterList*, 2> params;
|
|
|
|
// - The implicit 'self' argument if in a type context.
|
|
auto DC = storage->getDeclContext();
|
|
if (DC->isTypeContext())
|
|
params.push_back(ParameterList::createSelf(loc, DC, /*isStatic*/false));
|
|
|
|
// - The buffer parameter, (buffer: Builtin.RawPointer,
|
|
// inout storage: Builtin.UnsafeValueBuffer,
|
|
// indices...).
|
|
ParamDecl *bufferElements[] = {
|
|
buildArgument(loc, DC, "buffer", ctx.TheRawPointerType,
|
|
ctx.TheRawPointerType, VarDecl::Specifier::Default),
|
|
buildArgument(loc, DC, "callbackStorage", ctx.TheUnsafeValueBufferType,
|
|
ctx.TheUnsafeValueBufferType, VarDecl::Specifier::InOut)};
|
|
params.push_back(buildIndexForwardingParamList(storage, bufferElements));
|
|
|
|
// The accessor returns (temporary: Builtin.RawPointer,
|
|
// callback: Optional<Builtin.RawPointer>),
|
|
// where the first pointer is the materialized address and the
|
|
// second is the address of an optional callback.
|
|
TupleTypeElt retElts[] = {
|
|
{ ctx.TheRawPointerType },
|
|
{ OptionalType::get(ctx.TheRawPointerType) },
|
|
};
|
|
Type retTy = TupleType::get(retElts, ctx);
|
|
|
|
// Accessors of generic subscripts get a copy of the subscript's
|
|
// generic parameter list, because they're not nested inside the
|
|
// subscript.
|
|
GenericParamList *genericParams = nullptr;
|
|
if (auto *subscript = dyn_cast<SubscriptDecl>(storage))
|
|
genericParams = subscript->getGenericParams();
|
|
|
|
auto *materializeForSet = AccessorDecl::create(
|
|
ctx, loc, /*AccessorKeywordLoc=*/SourceLoc(),
|
|
AccessorKind::IsMaterializeForSet, AddressorKind::NotAddressor, storage,
|
|
/*StaticLoc=*/SourceLoc(), StaticSpellingKind::None,
|
|
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
|
|
(genericParams
|
|
? genericParams->clone(DC)
|
|
: nullptr),
|
|
params, TypeLoc::withoutLoc(retTy), DC);
|
|
materializeForSet->setImplicit();
|
|
|
|
// Open-code the setMutating() calculation since we might run before
|
|
// the setter has been type checked.
|
|
Type contextTy = DC->getDeclaredInterfaceType();
|
|
if (contextTy && !contextTy->hasReferenceSemantics()) {
|
|
bool hasMutatingSetter =
|
|
!setter->getAttrs().hasAttribute<NonMutatingAttr>() &&
|
|
storage->isSetterMutating();
|
|
bool hasMutatingGetter =
|
|
getter->getAttrs().hasAttribute<MutatingAttr>();
|
|
if (hasMutatingSetter || hasMutatingGetter)
|
|
materializeForSet->setSelfAccessKind(SelfAccessKind::Mutating);
|
|
}
|
|
|
|
materializeForSet->setStatic(storage->isStatic());
|
|
|
|
// materializeForSet is final if the storage is.
|
|
if (storage->isFinal())
|
|
makeFinal(ctx, materializeForSet);
|
|
|
|
if (auto *overridden = storage->getOverriddenDecl()) {
|
|
auto *overriddenAccessor = overridden->getMaterializeForSetFunc();
|
|
if (overriddenAccessor && !overriddenAccessor->hasForcedStaticDispatch() &&
|
|
overridden->isSetterAccessibleFrom(storage->getDeclContext())) {
|
|
materializeForSet->setOverriddenDecl(overriddenAccessor);
|
|
}
|
|
}
|
|
|
|
// If the storage is dynamic or ObjC-native, we can't add a dynamically-
|
|
// dispatched method entry for materializeForSet, so force it to be
|
|
// statically dispatched. ("final" would be inappropriate because the
|
|
// property can still be overridden.)
|
|
if (needsDynamicMaterializeForSet(storage))
|
|
materializeForSet->setForcedStaticDispatch(true);
|
|
|
|
// Make sure materializeForSet is available enough to access
|
|
// the storage (and its getters/setters if it has them).
|
|
SmallVector<const Decl *, 2> asAvailableAs;
|
|
asAvailableAs.push_back(storage);
|
|
if (FuncDecl *getter = storage->getGetter()) {
|
|
asAvailableAs.push_back(getter);
|
|
}
|
|
if (FuncDecl *setter = storage->getSetter()) {
|
|
asAvailableAs.push_back(setter);
|
|
}
|
|
|
|
maybeMarkTransparent(materializeForSet, storage, TC);
|
|
|
|
AvailabilityInference::applyInferredAvailableAttrs(materializeForSet,
|
|
asAvailableAs, ctx);
|
|
|
|
TC.Context.addSynthesizedDecl(materializeForSet);
|
|
TC.DeclsToFinalize.insert(materializeForSet);
|
|
|
|
return materializeForSet;
|
|
}
|
|
|
|
static void convertStoredVarInProtocolToComputed(VarDecl *VD, TypeChecker &TC) {
|
|
auto *Get = createGetterPrototype(VD, TC);
|
|
|
|
// Okay, we have both the getter and setter. Set them in VD.
|
|
VD->makeComputed(SourceLoc(), Get, nullptr, nullptr, SourceLoc());
|
|
|
|
// We've added some members to our containing class, add them to the members
|
|
// list.
|
|
addMemberToContextIfNeeded(Get, VD->getDeclContext(), VD);
|
|
}
|
|
|
|
|
|
/// Build an expression that evaluates the specified parameter list as a tuple
|
|
/// or paren expr, suitable for use in an applyexpr.
|
|
///
|
|
/// NOTE: This returns null if a varargs parameter exists in the list, as it
|
|
/// cannot be forwarded correctly yet.
|
|
///
|
|
static Expr *buildArgumentForwardingExpr(ArrayRef<ParamDecl*> params,
|
|
ASTContext &ctx) {
|
|
SmallVector<Identifier, 4> labels;
|
|
SmallVector<SourceLoc, 4> labelLocs;
|
|
SmallVector<Expr *, 4> args;
|
|
|
|
for (auto param : params) {
|
|
// We cannot express how to forward variadic parameters yet.
|
|
if (param->isVariadic())
|
|
return nullptr;
|
|
|
|
Expr *ref = new (ctx) DeclRefExpr(param, DeclNameLoc(), /*implicit*/ true);
|
|
if (param->isInOut())
|
|
ref = new (ctx) InOutExpr(SourceLoc(), ref, Type(), /*isImplicit=*/true);
|
|
args.push_back(ref);
|
|
|
|
labels.push_back(param->getArgumentName());
|
|
labelLocs.push_back(SourceLoc());
|
|
}
|
|
|
|
// A single unlabeled value is not a tuple.
|
|
if (args.size() == 1 && labels[0].empty()) {
|
|
return new (ctx) ParenExpr(SourceLoc(), args[0], SourceLoc(),
|
|
/*hasTrailingClosure=*/false);
|
|
}
|
|
|
|
return TupleExpr::create(ctx, SourceLoc(), args, labels, labelLocs,
|
|
SourceLoc(), false, IsImplicit);
|
|
}
|
|
|
|
|
|
/// Build a reference to the subscript index variables for this subscript
|
|
/// accessor.
|
|
static Expr *buildSubscriptIndexReference(ASTContext &ctx,
|
|
AccessorDecl *accessor) {
|
|
// Pull out the body parameters, which we should have cloned
|
|
// previously to be forwardable. Drop the initial buffer/value
|
|
// parameter in accessors that have one.
|
|
auto params = accessor->getParameterLists().back()->getArray();
|
|
auto accessorKind = accessor->getAccessorKind();
|
|
|
|
// Ignore the value/buffer parameter.
|
|
if (accessorKind != AccessorKind::IsGetter)
|
|
params = params.slice(1);
|
|
|
|
// Ignore the materializeForSet callback storage parameter.
|
|
if (accessorKind == AccessorKind::IsMaterializeForSet)
|
|
params = params.slice(1);
|
|
|
|
// Okay, everything else should be forwarded, build the expression.
|
|
auto result = buildArgumentForwardingExpr(params, ctx);
|
|
assert(result && "FIXME: Cannot forward varargs");
|
|
return result;
|
|
}
|
|
|
|
enum class SelfAccessorKind {
|
|
/// We're building a derived accessor on top of whatever this
|
|
/// class provides.
|
|
Peer,
|
|
|
|
/// We're building a setter or something around an underlying
|
|
/// implementation, which might be storage or inherited from a
|
|
/// superclass.
|
|
Super,
|
|
};
|
|
|
|
static Expr *buildSelfReference(VarDecl *selfDecl,
|
|
SelfAccessorKind selfAccessorKind,
|
|
TypeChecker &TC) {
|
|
switch (selfAccessorKind) {
|
|
case SelfAccessorKind::Peer:
|
|
return new (TC.Context) DeclRefExpr(selfDecl, DeclNameLoc(), IsImplicit);
|
|
|
|
case SelfAccessorKind::Super:
|
|
return new (TC.Context) SuperRefExpr(selfDecl, SourceLoc(), IsImplicit);
|
|
}
|
|
llvm_unreachable("bad self access kind");
|
|
}
|
|
|
|
namespace {
|
|
/// A simple helper interface for buildStorageReference.
|
|
class StorageReferenceContext {
|
|
StorageReferenceContext(const StorageReferenceContext &) = delete;
|
|
public:
|
|
StorageReferenceContext() = default;
|
|
virtual ~StorageReferenceContext() = default;
|
|
|
|
/// Returns the declaration of the entity to use as the base of
|
|
/// the access, or nil if no base is required.
|
|
virtual VarDecl *getSelfDecl() const = 0;
|
|
|
|
/// Returns an expression producing the index value, assuming that
|
|
/// the storage is a subscript declaration.
|
|
virtual Expr *getIndexRefExpr(ASTContext &ctx,
|
|
SubscriptDecl *subscript) const = 0;
|
|
};
|
|
|
|
/// A reference to storage from within an accessor.
|
|
class AccessorStorageReferenceContext : public StorageReferenceContext {
|
|
AccessorDecl *Accessor;
|
|
public:
|
|
AccessorStorageReferenceContext(AccessorDecl *accessor)
|
|
: Accessor(accessor) {}
|
|
~AccessorStorageReferenceContext() override = default;
|
|
|
|
VarDecl *getSelfDecl() const override {
|
|
return Accessor->getImplicitSelfDecl();
|
|
}
|
|
Expr *getIndexRefExpr(ASTContext &ctx,
|
|
SubscriptDecl *subscript) const override {
|
|
return buildSubscriptIndexReference(ctx, Accessor);
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
/// Build an l-value for the storage of a declaration.
|
|
static Expr *buildStorageReference(
|
|
const StorageReferenceContext &referenceContext,
|
|
AbstractStorageDecl *storage,
|
|
AccessSemantics semantics,
|
|
SelfAccessorKind selfAccessorKind,
|
|
TypeChecker &TC) {
|
|
ASTContext &ctx = TC.Context;
|
|
|
|
VarDecl *selfDecl = referenceContext.getSelfDecl();
|
|
if (!selfDecl) {
|
|
return new (ctx) DeclRefExpr(storage, DeclNameLoc(), IsImplicit, semantics);
|
|
}
|
|
|
|
// If we should use a super access if applicable, and we have an
|
|
// overridden decl, then use ordinary access to it.
|
|
if (selfAccessorKind == SelfAccessorKind::Super) {
|
|
if (auto overridden = storage->getOverriddenDecl()) {
|
|
storage = overridden;
|
|
semantics = AccessSemantics::Ordinary;
|
|
} else {
|
|
selfAccessorKind = SelfAccessorKind::Peer;
|
|
}
|
|
}
|
|
|
|
Expr *selfDRE = buildSelfReference(selfDecl, selfAccessorKind, TC);
|
|
|
|
if (auto subscript = dyn_cast<SubscriptDecl>(storage)) {
|
|
Expr *indices = referenceContext.getIndexRefExpr(ctx, subscript);
|
|
return SubscriptExpr::create(ctx, selfDRE, indices, storage,
|
|
IsImplicit, semantics);
|
|
}
|
|
|
|
// This is a potentially polymorphic access, which is unnecessary;
|
|
// however, it shouldn't be problematic because any overrides
|
|
// should also redefine materializeForSet.
|
|
return new (ctx) MemberRefExpr(selfDRE, SourceLoc(), storage,
|
|
DeclNameLoc(), IsImplicit, semantics);
|
|
}
|
|
|
|
static Expr *buildStorageReference(AccessorDecl *accessor,
|
|
AbstractStorageDecl *storage,
|
|
AccessSemantics semantics,
|
|
SelfAccessorKind selfAccessorKind,
|
|
TypeChecker &TC) {
|
|
return buildStorageReference(AccessorStorageReferenceContext(accessor),
|
|
storage, semantics, selfAccessorKind, TC);
|
|
}
|
|
|
|
/// Load the value of VD. If VD is an @override of another value, we call the
|
|
/// superclass getter. Otherwise, we do a direct load of the value.
|
|
static Expr *createPropertyLoadOrCallSuperclassGetter(AccessorDecl *accessor,
|
|
AbstractStorageDecl *storage,
|
|
TypeChecker &TC) {
|
|
return buildStorageReference(accessor, storage,
|
|
AccessSemantics::DirectToStorage,
|
|
SelfAccessorKind::Super, TC);
|
|
}
|
|
|
|
/// Look up the NSCopying protocol from the Foundation module, if present.
|
|
/// Otherwise return null.
|
|
static ProtocolDecl *getNSCopyingProtocol(TypeChecker &TC,
|
|
DeclContext *DC) {
|
|
ASTContext &ctx = TC.Context;
|
|
auto foundation = ctx.getLoadedModule(ctx.Id_Foundation);
|
|
if (!foundation)
|
|
return nullptr;
|
|
|
|
SmallVector<ValueDecl *, 2> results;
|
|
DC->lookupQualified(ModuleType::get(foundation),
|
|
ctx.getSwiftId(KnownFoundationEntity::NSCopying),
|
|
NL_QualifiedDefault | NL_KnownNonCascadingDependency,
|
|
/*typeResolver=*/nullptr,
|
|
results);
|
|
|
|
if (results.size() != 1)
|
|
return nullptr;
|
|
|
|
return dyn_cast<ProtocolDecl>(results.front());
|
|
}
|
|
|
|
|
|
|
|
/// Synthesize the code to store 'Val' to 'VD', given that VD has an @NSCopying
|
|
/// attribute on it. We know that VD is a stored property in a class, so we
|
|
/// just need to generate something like "self.property = val.copy(zone: nil)"
|
|
/// here. This does some type checking to validate that the call will succeed.
|
|
static Expr *synthesizeCopyWithZoneCall(Expr *Val, VarDecl *VD,
|
|
TypeChecker &TC) {
|
|
auto &Ctx = TC.Context;
|
|
|
|
// We support @NSCopying on class types (which conform to NSCopying),
|
|
// protocols which conform, and option types thereof.
|
|
Type UnderlyingType = VD->getType()->getReferenceStorageReferent();
|
|
|
|
bool isOptional = false;
|
|
if (Type optionalEltTy = UnderlyingType->getOptionalObjectType()) {
|
|
UnderlyingType = optionalEltTy;
|
|
isOptional = true;
|
|
}
|
|
|
|
// The element type must conform to NSCopying. If not, emit an error and just
|
|
// recovery by synthesizing without the copy call.
|
|
auto *CopyingProto = getNSCopyingProtocol(TC, VD->getDeclContext());
|
|
if (!CopyingProto || !TC.conformsToProtocol(UnderlyingType, CopyingProto,
|
|
VD->getDeclContext(), None)) {
|
|
TC.diagnose(VD->getLoc(), diag::nscopying_doesnt_conform);
|
|
return Val;
|
|
}
|
|
|
|
// If we have an optional type, we have to "?" the incoming value to only
|
|
// evaluate the subexpression if the incoming value is non-null.
|
|
if (isOptional)
|
|
Val = new (Ctx) BindOptionalExpr(Val, SourceLoc(), 0);
|
|
|
|
// Generate:
|
|
// (force_value_expr type='<null>'
|
|
// (call_expr type='<null>'
|
|
// (unresolved_dot_expr type='<null>' field 'copy'
|
|
// "Val")
|
|
// (paren_expr type='<null>'
|
|
// (nil_literal_expr type='<null>'))))
|
|
auto UDE = new (Ctx) UnresolvedDotExpr(Val, SourceLoc(),
|
|
Ctx.getIdentifier("copy"),
|
|
DeclNameLoc(), /*implicit*/true);
|
|
Expr *Nil = new (Ctx) NilLiteralExpr(SourceLoc(), /*implicit*/true);
|
|
|
|
//- (id)copyWithZone:(NSZone *)zone;
|
|
Expr *Call = CallExpr::createImplicit(Ctx, UDE, { Nil }, { Ctx.Id_with });
|
|
|
|
TypeLoc ResultTy;
|
|
ResultTy.setType(VD->getType(), true);
|
|
|
|
// If we're working with non-optional types, we're forcing the cast.
|
|
if (!isOptional) {
|
|
Call = new (Ctx) ForcedCheckedCastExpr(Call, SourceLoc(), SourceLoc(),
|
|
TypeLoc::withoutLoc(UnderlyingType));
|
|
Call->setImplicit();
|
|
return Call;
|
|
}
|
|
|
|
// We're working with optional types, so perform a conditional checked
|
|
// downcast.
|
|
Call = new (Ctx) ConditionalCheckedCastExpr(Call, SourceLoc(), SourceLoc(),
|
|
TypeLoc::withoutLoc(UnderlyingType));
|
|
Call->setImplicit();
|
|
|
|
// Use OptionalEvaluationExpr to evaluate the "?".
|
|
return new (Ctx) OptionalEvaluationExpr(Call);
|
|
}
|
|
|
|
/// In a synthesized accessor body, store 'value' to the appropriate element.
|
|
///
|
|
/// If the property is an override, we call the superclass setter.
|
|
/// Otherwise, we do a direct store of the value.
|
|
static void createPropertyStoreOrCallSuperclassSetter(AccessorDecl *accessor,
|
|
Expr *value,
|
|
AbstractStorageDecl *storage,
|
|
SmallVectorImpl<ASTNode> &body,
|
|
TypeChecker &TC) {
|
|
// If the storage is an @NSCopying property, then we store the
|
|
// result of a copyWithZone call on the value, not the value itself.
|
|
if (auto property = dyn_cast<VarDecl>(storage)) {
|
|
if (property->getAttrs().hasAttribute<NSCopyingAttr>())
|
|
value = synthesizeCopyWithZoneCall(value, property, TC);
|
|
}
|
|
|
|
// Create:
|
|
// (assign (decl_ref_expr(VD)), decl_ref_expr(value))
|
|
// or:
|
|
// (assign (member_ref_expr(decl_ref_expr(self), VD)), decl_ref_expr(value))
|
|
Expr *dest = buildStorageReference(accessor, storage,
|
|
AccessSemantics::DirectToStorage,
|
|
SelfAccessorKind::Super, TC);
|
|
|
|
body.push_back(new (TC.Context) AssignExpr(dest, SourceLoc(), value,
|
|
IsImplicit));
|
|
}
|
|
|
|
/// Synthesize the body of a trivial getter. For a non-member vardecl or one
|
|
/// which is not an override of a base class property, it performs a direct
|
|
/// storage load. For an override of a base member property, it chains up to
|
|
/// super.
|
|
static void synthesizeTrivialGetter(AccessorDecl *getter,
|
|
AbstractStorageDecl *storage,
|
|
TypeChecker &TC) {
|
|
auto &ctx = TC.Context;
|
|
|
|
Expr *result = createPropertyLoadOrCallSuperclassGetter(getter, storage, TC);
|
|
ASTNode returnStmt = new (ctx) ReturnStmt(SourceLoc(), result, IsImplicit);
|
|
|
|
SourceLoc loc = storage->getLoc();
|
|
getter->setBody(BraceStmt::create(ctx, loc, returnStmt, loc, true));
|
|
|
|
TC.Context.addSynthesizedDecl(getter);
|
|
TC.DeclsToFinalize.insert(getter);
|
|
}
|
|
|
|
/// Synthesize the body of a trivial setter.
|
|
static void synthesizeTrivialSetter(AccessorDecl *setter,
|
|
AbstractStorageDecl *storage,
|
|
VarDecl *valueVar,
|
|
TypeChecker &TC) {
|
|
auto &ctx = TC.Context;
|
|
SourceLoc loc = storage->getLoc();
|
|
|
|
auto *valueDRE = new (ctx) DeclRefExpr(valueVar, DeclNameLoc(), IsImplicit);
|
|
SmallVector<ASTNode, 1> setterBody;
|
|
createPropertyStoreOrCallSuperclassSetter(setter, valueDRE, storage,
|
|
setterBody, TC);
|
|
setter->setBody(BraceStmt::create(ctx, loc, setterBody, loc, true));
|
|
|
|
TC.Context.addSynthesizedDecl(setter);
|
|
TC.DeclsToFinalize.insert(setter);
|
|
}
|
|
|
|
/// Does a storage decl currently lacking accessor functions require a
|
|
/// setter to be synthesized?
|
|
static bool doesStorageNeedSetter(AbstractStorageDecl *storage) {
|
|
assert(!storage->hasAccessorFunctions());
|
|
switch (storage->getStorageKind()) {
|
|
// Add a setter to a stored variable unless it's a let.
|
|
case AbstractStorageDecl::Stored:
|
|
return !cast<VarDecl>(storage)->isLet();
|
|
|
|
// Addressed storage gets a setter if it has a mutable addressor.
|
|
case AbstractStorageDecl::Addressed:
|
|
return storage->getMutableAddressor() != nullptr;
|
|
|
|
// These should already have accessor functions.
|
|
case AbstractStorageDecl::StoredWithTrivialAccessors:
|
|
case AbstractStorageDecl::StoredWithObservers:
|
|
case AbstractStorageDecl::InheritedWithObservers:
|
|
case AbstractStorageDecl::AddressedWithTrivialAccessors:
|
|
case AbstractStorageDecl::AddressedWithObservers:
|
|
case AbstractStorageDecl::ComputedWithMutableAddress:
|
|
llvm_unreachable("already has accessor functions");
|
|
|
|
case AbstractStorageDecl::Computed:
|
|
llvm_unreachable("not stored");
|
|
}
|
|
llvm_unreachable("bad storage kind");
|
|
}
|
|
|
|
/// Add trivial accessors to a Stored or Addressed property.
|
|
static void addTrivialAccessorsToStorage(AbstractStorageDecl *storage,
|
|
TypeChecker &TC) {
|
|
assert(!storage->hasAccessorFunctions() && "already has accessors?");
|
|
assert(!storage->getAttrs().hasAttribute<LazyAttr>());
|
|
assert(!storage->getAttrs().hasAttribute<NSManagedAttr>());
|
|
|
|
auto *DC = storage->getDeclContext();
|
|
|
|
// Create the getter.
|
|
AccessorDecl *getter = createGetterPrototype(storage, TC);
|
|
|
|
// Create the setter.
|
|
AccessorDecl *setter = nullptr;
|
|
ParamDecl *setterValueParam = nullptr;
|
|
if (doesStorageNeedSetter(storage))
|
|
setter = createSetterPrototype(storage, setterValueParam, TC);
|
|
|
|
// Okay, we have both the getter and setter. Set them in VD.
|
|
storage->addTrivialAccessors(getter, setter, nullptr);
|
|
|
|
// Synthesize the body of the getter.
|
|
synthesizeTrivialGetter(getter, storage, TC);
|
|
maybeMarkTransparent(getter, storage, TC);
|
|
|
|
if (setter) {
|
|
// Synthesize the body of the setter.
|
|
synthesizeTrivialSetter(setter, storage, setterValueParam, TC);
|
|
maybeMarkTransparent(setter, storage, TC);
|
|
}
|
|
|
|
// We've added some members to our containing context, add them to
|
|
// the right list.
|
|
addMemberToContextIfNeeded(getter, DC, storage);
|
|
if (setter)
|
|
addMemberToContextIfNeeded(setter, DC, getter);
|
|
|
|
maybeAddMaterializeForSet(storage, TC);
|
|
}
|
|
|
|
/// Add a trivial setter and materializeForSet to a
|
|
/// ComputedWithMutableAddress storage decl.
|
|
void swift::
|
|
synthesizeSetterForMutableAddressedStorage(AbstractStorageDecl *storage,
|
|
TypeChecker &TC) {
|
|
auto setter = storage->getSetter();
|
|
assert(setter);
|
|
assert(!storage->getSetter()->getBody());
|
|
assert(storage->getStorageKind() ==
|
|
AbstractStorageDecl::ComputedWithMutableAddress);
|
|
|
|
// Synthesize the body of the setter.
|
|
VarDecl *valueParamDecl = getFirstParamDecl(setter);
|
|
synthesizeTrivialSetter(setter, storage, valueParamDecl, TC);
|
|
maybeMarkTransparent(setter, storage, TC);
|
|
}
|
|
|
|
/// Add a materializeForSet accessor to the given declaration.
|
|
static FuncDecl *addMaterializeForSet(AbstractStorageDecl *storage,
|
|
TypeChecker &TC) {
|
|
if (TC.Context.getOptionalDecl() == nullptr) {
|
|
TC.diagnose(storage->getStartLoc(), diag::optional_intrinsics_not_found);
|
|
return nullptr;
|
|
}
|
|
|
|
auto materializeForSet = createMaterializeForSetPrototype(
|
|
storage, storage->getGetter(), storage->getSetter(), TC);
|
|
addMemberToContextIfNeeded(materializeForSet, storage->getDeclContext(),
|
|
storage->getSetter());
|
|
storage->setMaterializeForSetFunc(materializeForSet);
|
|
|
|
return materializeForSet;
|
|
}
|
|
|
|
static void convertNSManagedStoredVarToComputed(VarDecl *VD, TypeChecker &TC) {
|
|
assert(VD->getStorageKind() == AbstractStorageDecl::Stored);
|
|
|
|
// Create the getter.
|
|
auto *Get = createGetterPrototype(VD, TC);
|
|
|
|
// Create the setter.
|
|
ParamDecl *SetValueDecl = nullptr;
|
|
auto *Set = createSetterPrototype(VD, SetValueDecl, TC);
|
|
|
|
// Okay, we have both the getter and setter. Set them in VD.
|
|
VD->makeComputed(SourceLoc(), Get, Set, nullptr, SourceLoc());
|
|
|
|
// We've added some members to our containing class/extension, add them to
|
|
// the members list.
|
|
addMemberToContextIfNeeded(Get, VD->getDeclContext(), VD);
|
|
addMemberToContextIfNeeded(Set, VD->getDeclContext(), Get);
|
|
|
|
maybeAddMaterializeForSet(VD, TC);
|
|
}
|
|
|
|
/// The specified AbstractStorageDecl was just found to satisfy a
|
|
/// protocol property requirement. Ensure that it has the full
|
|
/// complement of accessors.
|
|
void TypeChecker::synthesizeWitnessAccessorsForStorage(
|
|
AbstractStorageDecl *requirement,
|
|
AbstractStorageDecl *storage) {
|
|
// If the decl is stored, convert it to StoredWithTrivialAccessors
|
|
// by synthesizing the full set of accessors.
|
|
if (!storage->hasAccessorFunctions()) {
|
|
// Don't do this if the declaration is lazy or NSManaged.
|
|
// This must be a re-entrant attempt to synthesize accessors
|
|
// before validateDecl has finished.
|
|
if (storage->getAttrs().hasAttribute<LazyAttr>() ||
|
|
storage->getAttrs().hasAttribute<NSManagedAttr>())
|
|
return;
|
|
|
|
addTrivialAccessorsToStorage(storage, *this);
|
|
}
|
|
|
|
// @objc protocols don't need a materializeForSet since ObjC doesn't
|
|
// have that concept.
|
|
bool wantMaterializeForSet =
|
|
!requirement->isObjC() && requirement->getSetter();
|
|
|
|
// If we want wantMaterializeForSet, create it now.
|
|
if (wantMaterializeForSet && !storage->getMaterializeForSetFunc())
|
|
addMaterializeForSet(storage, *this);
|
|
}
|
|
|
|
/// Given a VarDecl with a willSet: and/or didSet: specifier, synthesize the
|
|
/// (trivial) getter and the setter, which calls these.
|
|
void swift::synthesizeObservingAccessors(VarDecl *VD, TypeChecker &TC) {
|
|
assert(VD->hasObservers());
|
|
assert(VD->getGetter() && VD->getSetter() &&
|
|
!VD->getGetter()->hasBody() && !VD->getSetter()->hasBody() &&
|
|
"willSet/didSet var already has a getter or setter");
|
|
|
|
auto &Ctx = VD->getASTContext();
|
|
SourceLoc Loc = VD->getLoc();
|
|
|
|
// The getter is always trivial: just perform a (direct!) load of storage, or
|
|
// a call of a superclass getter if this is an override.
|
|
auto *Get = VD->getGetter();
|
|
synthesizeTrivialGetter(Get, VD, TC);
|
|
maybeMarkTransparent(Get, VD, TC);
|
|
|
|
// Okay, the getter is done, create the setter now. Start by finding the
|
|
// decls for 'self' and 'value'.
|
|
auto *Set = VD->getSetter();
|
|
auto *SelfDecl = Set->getImplicitSelfDecl();
|
|
VarDecl *ValueDecl = Set->getParameterLists().back()->get(0);
|
|
|
|
// The setter loads the oldValue, invokes willSet with the incoming value,
|
|
// does a direct store, then invokes didSet with the oldValue.
|
|
SmallVector<ASTNode, 6> SetterBody;
|
|
|
|
// If there is a didSet, it will take the old value. Load it into a temporary
|
|
// 'let' so we have it for later.
|
|
// TODO: check the body of didSet to only do this load (which may call the
|
|
// superclass getter) if didSet takes an argument.
|
|
VarDecl *OldValue = nullptr;
|
|
if (VD->getDidSetFunc()) {
|
|
Expr *OldValueExpr
|
|
= createPropertyLoadOrCallSuperclassGetter(Set, VD, TC);
|
|
|
|
OldValue = new (Ctx) VarDecl(/*IsStatic*/false, VarDecl::Specifier::Let,
|
|
/*IsCaptureList*/false, SourceLoc(),
|
|
Ctx.getIdentifier("tmp"), Type(), Set);
|
|
OldValue->setImplicit();
|
|
auto *tmpPattern = new (Ctx) NamedPattern(OldValue, /*implicit*/ true);
|
|
auto tmpPBD = PatternBindingDecl::create(Ctx, SourceLoc(),
|
|
StaticSpellingKind::None,
|
|
SourceLoc(),
|
|
tmpPattern, OldValueExpr, Set);
|
|
tmpPBD->setImplicit();
|
|
SetterBody.push_back(tmpPBD);
|
|
SetterBody.push_back(OldValue);
|
|
}
|
|
|
|
// Create:
|
|
// (call_expr (dot_syntax_call_expr (decl_ref_expr(willSet)),
|
|
// (decl_ref_expr(self))),
|
|
// (declrefexpr(value)))
|
|
// or:
|
|
// (call_expr (decl_ref_expr(willSet)), (declrefexpr(value)))
|
|
if (auto willSet = VD->getWillSetFunc()) {
|
|
Expr *Callee = new (Ctx) DeclRefExpr(willSet, DeclNameLoc(), /*imp*/true);
|
|
auto *ValueDRE = new (Ctx) DeclRefExpr(ValueDecl, DeclNameLoc(),
|
|
/*imp*/true);
|
|
if (SelfDecl) {
|
|
auto *SelfDRE = new (Ctx) DeclRefExpr(SelfDecl, DeclNameLoc(),
|
|
/*imp*/true);
|
|
Callee = new (Ctx) DotSyntaxCallExpr(Callee, SourceLoc(), SelfDRE);
|
|
}
|
|
SetterBody.push_back(CallExpr::createImplicit(Ctx, Callee, { ValueDRE },
|
|
{ Identifier() }));
|
|
|
|
// Make sure the didSet/willSet accessors are marked final if in a class.
|
|
if (!willSet->isFinal() &&
|
|
VD->getDeclContext()->getAsClassOrClassExtensionContext())
|
|
makeFinal(Ctx, willSet);
|
|
}
|
|
|
|
// Create an assignment into the storage or call to superclass setter.
|
|
auto *ValueDRE = new (Ctx) DeclRefExpr(ValueDecl, DeclNameLoc(), true);
|
|
createPropertyStoreOrCallSuperclassSetter(Set, ValueDRE, VD, SetterBody, TC);
|
|
|
|
// Create:
|
|
// (call_expr (dot_syntax_call_expr (decl_ref_expr(didSet)),
|
|
// (decl_ref_expr(self))),
|
|
// (decl_ref_expr(tmp)))
|
|
// or:
|
|
// (call_expr (decl_ref_expr(didSet)), (decl_ref_expr(tmp)))
|
|
if (auto didSet = VD->getDidSetFunc()) {
|
|
auto *OldValueExpr = new (Ctx) DeclRefExpr(OldValue, DeclNameLoc(),
|
|
/*impl*/true);
|
|
Expr *Callee = new (Ctx) DeclRefExpr(didSet, DeclNameLoc(), /*imp*/true);
|
|
if (SelfDecl) {
|
|
auto *SelfDRE = new (Ctx) DeclRefExpr(SelfDecl, DeclNameLoc(),
|
|
/*imp*/true);
|
|
Callee = new (Ctx) DotSyntaxCallExpr(Callee, SourceLoc(), SelfDRE);
|
|
}
|
|
SetterBody.push_back(CallExpr::createImplicit(Ctx, Callee, { OldValueExpr },
|
|
{ Identifier() }));
|
|
|
|
// Make sure the didSet/willSet accessors are marked final if in a class.
|
|
if (!didSet->isFinal() &&
|
|
VD->getDeclContext()->getAsClassOrClassExtensionContext())
|
|
makeFinal(Ctx, didSet);
|
|
}
|
|
|
|
Set->setBody(BraceStmt::create(Ctx, Loc, SetterBody, Loc, true));
|
|
}
|
|
|
|
namespace {
|
|
/// This ASTWalker explores an expression tree looking for expressions (which
|
|
/// are DeclContext's) and changes their parent DeclContext to NewDC.
|
|
class RecontextualizeClosures : public ASTWalker {
|
|
DeclContext *NewDC;
|
|
public:
|
|
RecontextualizeClosures(DeclContext *NewDC) : NewDC(NewDC) {}
|
|
|
|
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
|
|
// If we find a closure, update its declcontext and do *not* walk into it.
|
|
if (auto CE = dyn_cast<AbstractClosureExpr>(E)) {
|
|
CE->setParent(NewDC);
|
|
return { false, E };
|
|
}
|
|
|
|
if (auto CLE = dyn_cast<CaptureListExpr>(E)) {
|
|
// Make sure to recontextualize any decls in the capture list as well.
|
|
for (auto &CLE : CLE->getCaptureList()) {
|
|
CLE.Var->setDeclContext(NewDC);
|
|
CLE.Init->setDeclContext(NewDC);
|
|
}
|
|
}
|
|
|
|
return { true, E };
|
|
}
|
|
|
|
/// We don't want to recurse into declarations or statements.
|
|
bool walkToDeclPre(Decl *) override { return false; }
|
|
std::pair<bool, Stmt*> walkToStmtPre(Stmt *S) override { return {false,S}; }
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
/// Synthesize the getter for a lazy property with the specified storage
|
|
/// vardecl.
|
|
static FuncDecl *completeLazyPropertyGetter(VarDecl *VD, VarDecl *Storage,
|
|
TypeChecker &TC) {
|
|
auto &Ctx = VD->getASTContext();
|
|
|
|
// The getter checks the optional, storing the initial value in if nil. The
|
|
// specific pattern we generate is:
|
|
// get {
|
|
// let tmp1 = storage
|
|
// if tmp1 {
|
|
// return tmp1!
|
|
// }
|
|
// let tmp2 : Ty = <<initializer expression>>
|
|
// storage = tmp2
|
|
// return tmp2
|
|
// }
|
|
auto *Get = VD->getGetter();
|
|
|
|
SmallVector<ASTNode, 6> Body;
|
|
|
|
// Load the existing storage and store it into the 'tmp1' temporary.
|
|
auto *Tmp1VD = new (Ctx) VarDecl(/*IsStatic*/false, VarDecl::Specifier::Let,
|
|
/*IsCaptureList*/false, SourceLoc(),
|
|
Ctx.getIdentifier("tmp1"), Type(), Get);
|
|
Tmp1VD->setImplicit();
|
|
|
|
auto *Tmp1PBDPattern = new (Ctx) NamedPattern(Tmp1VD, /*implicit*/true);
|
|
auto *Tmp1Init = createPropertyLoadOrCallSuperclassGetter(Get, Storage, TC);
|
|
auto *Tmp1PBD = PatternBindingDecl::create(Ctx, /*StaticLoc*/SourceLoc(),
|
|
StaticSpellingKind::None,
|
|
/*VarLoc*/SourceLoc(),
|
|
Tmp1PBDPattern, Tmp1Init, Get);
|
|
Body.push_back(Tmp1PBD);
|
|
Body.push_back(Tmp1VD);
|
|
|
|
// Build the early return inside the if.
|
|
auto *Tmp1DRE = new (Ctx) DeclRefExpr(Tmp1VD, DeclNameLoc(), /*Implicit*/true,
|
|
AccessSemantics::DirectToStorage);
|
|
auto *EarlyReturnVal = new (Ctx) ForceValueExpr(Tmp1DRE, SourceLoc());
|
|
auto *Return = new (Ctx) ReturnStmt(SourceLoc(), EarlyReturnVal,
|
|
/*implicit*/true);
|
|
|
|
// Build the "if" around the early return.
|
|
Tmp1DRE = new (Ctx) DeclRefExpr(Tmp1VD, DeclNameLoc(), /*Implicit*/true,
|
|
AccessSemantics::DirectToStorage);
|
|
|
|
// Call through "hasValue" on the decl ref.
|
|
Tmp1DRE->setType(OptionalType::get(VD->getType()));
|
|
constraints::ConstraintSystem cs(TC,
|
|
VD->getDeclContext(),
|
|
constraints::ConstraintSystemOptions());
|
|
constraints::Solution solution(cs, constraints::Score());
|
|
auto HasValueExpr = solution.convertOptionalToBool(Tmp1DRE, nullptr);
|
|
|
|
Body.push_back(new (Ctx) IfStmt(SourceLoc(), HasValueExpr, Return,
|
|
/*elseloc*/SourceLoc(), /*else*/nullptr,
|
|
/*implicit*/ true, Ctx));
|
|
|
|
|
|
auto *Tmp2VD = new (Ctx) VarDecl(/*IsStatic*/false, VarDecl::Specifier::Let,
|
|
/*IsCaptureList*/false, SourceLoc(),
|
|
Ctx.getIdentifier("tmp2"), VD->getType(),
|
|
Get);
|
|
Tmp2VD->setImplicit();
|
|
|
|
|
|
// Take the initializer from the PatternBindingDecl for VD.
|
|
// TODO: This doesn't work with complicated patterns like:
|
|
// lazy var (a,b) = foo()
|
|
auto *InitValue = VD->getParentInitializer();
|
|
auto PBD = VD->getParentPatternBinding();
|
|
unsigned entryIndex = PBD->getPatternEntryIndexForVarDecl(VD);
|
|
PBD->setInit(entryIndex, nullptr);
|
|
PBD->setInitializerChecked(entryIndex);
|
|
|
|
// Recontextualize any closure declcontexts nested in the initializer to
|
|
// realize that they are in the getter function.
|
|
InitValue->walk(RecontextualizeClosures(Get));
|
|
|
|
Pattern *Tmp2PBDPattern = new (Ctx) NamedPattern(Tmp2VD, /*implicit*/true);
|
|
Tmp2PBDPattern = new (Ctx) TypedPattern(Tmp2PBDPattern,
|
|
TypeLoc::withoutLoc(VD->getType()),
|
|
/*implicit*/true);
|
|
|
|
auto *Tmp2PBD = PatternBindingDecl::create(Ctx, /*StaticLoc*/SourceLoc(),
|
|
StaticSpellingKind::None,
|
|
InitValue->getStartLoc(),
|
|
Tmp2PBDPattern, InitValue, Get);
|
|
Body.push_back(Tmp2PBD);
|
|
Body.push_back(Tmp2VD);
|
|
|
|
// Assign tmp2 into storage.
|
|
auto Tmp2DRE = new (Ctx) DeclRefExpr(Tmp2VD, DeclNameLoc(), /*Implicit*/true,
|
|
AccessSemantics::DirectToStorage);
|
|
createPropertyStoreOrCallSuperclassSetter(Get, Tmp2DRE, Storage, Body, TC);
|
|
|
|
// Return tmp2.
|
|
Tmp2DRE = new (Ctx) DeclRefExpr(Tmp2VD, DeclNameLoc(), /*Implicit*/true,
|
|
AccessSemantics::DirectToStorage);
|
|
|
|
Body.push_back(new (Ctx) ReturnStmt(SourceLoc(), Tmp2DRE, /*implicit*/true));
|
|
|
|
Get->setBody(BraceStmt::create(Ctx, VD->getLoc(), Body, VD->getLoc(),
|
|
/*implicit*/true));
|
|
|
|
return Get;
|
|
}
|
|
|
|
void TypeChecker::completePropertyBehaviorStorage(VarDecl *VD,
|
|
VarDecl *BehaviorStorage,
|
|
FuncDecl *DefaultInitStorage,
|
|
FuncDecl *ParamInitStorage,
|
|
Type SelfTy,
|
|
Type StorageTy,
|
|
NormalProtocolConformance *BehaviorConformance,
|
|
SubstitutionList SelfInterfaceSubs,
|
|
SubstitutionList SelfContextSubs) {
|
|
assert(BehaviorStorage);
|
|
assert((bool)DefaultInitStorage != (bool)ParamInitStorage);
|
|
|
|
// Substitute the storage type into the conforming context.
|
|
auto sig = BehaviorConformance->getProtocol()->getGenericSignatureOfContext();
|
|
|
|
auto interfaceMap = sig->getSubstitutionMap(SelfInterfaceSubs);
|
|
auto SubstStorageInterfaceTy = StorageTy.subst(interfaceMap);
|
|
assert(SubstStorageInterfaceTy && "storage type substitution failed?!");
|
|
|
|
auto contextMap = sig->getSubstitutionMap(SelfContextSubs);
|
|
auto SubstStorageContextTy = StorageTy.subst(contextMap);
|
|
assert(SubstStorageContextTy && "storage type substitution failed?!");
|
|
|
|
auto DC = VD->getDeclContext();
|
|
SmallString<64> NameBuf = VD->getName().str();
|
|
NameBuf += ".storage";
|
|
auto StorageName = Context.getIdentifier(NameBuf);
|
|
auto storageSpecifier = BehaviorStorage->isSettable(DC)
|
|
? VarDecl::Specifier::Var
|
|
: VarDecl::Specifier::Let;
|
|
auto *Storage = new (Context) VarDecl(
|
|
/*IsStatic*/VD->isStatic(), storageSpecifier,
|
|
/*IsCaptureList*/false, VD->getLoc(), StorageName, SubstStorageContextTy,
|
|
DC);
|
|
Storage->setInterfaceType(SubstStorageInterfaceTy);
|
|
Storage->setUserAccessible(false);
|
|
// Mark the vardecl to be final, implicit, and private. In a class, this
|
|
// prevents it from being dynamically dispatched.
|
|
if (VD->getDeclContext()->getAsClassOrClassExtensionContext())
|
|
makeFinal(Context, Storage);
|
|
Storage->setImplicit();
|
|
Storage->setAccess(AccessLevel::Private);
|
|
Storage->setSetterAccess(AccessLevel::Private);
|
|
|
|
addMemberToContextIfNeeded(Storage, DC);
|
|
|
|
// Initialize the storage immediately, if we can.
|
|
Expr *InitStorageExpr = nullptr;
|
|
auto Method = DefaultInitStorage ? DefaultInitStorage : ParamInitStorage;
|
|
auto SpecializeInitStorage = ConcreteDeclRef(Context, Method,
|
|
SelfContextSubs);
|
|
|
|
if (DefaultInitStorage ||
|
|
(ParamInitStorage && VD->getParentInitializer())) {
|
|
|
|
// Build the initializer expression, 'Self.initStorage()', using the
|
|
// conformance.
|
|
auto SelfTypeRef = TypeExpr::createImplicit(SelfTy, Context);
|
|
|
|
auto InitStorageRef = new (Context) DeclRefExpr(SpecializeInitStorage,
|
|
DeclNameLoc(),
|
|
/*implicit*/ true);
|
|
auto InitStorageMethodTy = FunctionType::get(Context.TheEmptyTupleType,
|
|
SubstStorageContextTy);
|
|
auto InitStorageRefTy = FunctionType::get(SelfTypeRef->getType(),
|
|
InitStorageMethodTy);
|
|
InitStorageRef->setType(InitStorageRefTy);
|
|
|
|
auto SelfApply = new (Context) DotSyntaxCallExpr(InitStorageRef,
|
|
SourceLoc(),
|
|
SelfTypeRef);
|
|
SelfApply->setImplicit();
|
|
SelfApply->setType(InitStorageMethodTy);
|
|
SelfApply->setThrows(false);
|
|
|
|
SmallVector<Expr *, 1> InitStorageArgs;
|
|
SmallVector<Identifier, 1> InitStorageArgLabels;
|
|
if (ParamInitStorage) {
|
|
// Claim the var initializer as the parameter to the `initStorage`
|
|
// method.
|
|
auto InitValue = VD->getParentInitializer();
|
|
auto PBD = VD->getParentPatternBinding();
|
|
unsigned entryIndex = PBD->getPatternEntryIndexForVarDecl(VD);
|
|
PBD->setInit(entryIndex, nullptr);
|
|
PBD->setInitializerChecked(entryIndex);
|
|
|
|
// Recontextualize any closure declcontexts nested in the initializer to
|
|
// realize that they are in the initialization context.
|
|
InitValue->walk(RecontextualizeClosures(DC));
|
|
|
|
// Coerce to the property type.
|
|
InitValue = new (Context) CoerceExpr(InitValue, SourceLoc(),
|
|
TypeLoc::withoutLoc(SelfContextSubs[1].getReplacement()));
|
|
// Type-check the expression.
|
|
typeCheckExpression(InitValue, DC);
|
|
|
|
InitStorageArgs.push_back(InitValue);
|
|
InitStorageArgLabels.push_back(Identifier());
|
|
}
|
|
|
|
auto InitStorageExpr = CallExpr::createImplicit(Context,SelfApply,
|
|
InitStorageArgs,
|
|
InitStorageArgLabels);
|
|
InitStorageExpr->setType(SubstStorageContextTy);
|
|
InitStorageExpr->setThrows(false);
|
|
|
|
} else {
|
|
// Save the storage property and the initStorage reference for later.
|
|
// We'll leave it to DI analysis to insert the initializer call at the
|
|
// right place.
|
|
auto *Behavior = VD->getMutableBehavior();
|
|
Behavior->StorageDecl = Storage;
|
|
Behavior->InitStorageDecl = SpecializeInitStorage;
|
|
}
|
|
|
|
// Create the pattern binding decl for the storage decl. This will get
|
|
// default initialized using the protocol's initStorage() method.
|
|
Pattern *PBDPattern = new (Context) NamedPattern(Storage, /*implicit*/true);
|
|
PBDPattern = new (Context) TypedPattern(PBDPattern,
|
|
TypeLoc::withoutLoc(SubstStorageContextTy),
|
|
/*implicit*/true);
|
|
auto *PBD = PatternBindingDecl::create(Context, /*staticloc*/SourceLoc(),
|
|
VD->getParentPatternBinding()->getStaticSpelling(),
|
|
/*varloc*/VD->getLoc(),
|
|
PBDPattern, /*init*/InitStorageExpr,
|
|
VD->getDeclContext());
|
|
PBD->setImplicit();
|
|
PBD->setInitializerChecked(0);
|
|
addMemberToContextIfNeeded(PBD, VD->getDeclContext(), VD);
|
|
|
|
// Add accessors to the storage, since we'll need them to satisfy the
|
|
// conformance requirements.
|
|
addTrivialAccessorsToStorage(Storage, *this);
|
|
|
|
// FIXME: Hack to eliminate spurious diagnostics.
|
|
if (BehaviorStorage->isStatic() != Storage->isStatic()) return;
|
|
|
|
// Add the witnesses to the conformance.
|
|
recordKnownWitness(BehaviorConformance, BehaviorStorage, Storage);
|
|
recordKnownWitness(BehaviorConformance, BehaviorStorage->getGetter(),
|
|
Storage->getGetter());
|
|
if (BehaviorStorage->isSettable(DC))
|
|
recordKnownWitness(BehaviorConformance, BehaviorStorage->getSetter(),
|
|
Storage->getSetter());
|
|
}
|
|
|
|
void TypeChecker::completePropertyBehaviorParameter(VarDecl *VD,
|
|
FuncDecl *BehaviorParameter,
|
|
NormalProtocolConformance *BehaviorConformance,
|
|
SubstitutionList SelfInterfaceSubs,
|
|
SubstitutionList SelfContextSubs) {
|
|
// Create a method to witness the requirement.
|
|
auto DC = VD->getDeclContext();
|
|
SmallString<64> NameBuf = VD->getName().str();
|
|
NameBuf += ".parameter";
|
|
auto ParameterBaseName = Context.getIdentifier(NameBuf);
|
|
|
|
// Substitute the requirement type into the conforming context.
|
|
auto sig = BehaviorConformance->getProtocol()->getGenericSignatureOfContext();
|
|
auto ParameterTy = BehaviorParameter->getInterfaceType()
|
|
->castTo<AnyFunctionType>()
|
|
->getResult();
|
|
|
|
GenericSignature *genericSig = nullptr;
|
|
GenericEnvironment *genericEnv = nullptr;
|
|
|
|
auto interfaceMap = sig->getSubstitutionMap(SelfInterfaceSubs);
|
|
auto SubstInterfaceTy = ParameterTy.subst(interfaceMap);
|
|
assert(SubstInterfaceTy && "storage type substitution failed?!");
|
|
|
|
auto contextMap = sig->getSubstitutionMap(SelfContextSubs);
|
|
|
|
auto SubstBodyResultTy = SubstInterfaceTy->castTo<AnyFunctionType>()
|
|
->getResult();
|
|
|
|
// Add the Self type back to the interface and context types.
|
|
if (DC->isTypeContext()) {
|
|
if (DC->isGenericContext()) {
|
|
genericSig = DC->getGenericSignatureOfContext();
|
|
genericEnv = DC->getGenericEnvironmentOfContext();
|
|
SubstInterfaceTy = GenericFunctionType::get(genericSig,
|
|
DC->getSelfInterfaceType(),
|
|
SubstInterfaceTy,
|
|
AnyFunctionType::ExtInfo());
|
|
} else {
|
|
SubstInterfaceTy = FunctionType::get(DC->getSelfInterfaceType(),
|
|
SubstInterfaceTy);
|
|
}
|
|
}
|
|
|
|
// Borrow the parameters from the requirement declaration.
|
|
SmallVector<ParameterList *, 2> ParamLists;
|
|
if (DC->isTypeContext()) {
|
|
auto self = ParamDecl::createSelf(SourceLoc(), DC);
|
|
ParamLists.push_back(ParameterList::create(Context, SourceLoc(),
|
|
self, SourceLoc()));
|
|
ParamLists.back()->get(0)->setImplicit();
|
|
}
|
|
|
|
assert(BehaviorParameter->getParameterLists().size() == 2);
|
|
SmallVector<ParamDecl *, 4> Params;
|
|
SmallVector<Identifier, 4> NameComponents;
|
|
|
|
auto *DeclaredParams = BehaviorParameter->getParameterList(1);
|
|
for (unsigned i : indices(*DeclaredParams)) {
|
|
auto declaredParam = DeclaredParams->get(i);
|
|
auto declaredParamTy = declaredParam->getInterfaceType();
|
|
auto interfaceTy = declaredParamTy.subst(interfaceMap);
|
|
assert(interfaceTy);
|
|
auto contextTy = declaredParamTy.subst(contextMap);
|
|
assert(contextTy);
|
|
auto declaredSpecifier = declaredParam->getSpecifier();
|
|
|
|
SmallString<64> ParamNameBuf;
|
|
{
|
|
llvm::raw_svector_ostream names(ParamNameBuf);
|
|
names << "%arg." << i;
|
|
}
|
|
auto param = new (Context) ParamDecl(
|
|
declaredSpecifier, SourceLoc(), SourceLoc(), Identifier(), SourceLoc(),
|
|
Context.getIdentifier(ParamNameBuf), contextTy, DC);
|
|
param->setInterfaceType(interfaceTy);
|
|
param->setImplicit();
|
|
Params.push_back(param);
|
|
NameComponents.push_back(Identifier());
|
|
}
|
|
ParamLists.push_back(ParameterList::create(Context, Params));
|
|
|
|
auto *Parameter =
|
|
FuncDecl::create(Context, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None,
|
|
/*FuncLoc=*/SourceLoc(),
|
|
DeclName(Context, ParameterBaseName, NameComponents),
|
|
/*NameLoc=*/SourceLoc(),
|
|
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
|
|
/*GenericParams=*/nullptr, ParamLists,
|
|
TypeLoc::withoutLoc(SubstBodyResultTy), DC);
|
|
|
|
Parameter->setInterfaceType(SubstInterfaceTy);
|
|
Parameter->setGenericEnvironment(genericEnv);
|
|
Parameter->setValidationStarted();
|
|
|
|
// Mark the method to be final, implicit, and private. In a class, this
|
|
// prevents it from being dynamically dispatched.
|
|
if (DC->getAsClassOrClassExtensionContext())
|
|
makeFinal(Context, Parameter);
|
|
Parameter->setImplicit();
|
|
Parameter->setAccess(AccessLevel::Private);
|
|
|
|
// Recontextualize any closure declcontexts nested in the initializer to
|
|
// realize that they are in the parameter function.
|
|
assert(VD->getBehavior()->Param);
|
|
VD->getBehavior()->Param->walk(RecontextualizeClosures(Parameter));
|
|
|
|
// Apply and return the closure in the function context.
|
|
SmallVector<Expr *, 4> argRefs;
|
|
SmallVector<Identifier, 4> argNames;
|
|
for (unsigned i : indices(Params)) {
|
|
auto param = Params[i];
|
|
auto expr = new (Context) DeclRefExpr(param, DeclNameLoc(),
|
|
/*implicit*/ true);
|
|
argRefs.push_back(expr);
|
|
argNames.push_back(DeclaredParams->get(i)->getName());
|
|
}
|
|
auto apply = CallExpr::createImplicit(Context, VD->getBehavior()->Param,
|
|
argRefs, argNames);
|
|
|
|
// Return the expression value.
|
|
auto Ret = new (Context) ReturnStmt(SourceLoc(), apply,
|
|
/*implicit*/ true);
|
|
auto Body = BraceStmt::create(Context, SourceLoc(), ASTNode(Ret),
|
|
SourceLoc(), /*implicit*/ true);
|
|
Parameter->setBody(Body);
|
|
|
|
typeCheckDecl(Parameter, true);
|
|
typeCheckDecl(Parameter, false);
|
|
addMemberToContextIfNeeded(Parameter, DC);
|
|
|
|
// Add the witnesses to the conformance.
|
|
recordKnownWitness(BehaviorConformance, BehaviorParameter, Parameter);
|
|
}
|
|
|
|
void TypeChecker::completePropertyBehaviorAccessors(VarDecl *VD,
|
|
VarDecl *ValueImpl,
|
|
Type valueTy,
|
|
SubstitutionList SelfInterfaceSubs,
|
|
SubstitutionList SelfContextSubs) {
|
|
auto selfTy = SelfContextSubs[0].getReplacement();
|
|
auto selfIfaceTy = SelfInterfaceSubs[0].getReplacement();
|
|
|
|
SmallVector<ASTNode, 3> bodyStmts;
|
|
|
|
auto makeSelfExpr = [&](FuncDecl *fromAccessor,
|
|
FuncDecl *toAccessor) -> Expr * {
|
|
Expr *selfExpr;
|
|
if (VD->getDeclContext()->isTypeContext()) {
|
|
ConcreteDeclRef selfRef = fromAccessor->getImplicitSelfDecl();
|
|
selfExpr = new (Context) DeclRefExpr(selfRef, DeclNameLoc(),
|
|
/*implicit*/ true);
|
|
|
|
} else {
|
|
// self is the empty tuple outside of a type.
|
|
selfExpr = TupleExpr::createEmpty(Context, SourceLoc(), SourceLoc(),
|
|
/*implicit*/ true);
|
|
}
|
|
|
|
// If forwarding from a nonmutating to a mutating accessor, we need to put
|
|
// `self` in a mutable temporary.
|
|
auto fromMutating = VD->getDeclContext()->isTypeContext()
|
|
&& fromAccessor->getImplicitSelfDecl()->isSettable(fromAccessor);
|
|
|
|
if (!fromMutating
|
|
&& toAccessor->getImplicitSelfDecl()->isSettable(toAccessor)) {
|
|
selfExpr->setType(selfTy);
|
|
auto var = new (Context) VarDecl(/*IsStatic*/false,
|
|
VarDecl::Specifier::Var,
|
|
/*IsCaptureList*/false, SourceLoc(),
|
|
Context.getIdentifier("tempSelf"),
|
|
selfTy, fromAccessor);
|
|
var->setInterfaceType(selfIfaceTy);
|
|
|
|
auto varPat = new (Context) NamedPattern(var);
|
|
auto pbd = PatternBindingDecl::create(Context, SourceLoc(),
|
|
StaticSpellingKind::None,
|
|
SourceLoc(),
|
|
varPat, selfExpr,
|
|
fromAccessor);
|
|
bodyStmts.push_back(var);
|
|
bodyStmts.push_back(pbd);
|
|
selfExpr = new (Context) DeclRefExpr(var, DeclNameLoc(),
|
|
/*implicit*/ true);
|
|
}
|
|
assert((!fromMutating
|
|
|| toAccessor->getImplicitSelfDecl()->isSettable(toAccessor))
|
|
&& "can't forward from mutating to nonmutating");
|
|
if (!toAccessor->isMutating()) {
|
|
selfExpr->setType(selfTy);
|
|
} else {
|
|
// Access the base as inout if the accessor is mutating.
|
|
auto lvTy = LValueType::get(selfTy);
|
|
selfExpr->setType(lvTy);
|
|
selfExpr->propagateLValueAccessKind(AccessKind::ReadWrite);
|
|
selfExpr = new (Context) InOutExpr(SourceLoc(),
|
|
selfExpr, selfTy, /*implicit*/ true);
|
|
}
|
|
return selfExpr;
|
|
};
|
|
|
|
{
|
|
auto getter = VD->getGetter();
|
|
assert(getter);
|
|
|
|
Expr *selfExpr = makeSelfExpr(getter, ValueImpl->getGetter());
|
|
|
|
auto implRef = ConcreteDeclRef(Context, ValueImpl, SelfContextSubs);
|
|
auto implMemberExpr = new (Context) MemberRefExpr(selfExpr,
|
|
SourceLoc(),
|
|
implRef,
|
|
DeclNameLoc(),
|
|
/*implicit*/ true);
|
|
Expr *returnExpr;
|
|
if (ValueImpl->isSettable(VD->getDeclContext())) {
|
|
auto valueLVTy = LValueType::get(valueTy);
|
|
implMemberExpr->setType(valueLVTy);
|
|
implMemberExpr->propagateLValueAccessKind(AccessKind::Read);
|
|
returnExpr = new (Context) LoadExpr(implMemberExpr,
|
|
valueTy);
|
|
returnExpr->setImplicit();
|
|
} else {
|
|
implMemberExpr->setType(valueTy);
|
|
returnExpr = implMemberExpr;
|
|
}
|
|
auto returnStmt = new (Context) ReturnStmt(SourceLoc(), returnExpr,
|
|
/*implicit*/ true);
|
|
bodyStmts.push_back(returnStmt);
|
|
auto body = BraceStmt::create(Context, SourceLoc(), bodyStmts, SourceLoc(),
|
|
/*implicit*/ true);
|
|
getter->setBody(body);
|
|
getter->setBodyTypeCheckedIfPresent();
|
|
}
|
|
|
|
bodyStmts.clear();
|
|
|
|
if (auto setter = VD->getSetter()) {
|
|
Expr *selfExpr = makeSelfExpr(setter, ValueImpl->getSetter());
|
|
auto implRef = ConcreteDeclRef(Context, ValueImpl, SelfContextSubs);
|
|
auto implMemberExpr = new (Context) MemberRefExpr(selfExpr,
|
|
SourceLoc(),
|
|
implRef,
|
|
DeclNameLoc(),
|
|
/*implicit*/ true);
|
|
auto valueLVTy = LValueType::get(valueTy);
|
|
implMemberExpr->setType(valueLVTy);
|
|
implMemberExpr->propagateLValueAccessKind(AccessKind::Write);
|
|
|
|
ConcreteDeclRef newValueRef = getFirstParamDecl(setter);
|
|
auto newValueExpr = new (Context) DeclRefExpr(newValueRef, DeclNameLoc(),
|
|
/*implicit*/ true);
|
|
newValueExpr->setType(valueTy);
|
|
|
|
auto assign = new (Context) AssignExpr(implMemberExpr, SourceLoc(),
|
|
newValueExpr, /*implicit*/ true);
|
|
assign->setType(TupleType::getEmpty(Context));
|
|
|
|
bodyStmts.push_back(assign);
|
|
auto body = BraceStmt::create(Context, SourceLoc(), bodyStmts, SourceLoc(),
|
|
/*implicit*/ true);
|
|
setter->setBody(body);
|
|
setter->setBodyTypeCheckedIfPresent();
|
|
}
|
|
}
|
|
|
|
void TypeChecker::completeLazyVarImplementation(VarDecl *VD) {
|
|
assert(VD->getAttrs().hasAttribute<LazyAttr>());
|
|
assert(VD->getStorageKind() == AbstractStorageDecl::Computed &&
|
|
"variable not validated yet");
|
|
assert(!VD->isStatic() && "Static vars are already lazy on their own");
|
|
|
|
// Create the storage property as an optional of VD's type.
|
|
SmallString<64> NameBuf = VD->getName().str();
|
|
NameBuf += ".storage";
|
|
auto StorageName = Context.getIdentifier(NameBuf);
|
|
auto StorageTy = OptionalType::get(VD->getType());
|
|
auto StorageInterfaceTy = OptionalType::get(VD->getInterfaceType());
|
|
|
|
auto *Storage = new (Context) VarDecl(/*IsStatic*/false, VarDecl::Specifier::Var,
|
|
/*IsCaptureList*/false, VD->getLoc(),
|
|
StorageName, StorageTy,
|
|
VD->getDeclContext());
|
|
Storage->setInterfaceType(StorageInterfaceTy);
|
|
Storage->setUserAccessible(false);
|
|
addMemberToContextIfNeeded(Storage, VD->getDeclContext(), VD);
|
|
|
|
// Create the pattern binding decl for the storage decl. This will get
|
|
// default initialized to nil.
|
|
Pattern *PBDPattern = new (Context) NamedPattern(Storage, /*implicit*/true);
|
|
PBDPattern = new (Context) TypedPattern(PBDPattern,
|
|
TypeLoc::withoutLoc(StorageTy),
|
|
/*implicit*/true);
|
|
auto *PBD = PatternBindingDecl::create(Context, /*staticloc*/SourceLoc(),
|
|
StaticSpellingKind::None,
|
|
/*varloc*/VD->getLoc(),
|
|
PBDPattern, /*init*/nullptr,
|
|
VD->getDeclContext());
|
|
PBD->setImplicit();
|
|
addMemberToContextIfNeeded(PBD, VD->getDeclContext(), VD);
|
|
|
|
// Now that we've got the storage squared away, synthesize the getter.
|
|
completeLazyPropertyGetter(VD, Storage, *this);
|
|
|
|
// The setter just forwards on to storage without materializing the initial
|
|
// value.
|
|
auto *Set = VD->getSetter();
|
|
VarDecl *SetValueDecl = getFirstParamDecl(Set);
|
|
// FIXME: This is wrong for observed properties.
|
|
synthesizeTrivialSetter(Set, Storage, SetValueDecl, *this);
|
|
|
|
// Mark the vardecl to be final, implicit, and private. In a class, this
|
|
// prevents it from being dynamically dispatched. Note that we do this after
|
|
// the accessors are set up, because we don't want the setter for the lazy
|
|
// property to inherit these properties from the storage.
|
|
if (VD->getDeclContext()->getAsClassOrClassExtensionContext())
|
|
makeFinal(Context, Storage);
|
|
Storage->setImplicit();
|
|
Storage->setAccess(AccessLevel::Private);
|
|
Storage->setSetterAccess(AccessLevel::Private);
|
|
}
|
|
|
|
/// Consider add a materializeForSet accessor to the given storage
|
|
/// decl (which has accessors).
|
|
void swift::maybeAddMaterializeForSet(AbstractStorageDecl *storage,
|
|
TypeChecker &TC) {
|
|
assert(storage->hasAccessorFunctions());
|
|
|
|
// Be idempotent. There are a bunch of places where we want to
|
|
// ensure that there's a materializeForSet accessor.
|
|
if (storage->getMaterializeForSetFunc()) return;
|
|
|
|
// Never add materializeForSet to readonly declarations.
|
|
if (!storage->getSetter()) return;
|
|
|
|
// We only need materializeForSet in type contexts.
|
|
auto *dc = storage->getDeclContext();
|
|
if (!dc->isTypeContext())
|
|
return;
|
|
|
|
// Requirements of ObjC protocols don't need this.
|
|
if (auto protoDecl = dyn_cast<ProtocolDecl>(dc))
|
|
if (protoDecl->isObjC())
|
|
return;
|
|
|
|
// Members of structs imported by Clang don't need this, because we can
|
|
// synthesize it later.
|
|
if (auto structDecl = dyn_cast<StructDecl>(dc))
|
|
if (structDecl->hasClangNode())
|
|
return;
|
|
|
|
addMaterializeForSet(storage, TC);
|
|
}
|
|
|
|
void swift::maybeAddAccessorsToVariable(VarDecl *var, TypeChecker &TC) {
|
|
if (var->getGetter())
|
|
return;
|
|
|
|
auto *dc = var->getDeclContext();
|
|
|
|
assert(!var->hasAccessorFunctions());
|
|
|
|
// Introduce accessors for a property with behaviors.
|
|
if (var->hasBehavior()) {
|
|
assert(!var->getBehavior()->Conformance.hasValue());
|
|
|
|
// The property should be considered computed by the time we're through.
|
|
SWIFT_DEFER {
|
|
assert(!var->hasStorage() && "behavior var was not made computed");
|
|
};
|
|
|
|
auto behavior = var->getMutableBehavior();
|
|
NormalProtocolConformance *conformance = nullptr;
|
|
VarDecl *valueProp = nullptr;
|
|
|
|
bool mightBeMutating = dc->isTypeContext()
|
|
&& !var->isStatic()
|
|
&& !dc->getDeclaredInterfaceType()->hasReferenceSemantics();
|
|
|
|
auto makeBehaviorAccessors = [&]{
|
|
AccessorDecl *getter;
|
|
AccessorDecl *setter = nullptr;
|
|
if (valueProp && valueProp->getGetter()) {
|
|
getter = createGetterPrototype(var, TC);
|
|
// The getter is mutating if the behavior implementation is, unless
|
|
// we're in a class or non-instance context.
|
|
if (mightBeMutating && valueProp->isGetterMutating())
|
|
getter->setSelfAccessKind(SelfAccessKind::Mutating);
|
|
|
|
getter->setAccess(var->getFormalAccess());
|
|
|
|
// Make a setter if the behavior property has one.
|
|
if (valueProp->getSetter()) {
|
|
ParamDecl *newValueParam = nullptr;
|
|
setter = createSetterPrototype(var, newValueParam, TC);
|
|
if (mightBeMutating && valueProp->isSetterMutating())
|
|
setter->setSelfAccessKind(SelfAccessKind::Mutating);
|
|
// TODO: max of property and implementation setter visibility?
|
|
setter->setAccess(var->getFormalAccess());
|
|
}
|
|
} else {
|
|
// Even if we couldn't find a value property, still make up a stub
|
|
// getter and setter, so that subsequent diagnostics make sense for a
|
|
// computed-ish property.
|
|
getter = createGetterPrototype(var, TC);
|
|
getter->setAccess(var->getFormalAccess());
|
|
ParamDecl *newValueParam = nullptr;
|
|
setter = createSetterPrototype(var, newValueParam, TC);
|
|
setter->setSelfAccessKind(SelfAccessKind::NonMutating);
|
|
setter->setAccess(var->getFormalAccess());
|
|
}
|
|
|
|
var->makeComputed(SourceLoc(), getter, setter, nullptr, SourceLoc());
|
|
|
|
// Save the conformance and 'value' decl for later type checking.
|
|
behavior->Conformance = conformance;
|
|
behavior->ValueDecl = valueProp;
|
|
|
|
addMemberToContextIfNeeded(getter, dc, var);
|
|
if (setter)
|
|
addMemberToContextIfNeeded(setter, dc, getter);
|
|
};
|
|
|
|
// Try to resolve the behavior to a protocol.
|
|
auto behaviorType = TC.resolveType(behavior->ProtocolName, dc,
|
|
TypeResolutionOptions());
|
|
if (!behaviorType) {
|
|
return makeBehaviorAccessors();
|
|
}
|
|
|
|
{
|
|
// The type must refer to a protocol.
|
|
auto behaviorProtoTy = behaviorType->getAs<ProtocolType>();
|
|
if (!behaviorProtoTy) {
|
|
TC.diagnose(behavior->getLoc(),
|
|
diag::property_behavior_not_protocol);
|
|
behavior->Conformance = (NormalProtocolConformance*)nullptr;
|
|
return makeBehaviorAccessors();
|
|
}
|
|
auto behaviorProto = behaviorProtoTy->getDecl();
|
|
|
|
// Validate the behavior protocol and all its extensions so we can do
|
|
// name lookup.
|
|
TC.validateDecl(behaviorProto);
|
|
for (auto ext : behaviorProto->getExtensions()) {
|
|
TC.validateExtension(ext);
|
|
}
|
|
|
|
// Look up the behavior protocol's "value" property, or bail if it doesn't
|
|
// have one. The property's accessors will decide whether the getter
|
|
// is mutating, and whether there's a setter. We'll type-check to make
|
|
// sure the property type matches later after validation.
|
|
auto lookup = TC.lookupMember(dc, behaviorProtoTy, TC.Context.Id_value);
|
|
for (auto found : lookup) {
|
|
if (auto foundVar = dyn_cast<VarDecl>(found.getValueDecl())) {
|
|
if (valueProp) {
|
|
TC.diagnose(behavior->getLoc(),
|
|
diag::property_behavior_protocol_reqt_ambiguous,
|
|
TC.Context.Id_value);
|
|
TC.diagnose(valueProp->getLoc(),
|
|
diag::property_behavior_protocol_reqt_here,
|
|
TC.Context.Id_value);
|
|
TC.diagnose(foundVar->getLoc(),
|
|
diag::property_behavior_protocol_reqt_here,
|
|
TC.Context.Id_value);
|
|
break;
|
|
}
|
|
|
|
valueProp = foundVar;
|
|
}
|
|
}
|
|
|
|
if (!valueProp) {
|
|
TC.diagnose(behavior->getLoc(),
|
|
diag::property_behavior_protocol_no_value);
|
|
return makeBehaviorAccessors();
|
|
}
|
|
|
|
TC.validateDecl(valueProp);
|
|
var->setIsGetterMutating(mightBeMutating &&
|
|
valueProp->isGetterMutating());
|
|
var->setIsSetterMutating(mightBeMutating &&
|
|
valueProp->isSetterMutating());
|
|
|
|
// Set up a conformance to represent the behavior instantiation.
|
|
// The conformance will be on the containing 'self' type, or '()' if the
|
|
// property is in a non-type context.
|
|
Type behaviorSelf;
|
|
if (dc->isTypeContext()) {
|
|
behaviorSelf = dc->getSelfInterfaceType();
|
|
assert(behaviorSelf && "type context doesn't have self type?!");
|
|
if (var->isStatic())
|
|
behaviorSelf = MetatypeType::get(behaviorSelf);
|
|
} else {
|
|
behaviorSelf = TC.Context.TheEmptyTupleType;
|
|
}
|
|
|
|
conformance = TC.Context.getBehaviorConformance(behaviorSelf,
|
|
behaviorProto,
|
|
behavior->getLoc(), var,
|
|
ProtocolConformanceState::Checking);
|
|
}
|
|
return makeBehaviorAccessors();
|
|
}
|
|
|
|
// Lazy properties require special handling.
|
|
if (var->getAttrs().hasAttribute<LazyAttr>()) {
|
|
auto *getter = createGetterPrototype(var, TC);
|
|
// lazy getters are mutating on an enclosing value type.
|
|
if (!dc->getAsClassOrClassExtensionContext()) {
|
|
getter->setSelfAccessKind(SelfAccessKind::Mutating);
|
|
var->setIsGetterMutating(true);
|
|
}
|
|
getter->setAccess(var->getFormalAccess());
|
|
|
|
ParamDecl *newValueParam = nullptr;
|
|
auto *setter = createSetterPrototype(var, newValueParam, TC);
|
|
|
|
AccessorDecl *materializeForSet = nullptr;
|
|
if (dc->getAsNominalTypeOrNominalTypeExtensionContext())
|
|
materializeForSet = createMaterializeForSetPrototype(var,
|
|
getter, setter,
|
|
TC);
|
|
|
|
var->makeComputed(SourceLoc(), getter, setter, materializeForSet, SourceLoc());
|
|
|
|
addMemberToContextIfNeeded(getter, dc, var);
|
|
addMemberToContextIfNeeded(setter, dc, getter);
|
|
if (materializeForSet)
|
|
addMemberToContextIfNeeded(materializeForSet, dc, setter);
|
|
return;
|
|
}
|
|
|
|
// Local variables don't otherwise get accessors.
|
|
if (dc->isLocalContext())
|
|
return;
|
|
|
|
// Implicit properties don't get accessors.
|
|
if (var->isImplicit())
|
|
return;
|
|
|
|
if (!dc->isTypeContext()) {
|
|
// Fixed-layout global variables don't get accessors.
|
|
if (!var->isResilient())
|
|
return;
|
|
|
|
// In a protocol context, variables written as just "var x : Int" or
|
|
// "let x : Int" are errors and recovered by building a computed property
|
|
// with just a getter. Diagnose this and create the getter decl now.
|
|
} else if (isa<ProtocolDecl>(dc)) {
|
|
if (var->hasStorage()) {
|
|
if (var->isLet())
|
|
TC.diagnose(var->getLoc(),
|
|
diag::protocol_property_must_be_computed_var);
|
|
else
|
|
TC.diagnose(var->getLoc(), diag::protocol_property_must_be_computed);
|
|
|
|
convertStoredVarInProtocolToComputed(var, TC);
|
|
}
|
|
return;
|
|
|
|
// NSManaged properties on classes require special handling.
|
|
} else if (dc->getAsClassOrClassExtensionContext()) {
|
|
if (var->getAttrs().hasAttribute<NSManagedAttr>()) {
|
|
convertNSManagedStoredVarToComputed(var, TC);
|
|
return;
|
|
}
|
|
|
|
// Stored properties imported from Clang don't get accessors.
|
|
} else if (auto *structDecl = dyn_cast<StructDecl>(dc)) {
|
|
if (structDecl->hasClangNode())
|
|
return;
|
|
}
|
|
|
|
// Stored properties in SIL mode don't get accessors.
|
|
if (auto sourceFile = dc->getParentSourceFile())
|
|
if (sourceFile->Kind == SourceFileKind::SIL)
|
|
return;
|
|
|
|
// Everything else gets accessors.
|
|
addTrivialAccessorsToStorage(var, TC);
|
|
}
|
|
|
|
/// \brief Create an implicit struct or class constructor.
|
|
///
|
|
/// \param decl The struct or class for which a constructor will be created.
|
|
/// \param ICK The kind of implicit constructor to create.
|
|
///
|
|
/// \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) {
|
|
assert(!decl->hasClangNode());
|
|
|
|
ASTContext &context = tc.Context;
|
|
SourceLoc Loc = decl->getLoc();
|
|
auto accessLevel = AccessLevel::Internal;
|
|
|
|
// Determine the parameter type of the implicit constructor.
|
|
SmallVector<ParamDecl*, 8> params;
|
|
if (ICK == ImplicitConstructorKind::Memberwise) {
|
|
assert(isa<StructDecl>(decl) && "Only struct have memberwise constructor");
|
|
|
|
// Computed and static properties are not initialized.
|
|
for (auto var : decl->getStoredProperties()) {
|
|
if (var->isImplicit())
|
|
continue;
|
|
tc.validateDecl(var);
|
|
|
|
// 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->getParentInitializer())
|
|
continue;
|
|
|
|
accessLevel = std::min(accessLevel, var->getFormalAccess());
|
|
|
|
auto varType = var->getType()
|
|
->getReferenceStorageReferent();
|
|
auto varInterfaceType = var->getInterfaceType()
|
|
->getReferenceStorageReferent();
|
|
|
|
// If var is a lazy property, its value is provided for the underlying
|
|
// storage. We thus take an optional of the properties type. We only
|
|
// need to do this because the implicit constructor is added before all
|
|
// the properties are type checked. Perhaps init() synth should be moved
|
|
// later.
|
|
if (var->getAttrs().hasAttribute<LazyAttr>()) {
|
|
varType = OptionalType::get(varType);
|
|
varInterfaceType = OptionalType::get(varInterfaceType);
|
|
}
|
|
|
|
// Create the parameter.
|
|
auto *arg = new (context)
|
|
ParamDecl(VarDecl::Specifier::Default, SourceLoc(), Loc,
|
|
var->getName(), Loc, var->getName(), varType, decl);
|
|
arg->setInterfaceType(varInterfaceType);
|
|
arg->setImplicit();
|
|
|
|
params.push_back(arg);
|
|
}
|
|
}
|
|
|
|
auto paramList = ParameterList::create(context, params);
|
|
|
|
// Create the constructor.
|
|
DeclName name(context, DeclBaseName::createConstructor(), paramList);
|
|
auto *selfParam = ParamDecl::createSelf(Loc, decl,
|
|
/*static*/false, /*inout*/true);
|
|
auto *ctor =
|
|
new (context) ConstructorDecl(name, Loc,
|
|
OTK_None, /*FailabilityLoc=*/SourceLoc(),
|
|
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
|
|
selfParam, paramList,
|
|
nullptr, decl);
|
|
|
|
// Mark implicit.
|
|
ctor->setImplicit();
|
|
ctor->setAccess(accessLevel);
|
|
|
|
if (ICK == ImplicitConstructorKind::Memberwise)
|
|
ctor->setIsMemberwiseInitializer();
|
|
|
|
// If we are defining a default initializer for a class that has a superclass,
|
|
// it overrides the default initializer of its superclass. Add an implicit
|
|
// 'override' attribute.
|
|
if (auto classDecl = dyn_cast<ClassDecl>(decl)) {
|
|
if (classDecl->getSuperclass())
|
|
ctor->getAttrs().add(new (tc.Context) OverrideAttr(/*IsImplicit=*/true));
|
|
}
|
|
|
|
// Type-check the constructor declaration.
|
|
tc.validateDecl(ctor);
|
|
|
|
return ctor;
|
|
}
|
|
|
|
/// Create a stub body that emits a fatal error message.
|
|
static void createStubBody(TypeChecker &tc, ConstructorDecl *ctor) {
|
|
auto unimplementedInitDecl = tc.Context.getUnimplementedInitializerDecl(&tc);
|
|
auto classDecl = ctor->getDeclContext()->getAsClassOrClassExtensionContext();
|
|
if (!unimplementedInitDecl) {
|
|
tc.diagnose(classDecl->getLoc(), diag::missing_unimplemented_init_runtime);
|
|
return;
|
|
}
|
|
|
|
// Create a call to Swift._unimplementedInitializer
|
|
auto loc = classDecl->getLoc();
|
|
Expr *fn = new (tc.Context) DeclRefExpr(unimplementedInitDecl,
|
|
DeclNameLoc(loc),
|
|
/*Implicit=*/true);
|
|
|
|
llvm::SmallString<64> buffer;
|
|
StringRef fullClassName = tc.Context.AllocateCopy(
|
|
(classDecl->getModuleContext()->getName().str() +
|
|
"." +
|
|
classDecl->getName().str()).toStringRef(buffer));
|
|
|
|
Expr *className = new (tc.Context) StringLiteralExpr(fullClassName, loc,
|
|
/*Implicit=*/true);
|
|
Expr *call = CallExpr::createImplicit(tc.Context, fn, { className },
|
|
{ tc.Context.Id_className });
|
|
ctor->setBody(BraceStmt::create(tc.Context, SourceLoc(),
|
|
ASTNode(call),
|
|
SourceLoc(),
|
|
/*implicit=*/true));
|
|
|
|
// Note that this is a stub implementation.
|
|
ctor->setStubImplementation(true);
|
|
}
|
|
|
|
ConstructorDecl *
|
|
swift::createDesignatedInitOverride(TypeChecker &tc,
|
|
ClassDecl *classDecl,
|
|
ConstructorDecl *superclassCtor,
|
|
DesignatedInitKind kind) {
|
|
// FIXME: Inheriting initializers that have their own generic parameters
|
|
if (superclassCtor->getGenericParams())
|
|
return nullptr;
|
|
|
|
// 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
|
|
// or an extension. We can use the type declared in that context to check
|
|
// if it's our immediate superclass and give up if we didn't.
|
|
//
|
|
// FIXME: Remove this when lookup of initializers becomes restricted to our
|
|
// immediate superclass.
|
|
auto *superclassCtorDecl =
|
|
superclassCtor->getDeclContext()
|
|
->getAsNominalTypeOrNominalTypeExtensionContext();
|
|
Type superclassTy = classDecl->getSuperclass();
|
|
Type superclassTyInContext = classDecl->mapTypeIntoContext(superclassTy);
|
|
NominalTypeDecl *superclassDecl = superclassTy->getAnyNominal();
|
|
if (superclassCtorDecl != superclassDecl) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Determine the initializer parameters.
|
|
auto &ctx = tc.Context;
|
|
|
|
// Create the 'self' declaration and patterns.
|
|
auto *selfDecl = ParamDecl::createSelf(SourceLoc(), classDecl);
|
|
|
|
// Create the initializer parameter patterns.
|
|
OptionSet<ParameterList::CloneFlags> options = ParameterList::Implicit;
|
|
options |= ParameterList::Inherited;
|
|
auto *bodyParams = superclassCtor->getParameterList(1)->clone(ctx,options);
|
|
|
|
// If the superclass is generic, we need to map the superclass constructor's
|
|
// parameter types into the generic context of our class.
|
|
//
|
|
// We might have to apply substitutions, if for example we have a declaration
|
|
// like 'class A : B<Int>'.
|
|
if (superclassDecl->getGenericSignatureOfContext()) {
|
|
auto *moduleDecl = classDecl->getParentModule();
|
|
auto subMap = superclassTyInContext->getContextSubstitutionMap(
|
|
moduleDecl,
|
|
superclassDecl,
|
|
classDecl->getGenericEnvironment());
|
|
|
|
for (auto *decl : *bodyParams) {
|
|
auto paramTy = decl->getInterfaceType()->getInOutObjectType();
|
|
|
|
// Apply the superclass substitutions to produce a contextual
|
|
// type in terms of the derived class archetypes.
|
|
auto paramSubstTy = paramTy.subst(subMap);
|
|
decl->setType(paramSubstTy);
|
|
|
|
// Map it to an interface type in terms of the derived class
|
|
// generic signature.
|
|
decl->setInterfaceType(paramSubstTy->mapTypeOutOfContext());
|
|
}
|
|
} else {
|
|
for (auto *decl : *bodyParams) {
|
|
if (!decl->hasType())
|
|
decl->setType(
|
|
classDecl->mapTypeIntoContext(
|
|
decl->getInterfaceType()->getInOutObjectType()));
|
|
}
|
|
}
|
|
|
|
// Create the initializer declaration, inheriting the name,
|
|
// failability, and throws from the superclass initializer.
|
|
auto ctor =
|
|
new (ctx) ConstructorDecl(superclassCtor->getFullName(),
|
|
classDecl->getBraces().Start,
|
|
superclassCtor->getFailability(),
|
|
/*FailabilityLoc=*/SourceLoc(),
|
|
/*Throws=*/superclassCtor->hasThrows(),
|
|
/*ThrowsLoc=*/SourceLoc(),
|
|
selfDecl, bodyParams,
|
|
/*GenericParams=*/nullptr, classDecl);
|
|
|
|
ctor->setImplicit();
|
|
|
|
AccessLevel access = classDecl->getFormalAccess();
|
|
access = std::max(access, AccessLevel::Internal);
|
|
access = std::min(access, superclassCtor->getFormalAccess());
|
|
ctor->setAccess(access);
|
|
|
|
// Inherit the @usableFromInline attribute.
|
|
if (superclassCtor->getAttrs().hasAttribute<UsableFromInlineAttr>()) {
|
|
auto *clonedAttr = new (ctx) UsableFromInlineAttr(/*implicit=*/true);
|
|
ctor->getAttrs().add(clonedAttr);
|
|
}
|
|
|
|
// Inherit the @inlinable attribute.
|
|
if (superclassCtor->getAttrs().hasAttribute<InlinableAttr>()) {
|
|
auto *clonedAttr = new (ctx) InlinableAttr(/*implicit=*/true);
|
|
ctor->getAttrs().add(clonedAttr);
|
|
}
|
|
|
|
// Make sure the constructor is only as available as its superclass's
|
|
// constructor.
|
|
AvailabilityInference::applyInferredAvailableAttrs(ctor, superclassCtor, ctx);
|
|
|
|
// Set the interface type of the initializer.
|
|
ctor->setGenericEnvironment(classDecl->getGenericEnvironmentOfContext());
|
|
tc.configureInterfaceType(ctor, ctor->getGenericSignature());
|
|
|
|
if (superclassCtor->isObjC()) {
|
|
// Inherit the @objc name from the superclass initializer, if it
|
|
// has one.
|
|
if (auto objcAttr = superclassCtor->getAttrs().getAttribute<ObjCAttr>()) {
|
|
if (objcAttr->hasName()) {
|
|
auto *clonedAttr = objcAttr->clone(ctx);
|
|
clonedAttr->setImplicit(true);
|
|
ctor->getAttrs().add(clonedAttr);
|
|
}
|
|
}
|
|
|
|
auto errorConvention = superclassCtor->getForeignErrorConvention();
|
|
markAsObjC(tc, ctor, ObjCReason::ImplicitlyObjC, errorConvention);
|
|
}
|
|
if (superclassCtor->isRequired())
|
|
ctor->getAttrs().add(new (tc.Context) RequiredAttr(/*IsImplicit=*/true));
|
|
if (superclassCtor->isDynamic())
|
|
ctor->getAttrs().add(new (tc.Context) DynamicAttr(/*IsImplicit*/true));
|
|
|
|
// Wire up the overrides.
|
|
ctor->getAttrs().add(new (tc.Context) OverrideAttr(/*IsImplicit=*/true));
|
|
ctor->setOverriddenDecl(superclassCtor);
|
|
ctor->setValidationStarted();
|
|
|
|
if (kind == DesignatedInitKind::Stub) {
|
|
// Make this a stub implementation.
|
|
createStubBody(tc, ctor);
|
|
ctor->setNeedsNewVTableEntry(false);
|
|
return ctor;
|
|
}
|
|
|
|
// Form the body of a chaining designated initializer.
|
|
assert(kind == DesignatedInitKind::Chaining);
|
|
|
|
// Reference to super.init.
|
|
Expr *superRef = new (ctx) SuperRefExpr(selfDecl, SourceLoc(),
|
|
/*Implicit=*/true);
|
|
Expr *ctorRef = new (ctx) UnresolvedDotExpr(superRef, SourceLoc(),
|
|
superclassCtor->getFullName(),
|
|
DeclNameLoc(),
|
|
/*Implicit=*/true);
|
|
|
|
auto ctorArgs = buildArgumentForwardingExpr(bodyParams->getArray(), ctx);
|
|
|
|
// If buildArgumentForwardingExpr failed, then it was because we tried to
|
|
// forward varargs, which cannot be done yet.
|
|
// TODO: We should be able to forward varargs!
|
|
if (!ctorArgs) {
|
|
tc.diagnose(classDecl->getLoc(),
|
|
diag::unsupported_synthesize_init_variadic,
|
|
classDecl->getDeclaredType());
|
|
tc.diagnose(superclassCtor, diag::variadic_superclass_init_here);
|
|
createStubBody(tc, ctor);
|
|
return ctor;
|
|
}
|
|
|
|
Expr *superCall =
|
|
CallExpr::create(ctx, ctorRef, ctorArgs,
|
|
superclassCtor->getFullName().getArgumentNames(), { },
|
|
/*hasTrailingClosure=*/false, /*implicit=*/true);
|
|
if (superclassCtor->hasThrows()) {
|
|
superCall = new (ctx) TryExpr(SourceLoc(), superCall, Type(),
|
|
/*implicit=*/true);
|
|
}
|
|
ctor->setBody(BraceStmt::create(tc.Context, SourceLoc(),
|
|
ASTNode(superCall),
|
|
SourceLoc(),
|
|
/*implicit=*/true));
|
|
|
|
return ctor;
|
|
}
|