mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Parameters (to methods, initializers, accessors, subscripts, etc) have always been represented
as Pattern's (of a particular sort), stemming from an early design direction that was abandoned.
Being built on top of patterns leads to patterns being overly complicated (e.g. tuple patterns
have to have varargs and default parameters) and make working on parameter lists complicated
and error prone. This might have been ok in 2015, but there is no way we can live like this in
2016.
Instead of using Patterns, carve out a new ParameterList and Parameter type to represent all the
parameter specific stuff. This simplifies many things and allows a lot of simplifications.
Unfortunately, I wasn't able to do this very incrementally, so this is a huge patch. The good
news is that it erases a ton of code, and the technical debt that went with it. Ignoring test
suite changes, we have:
77 files changed, 2359 insertions(+), 3221 deletions(-)
This patch also makes a bunch of wierd things dead, but I'll sweep those out in follow-on
patches.
Fixes <rdar://problem/22846558> No code completions in Foo( when Foo has error type
Fixes <rdar://problem/24026538> Slight regression in generated header, which I filed to go with 3a23d75.
Fixes an overloading bug involving default arguments and curried functions (see the diff to
Constraints/diagnostics.swift, which we now correctly accept).
Fixes cases where problems with parameters would get emitted multiple times, e.g. in the
test/Parse/subscripting.swift testcase.
The source range for ParamDecl now includes its type, which permutes some of the IDE / SourceModel tests
(for the better, I think).
Eliminates the bogus "type annotation missing in pattern" error message when a type isn't
specified for a parameter (see test/decl/func/functions.swift).
This now consistently parenthesizes argument lists in function types, which leads to many diffs in the
SILGen tests among others.
This does break the "sibling indentation" test in SourceKit/CodeFormat/indent-sibling.swift, and
I haven't been able to figure it out. Given that this is experimental functionality anyway,
I'm just XFAILing the test for now. i'll look at it separately from this mongo diff.
2092 lines
82 KiB
C++
2092 lines
82 KiB
C++
//===--- CodeSynthesis.cpp - Type Checking for Declarations ---------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://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/Attr.h"
|
|
#include "swift/AST/Availability.h"
|
|
#include "swift/AST/Expr.h"
|
|
#include "swift/AST/Parameter.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).decl;
|
|
}
|
|
|
|
static VarDecl *getFirstParamDecl(FuncDecl *fn) {
|
|
return getParamDeclAtIndex(fn, 0);
|
|
};
|
|
|
|
|
|
static Parameter buildArgument(SourceLoc loc, DeclContext *DC,
|
|
StringRef name, Type type, bool isLet) {
|
|
auto &context = DC->getASTContext();
|
|
auto *param = new (context) ParamDecl(isLet, SourceLoc(), Identifier(),
|
|
loc, context.getIdentifier(name),
|
|
Type(), DC);
|
|
param->setImplicit();
|
|
|
|
Parameter P = Parameter::withoutLoc(param);
|
|
P.type.setType(type);
|
|
return P;
|
|
}
|
|
|
|
static Parameter buildLetArgument(SourceLoc loc, DeclContext *DC,
|
|
StringRef name, Type type) {
|
|
return buildArgument(loc, DC, name, type, /*isLet*/ true);
|
|
}
|
|
|
|
static Parameter buildInOutArgument(SourceLoc loc, DeclContext *DC,
|
|
StringRef name, Type type) {
|
|
return buildArgument(loc, DC, name, InOutType::get(type), /*isLet*/ false);
|
|
}
|
|
|
|
static Type getTypeOfStorage(AbstractStorageDecl *storage,
|
|
TypeChecker &TC) {
|
|
if (auto var = dyn_cast<VarDecl>(storage)) {
|
|
return TC.getTypeOfRValue(var, /*want interface type*/ false);
|
|
} else {
|
|
// None of the transformations done by getTypeOfRValue are
|
|
// necessary for subscripts.
|
|
auto subscript = cast<SubscriptDecl>(storage);
|
|
return subscript->getElementType();
|
|
}
|
|
}
|
|
|
|
/// 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<Parameter> 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);
|
|
if (prefix.empty())
|
|
return indices;
|
|
|
|
|
|
// Otherwise, we need to build up a new parameter list.
|
|
SmallVector<Parameter, 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 FuncDecl *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())
|
|
getterParams.push_back(ParameterList::createSelf(loc,
|
|
storage->getDeclContext(),
|
|
/*isStatic*/false));
|
|
|
|
// 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 storageType = getTypeOfStorage(storage, TC);
|
|
|
|
auto getter = FuncDecl::create(
|
|
TC.Context, staticLoc, StaticSpellingKind::None, loc, Identifier(), loc,
|
|
SourceLoc(), SourceLoc(), /*GenericParams=*/nullptr, Type(), getterParams,
|
|
TypeLoc::withoutLoc(storageType), storage->getDeclContext());
|
|
getter->setImplicit();
|
|
|
|
if (storage->isGetterMutating())
|
|
getter->setMutating();
|
|
|
|
// If the var is marked final, then so is the getter.
|
|
if (storage->isFinal())
|
|
makeFinal(TC.Context, getter);
|
|
|
|
if (storage->isStatic())
|
|
getter->setStatic();
|
|
|
|
return getter;
|
|
}
|
|
|
|
static FuncDecl *createSetterPrototype(AbstractStorageDecl *storage,
|
|
ParamDecl *&valueDecl,
|
|
TypeChecker &TC) {
|
|
SourceLoc loc = storage->getLoc();
|
|
|
|
// Create the parameter list for the setter.
|
|
SmallVector<ParameterList*, 2> params;
|
|
|
|
// The implicit 'self' argument if in a type context.
|
|
if (storage->getDeclContext()->isTypeContext()) {
|
|
params.push_back(ParameterList::createSelf(loc,
|
|
storage->getDeclContext(),
|
|
/*isStatic*/false));
|
|
}
|
|
|
|
// Add a "(value : T, indices...)" argument list.
|
|
auto storageType = getTypeOfStorage(storage, TC);
|
|
auto valueParam = buildLetArgument(storage->getLoc(),
|
|
storage->getDeclContext(), "value",
|
|
storageType);
|
|
valueDecl = valueParam.decl;
|
|
params.push_back(buildIndexForwardingParamList(storage, valueParam));
|
|
|
|
Type setterRetTy = TupleType::getEmpty(TC.Context);
|
|
FuncDecl *setter = FuncDecl::create(
|
|
TC.Context, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, loc,
|
|
Identifier(), loc, SourceLoc(), SourceLoc(), /*generic=*/nullptr, Type(),
|
|
params, TypeLoc::withoutLoc(setterRetTy), storage->getDeclContext());
|
|
setter->setImplicit();
|
|
|
|
if (!storage->isSetterNonMutating())
|
|
setter->setMutating();
|
|
|
|
// If the var is marked final, then so is the getter.
|
|
if (storage->isFinal())
|
|
makeFinal(TC.Context, setter);
|
|
|
|
if (storage->isStatic())
|
|
setter->setStatic();
|
|
|
|
return setter;
|
|
}
|
|
|
|
/// Returns the type of the self argument of a materializeForSet
|
|
/// callback. If we don't have a meaningful direct self type, just
|
|
/// use something meaningless and hope it doesn't matter.
|
|
static Type getSelfTypeForMaterializeForSetCallback(ASTContext &ctx,
|
|
DeclContext *DC,
|
|
bool isStatic) {
|
|
Type selfType = DC->getDeclaredTypeInContext();
|
|
if (!selfType) {
|
|
// This restriction is theoretically liftable by writing the necessary
|
|
// contextual information into the callback storage.
|
|
assert(!DC->isGenericContext() &&
|
|
"no enclosing type for generic materializeForSet; callback "
|
|
"will not be able to bind type arguments!");
|
|
return TupleType::getEmpty(ctx);
|
|
}
|
|
|
|
// If we're in a protocol, we want to actually use the Self type.
|
|
if (selfType->is<ProtocolType>()) {
|
|
selfType = DC->getProtocolSelf()->getArchetype();
|
|
}
|
|
|
|
// Use the metatype if this is a static member.
|
|
if (isStatic) {
|
|
return MetatypeType::get(selfType, ctx);
|
|
} else {
|
|
return selfType;
|
|
}
|
|
}
|
|
|
|
// 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();
|
|
}
|
|
|
|
// True if a generated accessor needs to be registered as an external decl.
|
|
bool needsToBeRegisteredAsExternalDecl(AbstractStorageDecl *storage) {
|
|
// Either the storage itself was imported from Clang...
|
|
if (storage->hasClangNode())
|
|
return true;
|
|
|
|
// ...or it was synthesized into an imported type.
|
|
auto nominal = dyn_cast<NominalTypeDecl>(storage->getDeclContext());
|
|
if (!nominal)
|
|
return false;
|
|
return nominal->hasClangNode();
|
|
}
|
|
|
|
static Type createMaterializeForSetReturnType(AbstractStorageDecl *storage,
|
|
TypeChecker &TC) {
|
|
auto &ctx = storage->getASTContext();
|
|
SourceLoc loc = storage->getLoc();
|
|
|
|
auto DC = storage->getDeclContext();
|
|
|
|
if (DC->getDeclaredTypeInContext() &&
|
|
DC->getDeclaredTypeInContext()->is<ErrorType>()) {
|
|
return ErrorType::get(ctx);
|
|
}
|
|
|
|
Type callbackSelfType =
|
|
getSelfTypeForMaterializeForSetCallback(ctx, DC, storage->isStatic());
|
|
TupleTypeElt callbackArgs[] = {
|
|
ctx.TheRawPointerType,
|
|
InOutType::get(ctx.TheUnsafeValueBufferType),
|
|
InOutType::get(callbackSelfType),
|
|
MetatypeType::get(callbackSelfType, MetatypeRepresentation::Thick),
|
|
};
|
|
auto callbackExtInfo = FunctionType::ExtInfo()
|
|
.withRepresentation(FunctionType::Representation::Thin);
|
|
auto callbackType = FunctionType::get(TupleType::get(callbackArgs, ctx),
|
|
TupleType::getEmpty(ctx),
|
|
callbackExtInfo);
|
|
|
|
// Try to make the callback type optional. Don't crash if it doesn't
|
|
// work, though.
|
|
auto optCallbackType = TC.getOptionalType(loc, callbackType);
|
|
if (!optCallbackType) optCallbackType = callbackType;
|
|
|
|
TupleTypeElt retElts[] = {
|
|
{ ctx.TheRawPointerType },
|
|
{ optCallbackType },
|
|
};
|
|
return TupleType::get(retElts, ctx);
|
|
}
|
|
|
|
static FuncDecl *createMaterializeForSetPrototype(AbstractStorageDecl *storage,
|
|
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...).
|
|
|
|
Parameter bufferElements[] = {
|
|
buildLetArgument(loc, DC, "buffer", ctx.TheRawPointerType),
|
|
buildInOutArgument(loc, DC, "callbackStorage", ctx.TheUnsafeValueBufferType)
|
|
};
|
|
params.push_back(buildIndexForwardingParamList(storage, bufferElements));
|
|
|
|
// The accessor returns (Builtin.RawPointer, (@convention(thin) (...) -> ())?),
|
|
// where the first pointer is the materialized address and the
|
|
// second is an optional callback.
|
|
Type retTy = createMaterializeForSetReturnType(storage, TC);
|
|
|
|
auto *materializeForSet = FuncDecl::create(
|
|
ctx, /*StaticLoc=*/SourceLoc(), StaticSpellingKind::None, loc,
|
|
Identifier(), loc, SourceLoc(), SourceLoc(), /*generic=*/nullptr, Type(),
|
|
params, TypeLoc::withoutLoc(retTy), DC);
|
|
materializeForSet->setImplicit();
|
|
|
|
// materializeForSet is mutating and static if the setter is.
|
|
auto setter = storage->getSetter();
|
|
materializeForSet->setMutating(setter->isMutating());
|
|
materializeForSet->setStatic(setter->isStatic());
|
|
|
|
// materializeForSet is final if the storage is.
|
|
if (storage->isFinal())
|
|
makeFinal(ctx, materializeForSet);
|
|
|
|
// 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);
|
|
}
|
|
|
|
AvailabilityInference::applyInferredAvailableAttrs(materializeForSet,
|
|
asAvailableAs, ctx);
|
|
|
|
// If the property came from ObjC, we need to register this as an external
|
|
// definition to be compiled.
|
|
if (needsToBeRegisteredAsExternalDecl(storage))
|
|
TC.Context.addedExternalDecl(materializeForSet);
|
|
|
|
return materializeForSet;
|
|
}
|
|
|
|
void swift::convertStoredVarInProtocolToComputed(VarDecl *VD, TypeChecker &TC) {
|
|
auto *Get = createGetterPrototype(VD, TC);
|
|
|
|
// Okay, we have both the getter and setter. Set them in VD.
|
|
VD->makeComputed(VD->getLoc(), Get, nullptr, nullptr, VD->getLoc());
|
|
|
|
// We've added some members to our containing class, add them to the members
|
|
// list.
|
|
addMemberToContextIfNeeded(Get, VD->getDeclContext());
|
|
|
|
// Type check the getter declaration.
|
|
TC.typeCheckDecl(VD->getGetter(), true);
|
|
TC.typeCheckDecl(VD->getGetter(), false);
|
|
}
|
|
|
|
/// Build a tuple around the given arguments.
|
|
static Expr *buildTupleExpr(ASTContext &ctx, ArrayRef<Expr*> args) {
|
|
if (args.size() == 1) {
|
|
return args[0];
|
|
}
|
|
SmallVector<Identifier, 4> labels(args.size());
|
|
SmallVector<SourceLoc, 4> labelLocs(args.size());
|
|
return TupleExpr::create(ctx, SourceLoc(), args, labels, labelLocs,
|
|
SourceLoc(), false, IsImplicit);
|
|
}
|
|
|
|
|
|
/// 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<Parameter> 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.decl, SourceLoc(),
|
|
/*implicit*/ true);
|
|
if (param.decl->getType()->is<InOutType>())
|
|
ref = new (ctx) InOutExpr(SourceLoc(), ref, Type(), /*implicit=*/true);
|
|
args.push_back(ref);
|
|
|
|
labels.push_back(param.decl->getArgumentName());
|
|
labelLocs.push_back(SourceLoc());
|
|
}
|
|
|
|
// A single unlabelled value is not a tuple.
|
|
if (args.size() == 1 && labels[0].empty())
|
|
return args[0];
|
|
|
|
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, FuncDecl *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 SelfAccessKind {
|
|
/// 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,
|
|
SelfAccessKind selfAccessKind,
|
|
TypeChecker &TC) {
|
|
switch (selfAccessKind) {
|
|
case SelfAccessKind::Peer:
|
|
return new (TC.Context) DeclRefExpr(selfDecl, SourceLoc(), IsImplicit);
|
|
|
|
case SelfAccessKind::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 {
|
|
FuncDecl *Accessor;
|
|
public:
|
|
AccessorStorageReferenceContext(FuncDecl *accessor) : Accessor(accessor) {}
|
|
virtual ~AccessorStorageReferenceContext() = default;
|
|
|
|
VarDecl *getSelfDecl() const override {
|
|
return Accessor->getImplicitSelfDecl();
|
|
}
|
|
Expr *getIndexRefExpr(ASTContext &ctx,
|
|
SubscriptDecl *subscript) const override {
|
|
return buildSubscriptIndexReference(ctx, Accessor);
|
|
}
|
|
};
|
|
}
|
|
|
|
/// Build an l-value for the storage of a declaration.
|
|
static Expr *buildStorageReference(
|
|
const StorageReferenceContext &referenceContext,
|
|
AbstractStorageDecl *storage,
|
|
AccessSemantics semantics,
|
|
SelfAccessKind selfAccessKind,
|
|
TypeChecker &TC) {
|
|
ASTContext &ctx = TC.Context;
|
|
|
|
VarDecl *selfDecl = referenceContext.getSelfDecl();
|
|
if (!selfDecl) {
|
|
return new (ctx) DeclRefExpr(storage, SourceLoc(), IsImplicit, semantics);
|
|
}
|
|
|
|
// If we should use a super access if applicable, and we have an
|
|
// overridden decl, then use ordinary access to it.
|
|
if (selfAccessKind == SelfAccessKind::Super) {
|
|
if (auto overridden = storage->getOverriddenDecl()) {
|
|
storage = overridden;
|
|
semantics = AccessSemantics::Ordinary;
|
|
} else {
|
|
selfAccessKind = SelfAccessKind::Peer;
|
|
}
|
|
}
|
|
|
|
Expr *selfDRE = buildSelfReference(selfDecl, selfAccessKind, TC);
|
|
|
|
if (auto subscript = dyn_cast<SubscriptDecl>(storage)) {
|
|
Expr *indices = referenceContext.getIndexRefExpr(ctx, subscript);
|
|
return new (ctx) SubscriptExpr(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,
|
|
SourceLoc(), IsImplicit, semantics);
|
|
}
|
|
|
|
static Expr *buildStorageReference(FuncDecl *accessor,
|
|
AbstractStorageDecl *storage,
|
|
AccessSemantics semantics,
|
|
SelfAccessKind selfAccessKind,
|
|
TypeChecker &TC) {
|
|
return buildStorageReference(AccessorStorageReferenceContext(accessor),
|
|
storage, semantics, selfAccessKind, 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(FuncDecl *accessor,
|
|
AbstractStorageDecl *storage,
|
|
TypeChecker &TC) {
|
|
return buildStorageReference(accessor, storage,
|
|
AccessSemantics::DirectToStorage,
|
|
SelfAccessKind::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.getIdentifier("NSCopying"),
|
|
NL_QualifiedDefault | NL_KnownNonCascadingDependency,
|
|
/*resolver=*/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.copyWithZone(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 = TC.getTypeOfRValue(VD, /*want interface type*/false);
|
|
|
|
bool isOptional = false;
|
|
if (Type optionalEltTy = UnderlyingType->getAnyOptionalObjectType()) {
|
|
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 'copyWithZone'
|
|
// "Val")
|
|
// (paren_expr type='<null>'
|
|
// (nil_literal_expr type='<null>'))))
|
|
auto UDE = new (Ctx) UnresolvedDotExpr(Val, SourceLoc(),
|
|
Ctx.getIdentifier("copyWithZone"),
|
|
SourceLoc(), /*implicit*/true);
|
|
Expr *Nil = new (Ctx) NilLiteralExpr(SourceLoc(), /*implicit*/true);
|
|
Nil = new (Ctx) ParenExpr(SourceLoc(), Nil, SourceLoc(), false);
|
|
|
|
//- (id)copyWithZone:(NSZone *)zone;
|
|
Expr *Call = new (Ctx) CallExpr(UDE, Nil, /*implicit*/true);
|
|
|
|
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(FuncDecl *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,
|
|
SelfAccessKind::Super, TC);
|
|
|
|
body.push_back(new (TC.Context) AssignExpr(dest, SourceLoc(), value,
|
|
IsImplicit));
|
|
}
|
|
|
|
/// 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).
|
|
static void maybeMarkTransparent(FuncDecl *accessor,
|
|
AbstractStorageDecl *storage,
|
|
TypeChecker &TC) {
|
|
auto *NTD = storage->getDeclContext()
|
|
->isNominalTypeOrNominalTypeExtensionContext();
|
|
|
|
// FIXME: resilient global variables
|
|
if (!NTD || NTD->hasFixedLayout())
|
|
accessor->getAttrs().add(new (TC.Context) TransparentAttr(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(FuncDecl *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));
|
|
|
|
maybeMarkTransparent(getter, storage, TC);
|
|
|
|
// Register the accessor as an external decl if the storage was imported.
|
|
if (needsToBeRegisteredAsExternalDecl(storage))
|
|
TC.Context.addedExternalDecl(getter);
|
|
}
|
|
|
|
/// Synthesize the body of a trivial setter.
|
|
static void synthesizeTrivialSetter(FuncDecl *setter,
|
|
AbstractStorageDecl *storage,
|
|
VarDecl *valueVar,
|
|
TypeChecker &TC) {
|
|
if (storage->isInvalid()) return;
|
|
|
|
auto &ctx = TC.Context;
|
|
SourceLoc loc = storage->getLoc();
|
|
|
|
auto *valueDRE = new (ctx) DeclRefExpr(valueVar, SourceLoc(), IsImplicit);
|
|
SmallVector<ASTNode, 1> setterBody;
|
|
createPropertyStoreOrCallSuperclassSetter(setter, valueDRE, storage,
|
|
setterBody, TC);
|
|
setter->setBody(BraceStmt::create(ctx, loc, setterBody, loc, true));
|
|
|
|
maybeMarkTransparent(setter, storage, TC);
|
|
|
|
// Register the accessor as an external decl if the storage was imported.
|
|
if (needsToBeRegisteredAsExternalDecl(storage))
|
|
TC.Context.addedExternalDecl(setter);
|
|
}
|
|
|
|
/// Build the result expression of a materializeForSet accessor.
|
|
///
|
|
/// \param address an expression yielding the address to return
|
|
/// \param callbackFn an optional closure expression for the callback
|
|
static Expr *buildMaterializeForSetResult(ASTContext &ctx, Expr *address,
|
|
Expr *callbackFn) {
|
|
if (!callbackFn) {
|
|
callbackFn = new (ctx) NilLiteralExpr(SourceLoc(), IsImplicit);
|
|
}
|
|
|
|
return TupleExpr::create(ctx, SourceLoc(), { address, callbackFn },
|
|
{ Identifier(), Identifier() },
|
|
{ SourceLoc(), SourceLoc() },
|
|
SourceLoc(), false, IsImplicit);
|
|
}
|
|
|
|
/// Create a call to the builtin function with the given name.
|
|
static Expr *buildCallToBuiltin(ASTContext &ctx, StringRef builtinName,
|
|
ArrayRef<Expr*> args) {
|
|
auto builtin = getBuiltinValueDecl(ctx, ctx.getIdentifier(builtinName));
|
|
Expr *builtinDRE = new (ctx) DeclRefExpr(builtin, SourceLoc(), IsImplicit);
|
|
Expr *arg = buildTupleExpr(ctx, args);
|
|
return new (ctx) CallExpr(builtinDRE, arg, IsImplicit);
|
|
}
|
|
|
|
/// Synthesize the body of a materializeForSet accessor for a stored
|
|
/// property.
|
|
static void synthesizeStoredMaterializeForSet(FuncDecl *materializeForSet,
|
|
AbstractStorageDecl *storage,
|
|
VarDecl *bufferDecl,
|
|
TypeChecker &TC) {
|
|
ASTContext &ctx = TC.Context;
|
|
|
|
// return (Builtin.addressof(&self.property), nil)
|
|
Expr *result = buildStorageReference(materializeForSet, storage,
|
|
AccessSemantics::DirectToStorage,
|
|
SelfAccessKind::Peer, TC);
|
|
result = new (ctx) InOutExpr(SourceLoc(), result, Type(), IsImplicit);
|
|
result = buildCallToBuiltin(ctx, "addressof", result);
|
|
result = buildMaterializeForSetResult(ctx, result, /*callback*/ nullptr);
|
|
|
|
ASTNode returnStmt = new (ctx) ReturnStmt(SourceLoc(), result, IsImplicit);
|
|
|
|
SourceLoc loc = storage->getLoc();
|
|
materializeForSet->setBody(BraceStmt::create(ctx, loc, returnStmt, loc,true));
|
|
|
|
maybeMarkTransparent(materializeForSet, storage, TC);
|
|
|
|
TC.typeCheckDecl(materializeForSet, true);
|
|
|
|
// Register the accessor as an external decl if the storage was imported.
|
|
if (needsToBeRegisteredAsExternalDecl(storage))
|
|
TC.Context.addedExternalDecl(materializeForSet);
|
|
}
|
|
|
|
/// 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 a materializeForSet accessor to the given declaration.
|
|
static FuncDecl *addMaterializeForSet(AbstractStorageDecl *storage,
|
|
TypeChecker &TC) {
|
|
auto materializeForSet = createMaterializeForSetPrototype(storage, TC);
|
|
addMemberToContextIfNeeded(materializeForSet, storage->getDeclContext(),
|
|
storage->getSetter());
|
|
storage->setMaterializeForSetFunc(materializeForSet);
|
|
|
|
TC.computeAccessibility(materializeForSet);
|
|
|
|
TC.validateDecl(materializeForSet);
|
|
|
|
return materializeForSet;
|
|
}
|
|
|
|
/// Add trivial accessors to a Stored or Addressed property.
|
|
void swift::addTrivialAccessorsToStorage(AbstractStorageDecl *storage,
|
|
TypeChecker &TC) {
|
|
assert(!storage->hasAccessorFunctions() && "already has accessors?");
|
|
|
|
// Create the getter.
|
|
auto *getter = createGetterPrototype(storage, TC);
|
|
|
|
// Create the setter.
|
|
FuncDecl *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);
|
|
|
|
bool isDynamic = (storage->isDynamic() && storage->isObjC());
|
|
if (isDynamic)
|
|
getter->getAttrs().add(new (TC.Context) DynamicAttr(IsImplicit));
|
|
|
|
// Synthesize and type-check the body of the getter.
|
|
synthesizeTrivialGetter(getter, storage, TC);
|
|
TC.typeCheckDecl(getter, true);
|
|
TC.typeCheckDecl(getter, false);
|
|
|
|
if (setter) {
|
|
if (isDynamic)
|
|
setter->getAttrs().add(new (TC.Context) DynamicAttr(IsImplicit));
|
|
|
|
// Synthesize and type-check the body of the setter.
|
|
synthesizeTrivialSetter(setter, storage, setterValueParam, TC);
|
|
TC.typeCheckDecl(setter, true);
|
|
TC.typeCheckDecl(setter, false);
|
|
}
|
|
|
|
// We've added some members to our containing type, add them to the
|
|
// members list.
|
|
addMemberToContextIfNeeded(getter, storage->getDeclContext());
|
|
if (setter)
|
|
addMemberToContextIfNeeded(setter, storage->getDeclContext());
|
|
|
|
// Always add a materializeForSet when we're creating trivial
|
|
// accessors for a mutable stored property. We only do this when we
|
|
// need to be able to access something polymorphically, and we always
|
|
// want a materializeForSet in such situations.
|
|
if (setter) {
|
|
FuncDecl *materializeForSet = addMaterializeForSet(storage, TC);
|
|
synthesizeMaterializeForSet(materializeForSet, storage, TC);
|
|
TC.typeCheckDecl(materializeForSet, true);
|
|
TC.typeCheckDecl(materializeForSet, false);
|
|
}
|
|
}
|
|
|
|
/// 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 and type-check the body of the setter.
|
|
VarDecl *valueParamDecl = getFirstParamDecl(setter);
|
|
synthesizeTrivialSetter(setter, storage, valueParamDecl, TC);
|
|
TC.typeCheckDecl(setter, true);
|
|
TC.typeCheckDecl(setter, false);
|
|
}
|
|
|
|
/// 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()) {
|
|
addTrivialAccessorsToStorage(storage, *this);
|
|
return;
|
|
}
|
|
|
|
// Otherwise, if the requirement is settable, ensure that there's a
|
|
// materializeForSet function.
|
|
//
|
|
// @objc protocols don't need a materializeForSet since ObjC doesn't have
|
|
// that concept.
|
|
if (!requirement->isObjC() &&
|
|
requirement->getSetter() && !storage->getMaterializeForSetFunc()) {
|
|
FuncDecl *materializeForSet = addMaterializeForSet(storage, *this);
|
|
synthesizeMaterializeForSet(materializeForSet, storage, *this);
|
|
typeCheckDecl(materializeForSet, true);
|
|
typeCheckDecl(materializeForSet, false);
|
|
}
|
|
return;
|
|
}
|
|
|
|
using CallbackGenerator =
|
|
llvm::function_ref<void(SmallVectorImpl<ASTNode> &callbackBody,
|
|
VarDecl *selfDecl, VarDecl *bufferDecl,
|
|
VarDecl *callbackStorageDecl)>;
|
|
|
|
/// Build a materializeForSet callback function.
|
|
/// It should have type
|
|
/// (Builtin.RawPointer, inout Builtin.UnsafeValueBuffer,
|
|
/// inout T, T.Type) -> ()
|
|
static Expr *buildMaterializeForSetCallback(ASTContext &ctx,
|
|
FuncDecl *materializeForSet,
|
|
AbstractStorageDecl *storage,
|
|
const CallbackGenerator &generator) {
|
|
|
|
auto DC = storage->getDeclContext();
|
|
SourceLoc loc = storage->getLoc();
|
|
|
|
Type selfType =
|
|
getSelfTypeForMaterializeForSetCallback(ctx, DC,
|
|
materializeForSet->isStatic());
|
|
|
|
// Build the parameters pattern.
|
|
//
|
|
// Unexpected subtlety: it actually important to call the inout self
|
|
// parameter something other than 'self' so that we don't trigger
|
|
// the "implicit use of self" diagnostic.
|
|
auto bufferParam =
|
|
buildLetArgument(loc, DC, "buffer", ctx.TheRawPointerType);
|
|
auto callbackStorageParam =
|
|
buildInOutArgument(loc, DC, "callbackStorage",ctx.TheUnsafeValueBufferType);
|
|
auto selfValueParam = buildInOutArgument(loc, DC, "selfValue", selfType);
|
|
auto selfTypeParam = buildLetArgument(loc, DC, "selfType",
|
|
MetatypeType::get(selfType));
|
|
|
|
auto paramList = ParameterList::create(ctx, {
|
|
bufferParam,
|
|
callbackStorageParam,
|
|
selfValueParam,
|
|
selfTypeParam
|
|
});
|
|
|
|
// Create the closure expression itself.
|
|
auto closure = new (ctx) ClosureExpr(paramList, SourceLoc(), SourceLoc(),
|
|
SourceLoc(), TypeLoc(),
|
|
/*discriminator*/ 0, materializeForSet);
|
|
|
|
// Generate the body of the closure.
|
|
SmallVector<ASTNode, 4> body;
|
|
generator(body, selfValueParam.decl, bufferParam.decl,
|
|
callbackStorageParam.decl);
|
|
closure->setBody(BraceStmt::create(ctx, SourceLoc(), body, SourceLoc(),
|
|
IsImplicit),
|
|
/*isSingleExpression*/ false);
|
|
closure->setImplicit(IsImplicit);
|
|
|
|
// Call our special builtin to turn that into an opaque thin function.
|
|
auto result = buildCallToBuiltin(ctx, "makeMaterializeForSetCallback",
|
|
{ closure });
|
|
return result;
|
|
}
|
|
|
|
/// Build a builtin operation on a Builtin.UnsafeValueBuffer.
|
|
static Expr *buildValueBufferOperation(ASTContext &ctx, StringRef builtinName,
|
|
VarDecl *bufferDecl, Type valueType) {
|
|
// &buffer
|
|
Expr *bufferRef = new (ctx) DeclRefExpr(bufferDecl, SourceLoc(), IsImplicit);
|
|
bufferRef = new (ctx) InOutExpr(SourceLoc(), bufferRef, Type(), IsImplicit);
|
|
|
|
// T.self
|
|
Expr *metatypeRef = TypeExpr::createImplicit(valueType, ctx);
|
|
metatypeRef = new (ctx) DotSelfExpr(metatypeRef, SourceLoc(), SourceLoc());
|
|
metatypeRef->setImplicit(IsImplicit);
|
|
|
|
// Builtin.whatever(&buffer, T.self)
|
|
return buildCallToBuiltin(ctx, builtinName, { bufferRef, metatypeRef });
|
|
}
|
|
|
|
/// Build a call to Builtin.take.
|
|
static Expr *buildBuiltinTake(ASTContext &ctx, Expr *address,
|
|
Type valueType) {
|
|
// Builtin.take(address) as ValueType
|
|
Expr *result = buildCallToBuiltin(ctx, "take", { address });
|
|
result = new (ctx) CoerceExpr(result, SourceLoc(),
|
|
TypeLoc::withoutLoc(valueType));
|
|
result->setImplicit(IsImplicit);
|
|
return result;
|
|
}
|
|
|
|
/// Build an expression to initialize callback storage.
|
|
static ASTNode buildInitializeCallbackStorage(FuncDecl *materializeForSet,
|
|
Expr *initializer,
|
|
Type initializerType,
|
|
ASTContext &ctx) {
|
|
// let allocatedCallbackStorage =
|
|
// Builtin.allocateValueBuffer(&callbackStorage, IndexType.self))
|
|
VarDecl *callbackStorageDecl = getParamDeclAtIndex(materializeForSet, 1);
|
|
Expr *allocatedCallbackStorage =
|
|
buildValueBufferOperation(ctx, "allocValueBuffer", callbackStorageDecl,
|
|
initializerType);
|
|
|
|
// Builtin.initialize(indexArgs, allocatedCallbackStorage)
|
|
return buildCallToBuiltin(ctx, "initialize",
|
|
{ initializer, allocatedCallbackStorage });
|
|
}
|
|
|
|
/// Build an expression to take from callback storage.
|
|
static Expr *buildTakeFromCallbackStorage(VarDecl *storage, Type valueType,
|
|
ASTContext &ctx) {
|
|
Expr *address =
|
|
buildValueBufferOperation(ctx, "projectValueBuffer", storage, valueType);
|
|
return buildBuiltinTake(ctx, address, valueType);
|
|
}
|
|
|
|
namespace {
|
|
/// A reference to storage from the context of a materializeForSet
|
|
/// callback.
|
|
class CallbackStorageReferenceContext : public StorageReferenceContext {
|
|
VarDecl *Self;
|
|
VarDecl *CallbackStorage;
|
|
public:
|
|
CallbackStorageReferenceContext(VarDecl *self, VarDecl *callbackStorage)
|
|
: Self(self), CallbackStorage(callbackStorage) {}
|
|
virtual ~CallbackStorageReferenceContext() = default;
|
|
|
|
VarDecl *getSelfDecl() const override {
|
|
return Self;
|
|
}
|
|
Expr *getIndexRefExpr(ASTContext &ctx,
|
|
SubscriptDecl *subscript) const override {
|
|
return buildTakeFromCallbackStorage(CallbackStorage,
|
|
subscript->getIndicesType(), ctx);
|
|
}
|
|
};
|
|
}
|
|
|
|
/// Synthesize the body of a materializeForSet accessor for a
|
|
/// computed property.
|
|
static void synthesizeComputedMaterializeForSet(FuncDecl *materializeForSet,
|
|
AbstractStorageDecl *storage,
|
|
VarDecl *bufferDecl,
|
|
TypeChecker &TC) {
|
|
ASTContext &ctx = TC.Context;
|
|
|
|
SmallVector<ASTNode, 4> body;
|
|
|
|
AccessSemantics semantics;
|
|
// If the storage is dynamic, we must dynamically redispatch through the
|
|
// accessor. Otherwise, we can do a direct peer access.
|
|
if (needsDynamicMaterializeForSet(storage))
|
|
semantics = AccessSemantics::Ordinary;
|
|
else
|
|
semantics = AccessSemantics::DirectToAccessor;
|
|
|
|
// Builtin.initialize(self.property, buffer)
|
|
Expr *curValue = buildStorageReference(materializeForSet, storage,
|
|
semantics,
|
|
SelfAccessKind::Peer, TC);
|
|
Expr *bufferRef = new (ctx) DeclRefExpr(bufferDecl, SourceLoc(), IsImplicit);
|
|
body.push_back(buildCallToBuiltin(ctx, "initialize",
|
|
{ curValue, bufferRef }));
|
|
|
|
// If this is a subscript, preserve the index value:
|
|
if (auto subscript = dyn_cast<SubscriptDecl>(storage)) {
|
|
Expr *indices = buildSubscriptIndexReference(ctx, materializeForSet);
|
|
ASTNode initialize =
|
|
buildInitializeCallbackStorage(materializeForSet, indices,
|
|
subscript->getIndicesType(), ctx);
|
|
body.push_back(initialize);
|
|
}
|
|
|
|
// Build the callback.
|
|
Expr *callback =
|
|
buildMaterializeForSetCallback(ctx, materializeForSet, storage,
|
|
[&](SmallVectorImpl<ASTNode> &body,
|
|
VarDecl *selfDecl, VarDecl *bufferDecl,
|
|
VarDecl *callbackStorageDecl) {
|
|
// self.property = Builtin.take(buffer)
|
|
Expr *bufferRef =
|
|
new (ctx) DeclRefExpr(bufferDecl, SourceLoc(), IsImplicit);
|
|
Expr *value = buildBuiltinTake(ctx, bufferRef,
|
|
getTypeOfStorage(storage, TC));
|
|
|
|
Expr *storageRef =
|
|
buildStorageReference(
|
|
CallbackStorageReferenceContext{selfDecl, callbackStorageDecl},
|
|
storage, semantics,
|
|
SelfAccessKind::Peer, TC);
|
|
body.push_back(new (ctx) AssignExpr(storageRef, SourceLoc(),
|
|
value, IsImplicit));
|
|
|
|
// If this is a subscript, deallocate the subscript buffer:
|
|
if (auto subscript = dyn_cast<SubscriptDecl>(storage)) {
|
|
// Builtin.deallocValueBuffer(&callbackStorage, IndexType.self)
|
|
body.push_back(buildValueBufferOperation(ctx, "deallocValueBuffer",
|
|
callbackStorageDecl,
|
|
subscript->getIndicesType()));
|
|
}
|
|
});
|
|
|
|
// return (buffer, callback)
|
|
Expr *result = new (ctx) DeclRefExpr(bufferDecl, SourceLoc(), IsImplicit);
|
|
|
|
result = buildMaterializeForSetResult(ctx, result, callback);
|
|
body.push_back(new (ctx) ReturnStmt(SourceLoc(), result, IsImplicit));
|
|
|
|
SourceLoc loc = storage->getLoc();
|
|
materializeForSet->setBody(BraceStmt::create(ctx, loc, body, loc, true));
|
|
|
|
// Mark it transparent, there is no user benefit to this actually existing.
|
|
materializeForSet->getAttrs().add(new (ctx) TransparentAttr(IsImplicit));
|
|
|
|
TC.typeCheckDecl(materializeForSet, true);
|
|
|
|
// Register the accessor as an external decl if the storage was imported.
|
|
if (needsToBeRegisteredAsExternalDecl(storage))
|
|
TC.Context.addedExternalDecl(materializeForSet);
|
|
}
|
|
|
|
/// Build a direct call to an addressor from within a
|
|
/// materializeForSet method.
|
|
static Expr *buildCallToAddressor(FuncDecl *materializeForSet,
|
|
AbstractStorageDecl *storage,
|
|
VarDecl *bufferDecl,
|
|
FuncDecl *addressor,
|
|
ASTContext &ctx) {
|
|
// Build a direct reference to the addressor.
|
|
Expr *fn;
|
|
|
|
// Apply the self argument if applicable.
|
|
if (auto self = materializeForSet->getImplicitSelfDecl()) {
|
|
Expr *selfRef = new (ctx) DeclRefExpr(self, SourceLoc(), IsImplicit);
|
|
// if (addressor->computeSelfType(nullptr)->is<LValueType>()) {
|
|
// selfRef = new (ctx) InOutExpr(SourceLoc(), selfRef, Type(), IsImplicit);
|
|
// }
|
|
|
|
ValueDecl *localMembers[] = { addressor };
|
|
fn = new (ctx) OverloadedMemberRefExpr(selfRef, SourceLoc(),
|
|
ctx.AllocateCopy(localMembers),
|
|
SourceLoc(), IsImplicit, Type(),
|
|
AccessSemantics::DirectToStorage);
|
|
} else {
|
|
fn = new (ctx) DeclRefExpr(addressor, SourceLoc(), IsImplicit,
|
|
AccessSemantics::DirectToStorage);
|
|
}
|
|
|
|
// Apply the rest of the addressor arguments.
|
|
Expr *args;
|
|
if (isa<SubscriptDecl>(storage)) {
|
|
args = buildSubscriptIndexReference(ctx, materializeForSet);
|
|
} else {
|
|
args = TupleExpr::createImplicit(ctx, {}, {});
|
|
}
|
|
|
|
return new (ctx) CallExpr(fn, args, IsImplicit);
|
|
}
|
|
|
|
/// Given an expression of type UnsafeMutablePointer<T>, create an
|
|
/// expression of type Builtin.RawPointer.
|
|
static Expr *buildUnsafeMutablePointerToRawPointer(Expr *operand,
|
|
ASTContext &ctx) {
|
|
// Just directly drill in.
|
|
NominalTypeDecl *ptrType = ctx.getUnsafeMutablePointerDecl();
|
|
|
|
// If that doesn't work, just bail out; the result probably won't
|
|
// type-check, but there are worse failure modes for a broken stdlib.
|
|
if (!ptrType) return operand;
|
|
auto props = ptrType->getStoredProperties();
|
|
if (props.begin() == props.end()) return operand;
|
|
|
|
auto storageProp = props.front();
|
|
return new (ctx) MemberRefExpr(operand, SourceLoc(), storageProp,
|
|
SourceRange(), IsImplicit);
|
|
}
|
|
|
|
/// Synthesize the body of a materializeForSet accessor for an
|
|
/// addressed property.
|
|
static void synthesizeAddressedMaterializeForSet(FuncDecl *materializeForSet,
|
|
AbstractStorageDecl *storage,
|
|
VarDecl *bufferDecl,
|
|
TypeChecker &TC) {
|
|
ASTContext &ctx = TC.Context;
|
|
|
|
SmallVector<ASTNode, 4> body;
|
|
|
|
// Call the mutable addressor.
|
|
auto addressor = storage->getMutableAddressor();
|
|
Expr *addressorResult = buildCallToAddressor(materializeForSet, storage,
|
|
bufferDecl, addressor, ctx);
|
|
|
|
Expr *result;
|
|
Expr *callback;
|
|
switch (addressor->getAddressorKind()) {
|
|
case AddressorKind::NotAddressor:
|
|
llvm_unreachable("addressor is not an addressor?");
|
|
|
|
// If we have an unsafe addressor, this is easy: we just pull out
|
|
// the raw pointer and use that as the result.
|
|
case AddressorKind::Unsafe:
|
|
result = buildUnsafeMutablePointerToRawPointer(addressorResult, ctx);
|
|
callback = nullptr;
|
|
break;
|
|
|
|
case AddressorKind::Owning:
|
|
case AddressorKind::NativeOwning:
|
|
case AddressorKind::NativePinning: {
|
|
// We need to bind the result to a temporary variable.
|
|
// let temporary = addressor(self)(indices)
|
|
auto tempDecl = new (ctx) VarDecl(/*static*/ false, /*let*/ true,
|
|
SourceLoc(), ctx.getIdentifier("tmp"),
|
|
Type(), materializeForSet);
|
|
tempDecl->setImplicit(IsImplicit);
|
|
auto bindingPattern = new (ctx) NamedPattern(tempDecl, IsImplicit);
|
|
auto bindingDecl = PatternBindingDecl::create(ctx, /*static*/ SourceLoc(),
|
|
StaticSpellingKind::None,
|
|
SourceLoc(), bindingPattern,
|
|
addressorResult,
|
|
materializeForSet);
|
|
bindingDecl->setImplicit(IsImplicit);
|
|
body.push_back(bindingDecl);
|
|
body.push_back(tempDecl);
|
|
|
|
// This should be Builtin.NativePointer or something like it.
|
|
Type ownerType = [&]() -> Type {
|
|
switch (addressor->getAddressorKind()){
|
|
case AddressorKind::NotAddressor:
|
|
case AddressorKind::Unsafe:
|
|
llvm_unreachable("filtered out");
|
|
case AddressorKind::Owning:
|
|
return ctx.TheUnknownObjectType;
|
|
case AddressorKind::NativeOwning:
|
|
return ctx.TheNativeObjectType;
|
|
case AddressorKind::NativePinning:
|
|
return OptionalType::get(ctx.TheNativeObjectType);
|
|
}
|
|
llvm_unreachable("bad addressor kind");
|
|
}();
|
|
|
|
// Initialize the callback storage with the owner value, which is
|
|
// the second element of the addressor result.
|
|
Expr *owner = new (ctx) DeclRefExpr(tempDecl, SourceLoc(), IsImplicit);
|
|
owner = new (ctx) TupleElementExpr(owner, SourceLoc(), /*field index*/ 1,
|
|
SourceLoc(), Type());
|
|
owner->setImplicit(IsImplicit);
|
|
body.push_back(buildInitializeCallbackStorage(materializeForSet, owner,
|
|
ownerType, ctx));
|
|
|
|
// The result is the first element of the addressor result.
|
|
result = new (ctx) DeclRefExpr(tempDecl, SourceLoc(), IsImplicit);
|
|
result = new (ctx) TupleElementExpr(result, SourceLoc(), /*field index*/ 0,
|
|
SourceLoc(), Type());
|
|
result->setImplicit(IsImplicit);
|
|
result = buildUnsafeMutablePointerToRawPointer(result, ctx);
|
|
|
|
// Build the callback.
|
|
callback = buildMaterializeForSetCallback(ctx, materializeForSet, storage,
|
|
[&](SmallVectorImpl<ASTNode> &body, VarDecl *selfDecl,
|
|
VarDecl *bufferDecl, VarDecl *callbackStorageDecl) {
|
|
// Pull the owner out of callback storage.
|
|
Expr *owner =
|
|
buildTakeFromCallbackStorage(callbackStorageDecl, ownerType, ctx);
|
|
|
|
// For an owning addressor, we can just drop the value we pulled out.
|
|
if (addressor->getAddressorKind() != AddressorKind::NativePinning) {
|
|
body.push_back(owner);
|
|
|
|
// For a pinning addressor, we have to unpin it.
|
|
} else {
|
|
Expr *unpin = buildCallToBuiltin(ctx, "unpin", { owner });
|
|
body.push_back(unpin);
|
|
}
|
|
|
|
// This should always be a no-op, but do it for the sake of formalism.
|
|
// Builtin.deallocValueBuffer(&callbackStorage, OwnerType.self)
|
|
body.push_back(buildValueBufferOperation(ctx, "deallocValueBuffer",
|
|
callbackStorageDecl,
|
|
ownerType));
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
|
|
// return (buffer, callback)
|
|
result = buildMaterializeForSetResult(ctx, result, callback);
|
|
body.push_back(new (ctx) ReturnStmt(SourceLoc(), result, IsImplicit));
|
|
|
|
SourceLoc loc = storage->getLoc();
|
|
materializeForSet->setBody(BraceStmt::create(ctx, loc, body, loc));
|
|
|
|
// Mark it transparent, there is no user benefit to this actually existing.
|
|
materializeForSet->getAttrs().add(new (ctx) TransparentAttr(IsImplicit));
|
|
|
|
TC.typeCheckDecl(materializeForSet, true);
|
|
|
|
// Register the accessor as an external decl if the storage was imported.
|
|
if (needsToBeRegisteredAsExternalDecl(storage))
|
|
TC.Context.addedExternalDecl(materializeForSet);
|
|
}
|
|
|
|
void swift::synthesizeMaterializeForSet(FuncDecl *materializeForSet,
|
|
AbstractStorageDecl *storage,
|
|
TypeChecker &TC) {
|
|
VarDecl *bufferDecl = getFirstParamDecl(materializeForSet);
|
|
|
|
switch (storage->getStorageKind()) {
|
|
case AbstractStorageDecl::Stored:
|
|
case AbstractStorageDecl::Addressed:
|
|
llvm_unreachable("no accessors");
|
|
|
|
// We can use direct access to stored variables, but not if they're
|
|
// weak, unowned, or dynamic.
|
|
case AbstractStorageDecl::StoredWithTrivialAccessors: {
|
|
// Only variables can be Stored, and only variables can be weak/unowned.
|
|
auto var = cast<VarDecl>(storage);
|
|
if (var->getType()->is<ReferenceStorageType>()
|
|
|| needsDynamicMaterializeForSet(var)) {
|
|
synthesizeComputedMaterializeForSet(materializeForSet, storage,
|
|
bufferDecl, TC);
|
|
return;
|
|
}
|
|
|
|
synthesizeStoredMaterializeForSet(materializeForSet, storage,
|
|
bufferDecl, TC);
|
|
return;
|
|
}
|
|
|
|
// We should access these by calling mutableAddress.
|
|
case AbstractStorageDecl::AddressedWithTrivialAccessors:
|
|
case AbstractStorageDecl::ComputedWithMutableAddress:
|
|
synthesizeAddressedMaterializeForSet(materializeForSet, storage,
|
|
bufferDecl, TC);
|
|
return;
|
|
|
|
// These must be accessed with a getter/setter pair.
|
|
// TODO: StoredWithObservers and AddressedWithObservers could be
|
|
// made to work with the callback as long as there isn't a willSet.
|
|
case AbstractStorageDecl::StoredWithObservers:
|
|
case AbstractStorageDecl::InheritedWithObservers:
|
|
case AbstractStorageDecl::AddressedWithObservers:
|
|
case AbstractStorageDecl::Computed:
|
|
synthesizeComputedMaterializeForSet(materializeForSet, storage,
|
|
bufferDecl, TC);
|
|
return;
|
|
}
|
|
llvm_unreachable("bad abstract storage kind");
|
|
}
|
|
|
|
/// 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);
|
|
|
|
// 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).decl;
|
|
|
|
// 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, /*isLet*/ true,
|
|
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, SourceLoc(), /*imp*/true);
|
|
auto *ValueDRE = new (Ctx) DeclRefExpr(ValueDecl, SourceLoc(), /*imp*/true);
|
|
if (SelfDecl) {
|
|
auto *SelfDRE = new (Ctx) DeclRefExpr(SelfDecl, SourceLoc(), /*imp*/true);
|
|
Callee = new (Ctx) DotSyntaxCallExpr(Callee, SourceLoc(), SelfDRE);
|
|
}
|
|
SetterBody.push_back(new (Ctx) CallExpr(Callee, ValueDRE, true));
|
|
|
|
// Make sure the didSet/willSet accessors are marked final if in a class.
|
|
if (!willSet->isFinal() &&
|
|
VD->getDeclContext()->isClassOrClassExtensionContext())
|
|
makeFinal(Ctx, willSet);
|
|
}
|
|
|
|
// Create an assignment into the storage or call to superclass setter.
|
|
auto *ValueDRE = new (Ctx) DeclRefExpr(ValueDecl, SourceLoc(), 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, SourceLoc(),
|
|
/*impl*/true);
|
|
Expr *Callee = new (Ctx) DeclRefExpr(didSet, SourceLoc(), /*imp*/true);
|
|
if (SelfDecl) {
|
|
auto *SelfDRE = new (Ctx) DeclRefExpr(SelfDecl, SourceLoc(), /*imp*/true);
|
|
Callee = new (Ctx) DotSyntaxCallExpr(Callee, SourceLoc(), SelfDRE);
|
|
}
|
|
SetterBody.push_back(new (Ctx) CallExpr(Callee, OldValueExpr, true));
|
|
|
|
// Make sure the didSet/willSet accessors are marked final if in a class.
|
|
if (!didSet->isFinal() &&
|
|
VD->getDeclContext()->isClassOrClassExtensionContext())
|
|
makeFinal(Ctx, didSet);
|
|
}
|
|
|
|
Set->setBody(BraceStmt::create(Ctx, Loc, SetterBody, Loc, true));
|
|
|
|
// Type check the body of the getter and setter.
|
|
TC.typeCheckDecl(Get, true);
|
|
TC.typeCheckDecl(Get, false);
|
|
TC.typeCheckDecl(Set, true);
|
|
TC.typeCheckDecl(Set, false);
|
|
}
|
|
|
|
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(VD->getLoc(), Get, Set, nullptr, VD->getLoc());
|
|
|
|
TC.validateDecl(Get);
|
|
TC.validateDecl(Set);
|
|
|
|
// We've added some members to our containing class/extension, add them to
|
|
// the members list.
|
|
addMemberToContextIfNeeded(Get, VD->getDeclContext());
|
|
addMemberToContextIfNeeded(Set, VD->getDeclContext());
|
|
}
|
|
|
|
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}; }
|
|
};
|
|
}
|
|
|
|
/// 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();
|
|
TC.validateDecl(Get);
|
|
|
|
SmallVector<ASTNode, 6> Body;
|
|
|
|
// Load the existing storage and store it into the 'tmp1' temporary.
|
|
auto *Tmp1VD = new (Ctx) VarDecl(/*isStatic*/false, /*isLet*/true,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, SourceLoc(), /*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, SourceLoc(), /*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, /*isLet*/true,
|
|
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, SourceLoc(), /*Implicit*/true,
|
|
AccessSemantics::DirectToStorage);
|
|
createPropertyStoreOrCallSuperclassSetter(Get, Tmp2DRE, Storage, Body, TC);
|
|
|
|
// Return tmp2.
|
|
Tmp2DRE = new (Ctx) DeclRefExpr(Tmp2VD, SourceLoc(), /*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::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 *Storage = new (Context) VarDecl(/*isStatic*/false, /*isLet*/false,
|
|
VD->getLoc(), StorageName, StorageTy,
|
|
VD->getDeclContext());
|
|
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());
|
|
|
|
|
|
// Now that we've got the storage squared away, synthesize the getter.
|
|
auto *Get = completeLazyPropertyGetter(VD, Storage, *this);
|
|
|
|
// The setter just forwards on to storage without materializing the initial
|
|
// value.
|
|
auto *Set = VD->getSetter();
|
|
validateDecl(Set);
|
|
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()->isClassOrClassExtensionContext())
|
|
makeFinal(Context, Storage);
|
|
Storage->setImplicit();
|
|
Storage->setAccessibility(Accessibility::Private);
|
|
Storage->setSetterAccessibility(Accessibility::Private);
|
|
|
|
typeCheckDecl(Get, true);
|
|
typeCheckDecl(Get, false);
|
|
|
|
typeCheckDecl(Set, true);
|
|
typeCheckDecl(Set, false);
|
|
}
|
|
|
|
|
|
/// 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;
|
|
|
|
// Don't bother if the declaration is invalid.
|
|
if (storage->isInvalid()) return;
|
|
|
|
// We only need materializeForSet in polymorphic contexts:
|
|
auto containerTy =
|
|
storage->getDeclContext()->getDeclaredTypeOfContext();
|
|
if (!containerTy) return;
|
|
|
|
NominalTypeDecl *container = containerTy->getAnyNominal();
|
|
assert(container && "extension of non-nominal type?");
|
|
|
|
// - in non-ObjC protocols, but not protocol extensions.
|
|
if (auto protocol = dyn_cast<ProtocolDecl>(container)) {
|
|
if (protocol->isObjC()) return;
|
|
if (storage->getDeclContext()->isProtocolExtensionContext()) return;
|
|
|
|
// - in classes when the storage decl is not final and does
|
|
// not override a decl that requires a materializeForSet
|
|
} else if (isa<ClassDecl>(container)) {
|
|
if (storage->isFinal()) {
|
|
auto overridden = storage->getOverriddenDecl();
|
|
if (!overridden || !overridden->getMaterializeForSetFunc())
|
|
return;
|
|
}
|
|
|
|
// Structs and enums don't need this.
|
|
} else {
|
|
assert(isa<StructDecl>(container) || isa<EnumDecl>(container));
|
|
return;
|
|
}
|
|
|
|
addMaterializeForSet(storage, TC);
|
|
}
|
|
|
|
void swift::maybeAddAccessorsToVariable(VarDecl *var, TypeChecker &TC) {
|
|
if (var->getGetter() || var->isBeingTypeChecked() || isa<ParamDecl>(var))
|
|
return;
|
|
|
|
// Lazy properties get accessors.
|
|
if (var->getAttrs().hasAttribute<LazyAttr>()) {
|
|
var->setIsBeingTypeChecked();
|
|
|
|
auto *getter = createGetterPrototype(var, TC);
|
|
// lazy getters are mutating on an enclosing value type.
|
|
if (!var->getDeclContext()->isClassOrClassExtensionContext())
|
|
getter->setMutating();
|
|
getter->setAccessibility(var->getFormalAccess());
|
|
|
|
ParamDecl *newValueParam = nullptr;
|
|
auto *setter = createSetterPrototype(var, newValueParam, TC);
|
|
var->makeComputed(var->getLoc(), getter, setter, nullptr,
|
|
var->getLoc());
|
|
var->setIsBeingTypeChecked(false);
|
|
|
|
TC.validateDecl(getter);
|
|
TC.validateDecl(setter);
|
|
|
|
addMemberToContextIfNeeded(getter, var->getDeclContext());
|
|
addMemberToContextIfNeeded(setter, var->getDeclContext());
|
|
return;
|
|
|
|
}
|
|
// Stored properties in SIL mode don't get auto-synthesized accessors.
|
|
bool isInSILMode = false;
|
|
if (auto sourceFile = var->getDeclContext()->getParentSourceFile())
|
|
isInSILMode = sourceFile->Kind == SourceFileKind::SIL;
|
|
|
|
auto nominal = var->getDeclContext()->isNominalTypeOrNominalTypeExtensionContext();
|
|
if (var->hasAccessorFunctions() ||
|
|
var->isImplicit() ||
|
|
nominal == nullptr)
|
|
return;
|
|
|
|
// Non-NSManaged class instance variables get accessors, because it affects
|
|
// vtable layout.
|
|
if (isa<ClassDecl>(nominal)) {
|
|
if (var->getAttrs().hasAttribute<NSManagedAttr>()) {
|
|
var->setIsBeingTypeChecked();
|
|
convertNSManagedStoredVarToComputed(var, TC);
|
|
var->setIsBeingTypeChecked(false);
|
|
} else if (!isInSILMode) {
|
|
var->setIsBeingTypeChecked();
|
|
addTrivialAccessorsToStorage(var, TC);
|
|
var->setIsBeingTypeChecked(false);
|
|
}
|
|
}
|
|
|
|
// Public instance variables of resilient structs get accessors.
|
|
if (auto structDecl = dyn_cast<StructDecl>(nominal)) {
|
|
if (!structDecl->hasFixedLayout() && !isInSILMode) {
|
|
var->setIsBeingTypeChecked();
|
|
addTrivialAccessorsToStorage(var, TC);
|
|
var->setIsBeingTypeChecked(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// \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) {
|
|
ASTContext &context = tc.Context;
|
|
SourceLoc Loc = decl->getLoc();
|
|
Accessibility accessLevel = decl->getFormalAccess();
|
|
if (!decl->hasClangNode())
|
|
accessLevel = std::min(accessLevel, Accessibility::Internal);
|
|
|
|
// Determine the parameter type of the implicit constructor.
|
|
SmallVector<Parameter, 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 = tc.getTypeOfRValue(var);
|
|
|
|
// 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);
|
|
|
|
// Create the parameter.
|
|
auto *arg = new (context) ParamDecl(/*IsLet*/true, Loc, var->getName(),
|
|
Loc, var->getName(), varType, decl);
|
|
arg->setImplicit();
|
|
params.push_back(Parameter::withoutLoc(arg));
|
|
}
|
|
}
|
|
|
|
auto paramList = ParameterList::create(context, params);
|
|
|
|
// Create the constructor.
|
|
DeclName name(context, context.Id_init, paramList);
|
|
auto *selfParam = ParamDecl::createSelf(Loc, decl,
|
|
/*static*/false, /*inout*/true);
|
|
auto *ctor = new (context) ConstructorDecl(name, Loc, OTK_None, SourceLoc(),
|
|
selfParam, paramList,
|
|
nullptr, SourceLoc(), decl);
|
|
|
|
// Mark implicit.
|
|
ctor->setImplicit();
|
|
ctor->setAccessibility(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(/*implicit=*/true));
|
|
}
|
|
|
|
// Type-check the constructor declaration.
|
|
tc.typeCheckDecl(ctor, /*isFirstPass=*/true);
|
|
|
|
// If the struct in which this constructor is being added was imported,
|
|
// add it as an external definition.
|
|
if (decl->hasClangNode()) {
|
|
tc.Context.addedExternalDecl(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->getExtensionType()->getClassOrBoundGenericClass();
|
|
if (!unimplementedInitDecl) {
|
|
tc.diagnose(classDecl->getLoc(), diag::missing_unimplemented_init_runtime);
|
|
return;
|
|
}
|
|
|
|
// Create a call to Swift._unimplemented_initializer
|
|
auto loc = classDecl->getLoc();
|
|
Expr *fn = new (tc.Context) DeclRefExpr(unimplementedInitDecl, 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);
|
|
className = new (tc.Context) ParenExpr(loc, className, loc, false);
|
|
Expr *call = new (tc.Context) CallExpr(fn, className, /*Implicit=*/true);
|
|
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) {
|
|
// Determine the initializer parameters.
|
|
Type superInitType = superclassCtor->getInitializerInterfaceType();
|
|
if (superInitType->is<GenericFunctionType>() ||
|
|
classDecl->getGenericParamsOfContext()) {
|
|
// FIXME: Handle generic initializers as well.
|
|
return nullptr;
|
|
}
|
|
|
|
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);
|
|
|
|
// Create the initializer declaration.
|
|
auto ctor = new (ctx) ConstructorDecl(superclassCtor->getFullName(),
|
|
classDecl->getBraces().Start,
|
|
superclassCtor->getFailability(),
|
|
SourceLoc(), selfDecl, bodyParams,
|
|
nullptr, SourceLoc(), classDecl);
|
|
ctor->setImplicit();
|
|
ctor->setAccessibility(std::min(classDecl->getFormalAccess(),
|
|
superclassCtor->getFormalAccess()));
|
|
|
|
// Make sure the constructor is only as available as its superclass's
|
|
// constructor.
|
|
AvailabilityInference::applyInferredAvailableAttrs(ctor, superclassCtor,
|
|
ctx);
|
|
|
|
// Configure 'self'.
|
|
auto selfType = configureImplicitSelf(tc, ctor);
|
|
|
|
// Set the type of the initializer.
|
|
configureConstructorType(ctor, selfType, bodyParams->getType(ctx),
|
|
superclassCtor->isBodyThrowing());
|
|
if (superclassCtor->isObjC()) {
|
|
auto errorConvention = superclassCtor->getForeignErrorConvention();
|
|
markAsObjC(tc, ctor, ObjCReason::ImplicitlyObjC, errorConvention);
|
|
|
|
// 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);
|
|
// Set it to implicit to disable printing it for SIL.
|
|
clonedAttr->setImplicit(true);
|
|
ctor->getAttrs().add(clonedAttr);
|
|
}
|
|
}
|
|
}
|
|
if (superclassCtor->isRequired())
|
|
ctor->getAttrs().add(new (tc.Context) RequiredAttr(/*implicit=*/true));
|
|
|
|
// Wire up the overrides.
|
|
ctor->getAttrs().add(new (tc.Context) OverrideAttr(/*Implicit=*/true));
|
|
checkOverrides(tc, ctor);
|
|
|
|
if (kind == DesignatedInitKind::Stub) {
|
|
// Make this a stub implementation.
|
|
createStubBody(tc, ctor);
|
|
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) UnresolvedConstructorExpr(superRef,
|
|
SourceLoc(),
|
|
SourceLoc(),
|
|
/*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 = new (ctx) CallExpr(ctorRef, ctorArgs, /*Implicit=*/true);
|
|
if (superclassCtor->isBodyThrowing()) {
|
|
superCall = new (ctx) TryExpr(SourceLoc(), superCall, Type(),
|
|
/*Implicit=*/true);
|
|
}
|
|
ctor->setBody(BraceStmt::create(tc.Context, SourceLoc(),
|
|
ASTNode(superCall),
|
|
SourceLoc(),
|
|
/*Implicit=*/true));
|
|
|
|
return ctor;
|
|
}
|
|
|
|
void TypeChecker::addImplicitDestructor(ClassDecl *CD) {
|
|
if (CD->hasDestructor() || CD->isInvalid())
|
|
return;
|
|
|
|
auto *selfDecl = ParamDecl::createSelf(CD->getLoc(), CD);
|
|
|
|
auto *DD = new (Context) DestructorDecl(Context.Id_deinit, CD->getLoc(),
|
|
selfDecl, CD);
|
|
|
|
DD->setImplicit();
|
|
|
|
// Type-check the destructor declaration.
|
|
typeCheckDecl(DD, /*isFirstPass=*/true);
|
|
|
|
// Create an empty body for the destructor.
|
|
DD->setBody(BraceStmt::create(Context, CD->getLoc(), { }, CD->getLoc(),true));
|
|
CD->addMember(DD);
|
|
CD->setHasDestructor();
|
|
}
|