blob: fb2e14352c9dd2cb77eef0a966065370c9e89778 [file] [log] [blame]
//===--- TypeCheckStorage.cpp - Checking Properties and Subscripts -------===//
//
// 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 properties, subscripts as well
// as their accessors.
//
//===----------------------------------------------------------------------===//
#include "CodeSynthesis.h"
#include "TypeChecker.h"
#include "TypeCheckAvailability.h"
#include "TypeCheckConcurrency.h"
#include "TypeCheckDecl.h"
#include "TypeCheckType.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/DiagnosticsParse.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/AST/Expr.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/Pattern.h"
#include "swift/AST/PropertyWrappers.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/AST/Types.h"
using namespace swift;
/// Set each bound variable in the pattern to have an error type.
void swift::setBoundVarsTypeError(Pattern *pattern, ASTContext &ctx) {
pattern->forEachVariable([&](VarDecl *var) {
// Don't change the type of a variable that we've been able to
// compute a type for.
if (var->hasInterfaceType())
return;
var->setInvalid();
});
}
/// Build a default initializer for the given type.
Expr *TypeChecker::buildDefaultInitializer(Type type) {
auto &Context = type->getASTContext();
// Default-initialize optional types and weak values to 'nil'.
if (type->getReferenceStorageReferent()->getOptionalObjectType())
return new (Context) NilLiteralExpr(SourceLoc(), /*Implicit=*/true);
// Build tuple literals for tuple types.
if (auto tupleType = type->getAs<TupleType>()) {
SmallVector<Expr *, 2> inits;
for (const auto &elt : tupleType->getElements()) {
if (elt.isVararg())
return nullptr;
auto eltInit = TypeChecker::buildDefaultInitializer(elt.getType());
if (!eltInit)
return nullptr;
inits.push_back(eltInit);
}
return TupleExpr::createImplicit(Context, inits, { });
}
// We don't default-initialize anything else.
return nullptr;
}
/// Does the context allow pattern bindings that don't bind any variables?
static bool contextAllowsPatternBindingWithoutVariables(DeclContext *dc) {
// Property decls in type context must bind variables.
if (dc->isTypeContext())
return false;
// Global variable decls must bind variables, except in scripts.
if (dc->isModuleScopeContext()) {
if (dc->getParentSourceFile()
&& dc->getParentSourceFile()->isScriptMode())
return true;
return false;
}
return true;
}
static bool hasStoredProperties(NominalTypeDecl *decl) {
return (isa<StructDecl>(decl) ||
(isa<ClassDecl>(decl) && !decl->hasClangNode()));
}
static void computeLoweredStoredProperties(NominalTypeDecl *decl) {
// Just walk over the members of the type, forcing backing storage
// for lazy properties and property wrappers to be synthesized.
for (auto *member : decl->getMembers()) {
auto *var = dyn_cast<VarDecl>(member);
if (!var || var->isStatic())
continue;
if (var->getAttrs().hasAttribute<LazyAttr>())
(void) var->getLazyStorageProperty();
if (var->hasAttachedPropertyWrapper())
(void) var->getPropertyWrapperBackingProperty();
}
// If this is an actor class, check conformance to the Actor protocol to
// ensure that the actor storage will get created (if needed).
if (auto classDecl = dyn_cast<ClassDecl>(decl)) {
if (classDecl->isActor()) {
ASTContext &ctx = decl->getASTContext();
if (auto actorProto = ctx.getProtocol(KnownProtocolKind::Actor)) {
SmallVector<ProtocolConformance *, 1> conformances;
classDecl->lookupConformance(
decl->getModuleContext(), actorProto, conformances);
for (auto conformance : conformances)
TypeChecker::checkConformance(conformance->getRootNormalConformance());
}
}
}
}
ArrayRef<VarDecl *>
StoredPropertiesRequest::evaluate(Evaluator &evaluator,
NominalTypeDecl *decl) const {
if (!hasStoredProperties(decl))
return ArrayRef<VarDecl *>();
SmallVector<VarDecl *, 4> results;
// Unless we're in a source file we don't have to do anything
// special to lower lazy properties and property wrappers.
if (isa<SourceFile>(decl->getModuleScopeContext()))
computeLoweredStoredProperties(decl);
for (auto *member : decl->getMembers()) {
if (auto *var = dyn_cast<VarDecl>(member))
if (!var->isStatic() && var->hasStorage()) {
results.push_back(var);
}
}
return decl->getASTContext().AllocateCopy(results);
}
ArrayRef<Decl *>
StoredPropertiesAndMissingMembersRequest::evaluate(Evaluator &evaluator,
NominalTypeDecl *decl) const {
if (!hasStoredProperties(decl))
return ArrayRef<Decl *>();
SmallVector<Decl *, 4> results;
// Unless we're in a source file we don't have to do anything
// special to lower lazy properties and property wrappers.
if (isa<SourceFile>(decl->getModuleScopeContext()))
computeLoweredStoredProperties(decl);
for (auto *member : decl->getMembers()) {
if (auto *var = dyn_cast<VarDecl>(member))
if (!var->isStatic() && var->hasStorage())
results.push_back(var);
if (auto missing = dyn_cast<MissingMemberDecl>(member))
if (missing->getNumberOfFieldOffsetVectorEntries() > 0)
results.push_back(missing);
}
return decl->getASTContext().AllocateCopy(results);
}
/// Validate the \c entryNumber'th entry in \c binding.
const PatternBindingEntry *
PatternBindingEntryRequest::evaluate(Evaluator &eval,
PatternBindingDecl *binding,
unsigned entryNumber) const {
const auto &pbe = binding->getPatternList()[entryNumber];
auto &Context = binding->getASTContext();
// Resolve the pattern.
auto *pattern = TypeChecker::resolvePattern(binding->getPattern(entryNumber),
binding->getDeclContext(),
/*isStmtCondition*/ true);
if (!pattern) {
binding->setInvalid();
binding->getPattern(entryNumber)->setType(ErrorType::get(Context));
return &pbe;
}
binding->setPattern(entryNumber, pattern,
binding->getInitContext(entryNumber));
// Validate 'static'/'class' on properties in nominal type decls.
auto StaticSpelling = binding->getStaticSpelling();
if (StaticSpelling != StaticSpellingKind::None &&
isa<ExtensionDecl>(binding->getDeclContext())) {
if (auto *NTD = binding->getDeclContext()->getSelfNominalTypeDecl()) {
if (!isa<ClassDecl>(NTD)) {
if (StaticSpelling == StaticSpellingKind::KeywordClass) {
binding->diagnose(diag::class_var_not_in_class, false)
.fixItReplace(binding->getStaticLoc(), "static");
NTD->diagnose(diag::extended_type_declared_here);
}
}
}
}
// Check the pattern.
auto contextualPattern =
ContextualPattern::forPatternBindingDecl(binding, entryNumber);
Type patternType = TypeChecker::typeCheckPattern(contextualPattern);
if (patternType->hasError()) {
swift::setBoundVarsTypeError(pattern, Context);
binding->setInvalid();
pattern->setType(ErrorType::get(Context));
return &pbe;
}
// If we have a type but no initializer, check whether the type is
// default-initializable. If so, do it.
if (!pbe.isInitialized() &&
binding->isDefaultInitializable(entryNumber) &&
pattern->hasStorage()) {
if (auto defaultInit = TypeChecker::buildDefaultInitializer(patternType)) {
// If we got a default initializer, install it and re-type-check it
// to make sure it is properly coerced to the pattern type.
binding->setInit(entryNumber, defaultInit);
}
}
// If the pattern contains some form of unresolved type, we'll need to
// check the initializer.
if (patternType->hasUnresolvedType() ||
patternType->hasUnboundGenericType()) {
if (TypeChecker::typeCheckPatternBinding(binding, entryNumber,
patternType)) {
binding->setInvalid();
return &pbe;
}
// A pattern binding at top level is not allowed to pick up another decl's
// opaque result type as its type by type inference.
if (!binding->getDeclContext()->isLocalContext() &&
binding->getInit(entryNumber)->getType()->hasOpaqueArchetype()) {
// TODO: Check whether the type is the pattern binding's own opaque type.
binding->diagnose(diag::inferred_opaque_type,
binding->getInit(entryNumber)->getType());
}
} else {
// Coerce the pattern to the computed type.
if (auto newPattern = TypeChecker::coercePatternToType(
contextualPattern, patternType,
TypeResolverContext::PatternBindingDecl)) {
pattern = newPattern;
} else {
binding->setInvalid();
pattern->setType(ErrorType::get(Context));
return &pbe;
}
}
// If the pattern binding appears in a type or library file context, then
// it must bind at least one variable.
if (!contextAllowsPatternBindingWithoutVariables(binding->getDeclContext())) {
llvm::SmallVector<VarDecl *, 2> vars;
binding->getPattern(entryNumber)->collectVariables(vars);
if (vars.empty()) {
// Selector for error message.
enum : unsigned {
Property,
GlobalVariable,
};
Context.Diags.diagnose(binding->getPattern(entryNumber)->getLoc(),
diag::pattern_binds_no_variables,
binding->getDeclContext()->isTypeContext()
? Property
: GlobalVariable);
}
}
return &pbe;
}
bool
IsGetterMutatingRequest::evaluate(Evaluator &evaluator,
AbstractStorageDecl *storage) const {
auto storageDC = storage->getDeclContext();
bool result = (!storage->isStatic() && storageDC->isTypeContext() &&
storageDC->hasValueSemantics());
// 'lazy' overrides the normal accessor-based rules and heavily
// restricts what accessors can be used. The getter is considered
// mutating if this is instance storage on a value type.
if (storage->getAttrs().hasAttribute<LazyAttr>()) {
return result;
}
// If we have an attached property wrapper, the getter's mutating-ness
// depends on the composition of the wrappers.
if (auto var = dyn_cast<VarDecl>(storage)) {
if (auto mut = var->getPropertyWrapperMutability()) {
return mut->Getter == PropertyWrapperMutability::Mutating
&& result;
}
}
auto checkMutability = [&](AccessorKind kind) -> bool {
auto *accessor = storage->getParsedAccessor(kind);
if (!accessor)
return false;
return accessor->isMutating();
};
// Protocol requirements are always written as '{ get }' or '{ get set }';
// the @_borrowed attribute determines if getReadImpl() becomes Get or Read.
if (isa<ProtocolDecl>(storageDC))
return checkMutability(AccessorKind::Get);
switch (storage->getReadImpl()) {
case ReadImplKind::Stored:
case ReadImplKind::Inherited:
return false;
case ReadImplKind::Get:
return checkMutability(AccessorKind::Get);
case ReadImplKind::Address:
return checkMutability(AccessorKind::Address);
case ReadImplKind::Read:
return checkMutability(AccessorKind::Read);
}
llvm_unreachable("bad impl kind");
}
bool
IsSetterMutatingRequest::evaluate(Evaluator &evaluator,
AbstractStorageDecl *storage) const {
// By default, the setter is mutating if we have an instance member of a
// value type, but this can be overridden below.
auto storageDC = storage->getDeclContext();
bool result = (!storage->isStatic() && storageDC->isTypeContext() &&
storageDC->hasValueSemantics());
// If we have an attached property wrapper, the setter is mutating
// or not based on the composition of the wrappers.
if (auto var = dyn_cast<VarDecl>(storage)) {
if (auto mut = var->getPropertyWrapperMutability()) {
bool isMutating = mut->Setter == PropertyWrapperMutability::Mutating;
if (auto *didSet = var->getParsedAccessor(AccessorKind::DidSet)) {
// If there's a didSet, we call the getter for the 'oldValue', and so
// should consider the getter's mutatingness as well
isMutating |= (mut->Getter == PropertyWrapperMutability::Mutating);
isMutating |= didSet->getAttrs().hasAttribute<MutatingAttr>();
}
if (auto *willSet = var->getParsedAccessor(AccessorKind::WillSet))
isMutating |= willSet->getAttrs().hasAttribute<MutatingAttr>();
return isMutating && result;
}
}
auto impl = storage->getImplInfo();
switch (impl.getWriteImpl()) {
case WriteImplKind::Immutable:
case WriteImplKind::Stored:
// Instance member setters are mutating; static property setters and
// top-level setters are not.
// It's important that we use this logic for "immutable" storage
// in order to handle initialization of let-properties.
return result;
case WriteImplKind::StoredWithObservers:
case WriteImplKind::InheritedWithObservers:
case WriteImplKind::Set: {
auto *setter = storage->getParsedAccessor(AccessorKind::Set);
if (setter)
result = setter->isMutating();
// As a special extra check, if the user also gave us a modify
// coroutine, check that it has the same mutatingness as the setter.
// TODO: arguably this should require the spelling to match even when
// it's the implied value.
auto modifyAccessor = storage->getParsedAccessor(AccessorKind::Modify);
if (impl.getReadWriteImpl() == ReadWriteImplKind::Modify &&
modifyAccessor != nullptr) {
auto modifyResult = modifyAccessor->isMutating();
if ((result || storage->isGetterMutating()) != modifyResult) {
modifyAccessor->diagnose(
diag::modify_mutatingness_differs_from_setter,
modifyResult ? SelfAccessKind::Mutating
: SelfAccessKind::NonMutating,
modifyResult ? SelfAccessKind::NonMutating
: SelfAccessKind::Mutating);
if (setter)
setter->diagnose(diag::previous_accessor, "setter", 0);
modifyAccessor->setInvalid();
}
}
return result;
}
case WriteImplKind::MutableAddress:
return storage->getParsedAccessor(AccessorKind::MutableAddress)
->isMutating();
case WriteImplKind::Modify:
return storage->getParsedAccessor(AccessorKind::Modify)
->isMutating();
}
llvm_unreachable("bad storage kind");
}
OpaqueReadOwnership
OpaqueReadOwnershipRequest::evaluate(Evaluator &evaluator,
AbstractStorageDecl *storage) const {
return (storage->getAttrs().hasAttribute<BorrowedAttr>()
? OpaqueReadOwnership::Borrowed
: OpaqueReadOwnership::Owned);
}
/// 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((DC->isLocalContext() || isa<FileUnit>(DC)) &&
"Unknown declcontext");
}
}
/// 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,
ASTContext &context) {
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);
// Give all of the parameters meaningless names so that we can forward
// them properly. If it's declared anonymously, SILGen will think
// it's unused.
// TODO: use some special DeclBaseName for this?
for (auto param : indices->getArray()) {
if (!param->hasName())
param->setName(context.getIdentifier("anonymous"));
assert(param->hasName());
}
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);
}
/// Create the generic parameters needed for the given accessor, if any.
static GenericParamList *createAccessorGenericParams(
AbstractStorageDecl *storage) {
// Accessors of generic subscripts get a copy of the subscript's
// generic parameter list, because they're not nested inside the
// subscript.
if (auto *subscript = dyn_cast<SubscriptDecl>(storage)) {
if (auto genericParams = subscript->getGenericParams())
return genericParams->clone(subscript->getDeclContext());
}
return nullptr;
}
static bool doesAccessorHaveBody(AccessorDecl *accessor) {
// Protocol requirements don't have bodies.
//
// FIXME: Revisit this if we ever get 'real' default implementations.
if (isa<ProtocolDecl>(accessor->getDeclContext()))
return false;
auto *storage = accessor->getStorage();
// NSManaged getters and setters don't have bodies.
if (storage->getAttrs().hasAttribute<NSManagedAttr>())
if (accessor->isGetterOrSetter())
return false;
return true;
}
/// 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->getParameters()->getArray();
auto accessorKind = accessor->getAccessorKind();
// Ignore the value parameter of a setter.
if (accessorKind == AccessorKind::Set) {
params = params.slice(1);
}
// Okay, everything else should be forwarded, build the expression.
return buildArgumentForwardingExpr(params, ctx);
}
namespace {
enum class TargetImpl {
/// We're doing an ordinary storage reference.
Ordinary,
/// We're referencing the physical storage created for the storage.
Storage,
/// We're referencing this specific implementation of the storage, not
/// an override of it.
Implementation,
/// We're referencing the superclass's implementation of the storage.
Super,
/// We're referencing the backing property for a property with a wrapper
/// through the 'value' property.
Wrapper,
/// We're referencing the backing property for a property with a wrapper
/// through the 'projectedValue' property.
WrapperStorage,
};
} // end anonymous namespace
namespace {
/// Describes the information needed to perform property wrapper access via
/// the enclosing self.
struct EnclosingSelfPropertyWrapperAccess {
/// The (genreric) subscript that will be used to perform the access.
SubscriptDecl *subscript;
/// The property being accessed.
VarDecl *accessedProperty;
};
}
/// Determine whether the given property should be accessed via the enclosing-self access pattern.
static Optional<EnclosingSelfPropertyWrapperAccess>
getEnclosingSelfPropertyWrapperAccess(VarDecl *property, bool forProjected) {
// The enclosing-self pattern only applies to instance properties of
// classes.
if (!property->isInstanceMember())
return None;
auto classDecl = property->getDeclContext()->getSelfClassDecl();
if (!classDecl)
return None;
// The pattern currently only works with the outermost property wrapper.
Type outermostWrapperType = property->getPropertyWrapperBackingPropertyType();
if (!outermostWrapperType)
return None;
NominalTypeDecl *wrapperTypeDecl = outermostWrapperType->getAnyNominal();
if (!wrapperTypeDecl)
return None;
// Look for a generic subscript that fits the general form we need.
auto wrapperInfo = wrapperTypeDecl->getPropertyWrapperTypeInfo();
auto subscript =
forProjected ? wrapperInfo.enclosingInstanceProjectedSubscript
: wrapperInfo.enclosingInstanceWrappedSubscript;
if (!subscript)
return None;
EnclosingSelfPropertyWrapperAccess result;
result.subscript = subscript;
if (forProjected) {
result.accessedProperty =
property->getPropertyWrapperBackingPropertyInfo().projectionVar;
} else {
result.accessedProperty = property;
}
return result;
}
static Optional<PropertyWrapperLValueness>
getPropertyWrapperLValueness(VarDecl *var) {
auto &ctx = var->getASTContext();
return evaluateOrDefault(
ctx.evaluator,
PropertyWrapperLValuenessRequest{var},
None);
}
/// Build a reference to the storage of a declaration. Returns nullptr if there
/// was an error. This should only occur if an invalid declaration was type
/// checked; another diagnostic should have been emitted already.
///
/// The resulting reference is used in synthesized property accessors and is of
/// one of the following forms:
/// 1. Without property wrappers:
/// - Stored: \c self.member
/// 2. With property wrappers:
/// - Wrapped: \c self._member.wrappedValue
/// - Composition: \c self._member.wrappedValue.wrappedValue….wrappedValue
/// - Projected: \c self._member.projectedValue
/// - Enclosed instance: \c Wrapper[_enclosedInstance: self, …]
static Expr *buildStorageReference(AccessorDecl *accessor,
AbstractStorageDecl *storage,
TargetImpl target,
bool isUsedForGetAccess,
bool isUsedForSetAccess,
ASTContext &ctx) {
// Whether the last component of the expression should be an l-value
bool isLValue = isUsedForSetAccess;
// Local function to "finish" the expression, creating a member reference
// to the given sequence of underlying variables.
Optional<EnclosingSelfPropertyWrapperAccess> enclosingSelfAccess;
// Contains the underlying wrappedValue declaration in a property wrapper
// along with whether or not the reference to this field needs to be an lvalue
llvm::SmallVector<std::pair<VarDecl *, bool>, 1> underlyingVars;
auto finish = [&](Expr *result) -> Expr * {
for (auto underlyingVarPair : underlyingVars) {
auto underlyingVar = underlyingVarPair.first;
auto isWrapperRefLValue = underlyingVarPair.second;
auto subs = result->getType()
->getWithoutSpecifierType()
->getContextSubstitutionMap(
accessor->getParentModule(),
underlyingVar->getDeclContext());
ConcreteDeclRef memberRef(underlyingVar, subs);
auto *memberRefExpr = new (ctx) MemberRefExpr(
result, SourceLoc(), memberRef, DeclNameLoc(), /*Implicit=*/true);
auto type = underlyingVar->getValueInterfaceType().subst(subs);
if (isWrapperRefLValue)
type = LValueType::get(type);
memberRefExpr->setType(type);
result = memberRefExpr;
}
return result;
};
VarDecl *selfDecl = accessor->getImplicitSelfDecl();
AccessSemantics semantics;
SelfAccessorKind selfAccessKind;
Type selfTypeForAccess = (selfDecl ? selfDecl->getType() : Type());
bool isMemberLValue = isLValue;
auto *genericEnv = accessor->getGenericEnvironment();
SubstitutionMap subs;
if (genericEnv)
subs = genericEnv->getForwardingSubstitutionMap();
switch (target) {
case TargetImpl::Ordinary:
semantics = AccessSemantics::Ordinary;
selfAccessKind = SelfAccessorKind::Peer;
break;
case TargetImpl::Storage:
semantics = AccessSemantics::DirectToStorage;
selfAccessKind = SelfAccessorKind::Peer;
break;
case TargetImpl::Implementation:
semantics = AccessSemantics::DirectToImplementation;
selfAccessKind = SelfAccessorKind::Peer;
break;
case TargetImpl::Super:
// If this really is an override, use a super-access.
if (auto override = storage->getOverriddenDecl()) {
semantics = AccessSemantics::Ordinary;
selfAccessKind = SelfAccessorKind::Super;
auto isMetatype = false;
if (auto *metaTy = selfTypeForAccess->getAs<MetatypeType>()) {
isMetatype = true;
selfTypeForAccess = metaTy->getInstanceType();
}
// Adjust the self type of the access to refer to the relevant superclass.
auto *baseClass = override->getDeclContext()->getSelfClassDecl();
selfTypeForAccess = selfTypeForAccess->getSuperclassForDecl(baseClass);
subs =
selfTypeForAccess->getContextSubstitutionMap(
accessor->getParentModule(),
baseClass);
storage = override;
if (isMetatype)
selfTypeForAccess = MetatypeType::get(selfTypeForAccess);
// Otherwise do a self-reference, which is dynamically bogus but
// should be statically valid. This should only happen in invalid cases.
} else {
semantics = AccessSemantics::Ordinary;
selfAccessKind = SelfAccessorKind::Peer;
}
break;
case TargetImpl::Wrapper: {
auto var = cast<VarDecl>(accessor->getStorage());
auto *backing = var->getPropertyWrapperBackingProperty();
// Error recovery.
if (!backing || backing->isInvalid())
return nullptr;
storage = backing;
// If the outermost property wrapper uses the enclosing self pattern,
// record that.
unsigned lastWrapperIdx = var->getAttachedPropertyWrappers().size();
unsigned firstWrapperIdx = 0;
enclosingSelfAccess =
getEnclosingSelfPropertyWrapperAccess(var, /*forProjected=*/false);
if (enclosingSelfAccess)
firstWrapperIdx = 1;
// Perform accesses to the wrappedValues along the composition chain.
if (firstWrapperIdx < lastWrapperIdx) {
auto lvalueness = *getPropertyWrapperLValueness(var);
// Figure out if the outermost wrapper instance should be an l-value
bool isLValueForGet = lvalueness.isLValueForGetAccess[firstWrapperIdx];
bool isLValueForSet = lvalueness.isLValueForSetAccess[firstWrapperIdx];
isMemberLValue = (isLValueForGet && isUsedForGetAccess) ||
(isLValueForSet && isUsedForSetAccess);
for (unsigned i : range(firstWrapperIdx, lastWrapperIdx)) {
auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(i);
auto wrappedValue = wrapperInfo.valueVar;
// Figure out if the wrappedValue accesses should be l-values
bool isWrapperRefLValue = isLValue;
if (i < lastWrapperIdx - 1) {
bool isLValueForGet = lvalueness.isLValueForGetAccess[i+1];
bool isLValueForSet = lvalueness.isLValueForSetAccess[i+1];
isWrapperRefLValue = (isLValueForGet && isUsedForGetAccess) ||
(isLValueForSet && isUsedForSetAccess);
}
// Check for availability of wrappedValue.
if (accessor->getAccessorKind() == AccessorKind::Get ||
accessor->getAccessorKind() == AccessorKind::Read) {
if (wrappedValue->getAttrs().getUnavailable(ctx)) {
ExportContext where = ExportContext::forDeclSignature(var);
diagnoseExplicitUnavailability(
wrappedValue,
var->getAttachedPropertyWrappers()[i]->getRangeWithAt(),
where, nullptr);
}
}
underlyingVars.push_back({ wrappedValue, isWrapperRefLValue });
}
}
semantics = AccessSemantics::DirectToStorage;
selfAccessKind = SelfAccessorKind::Peer;
break;
}
case TargetImpl::WrapperStorage: {
auto var =
cast<VarDecl>(accessor->getStorage())->getOriginalWrappedProperty();
auto *backing = var->getPropertyWrapperBackingProperty();
// Error recovery.
if (!backing || backing->isInvalid())
return nullptr;
storage = backing;
enclosingSelfAccess =
getEnclosingSelfPropertyWrapperAccess(var, /*forProjected=*/true);
if (!enclosingSelfAccess) {
auto projectionVar = cast<VarDecl>(accessor->getStorage());
if (auto lvalueness = getPropertyWrapperLValueness(projectionVar)) {
isMemberLValue =
(lvalueness->isLValueForGetAccess[0] && isUsedForGetAccess) ||
(lvalueness->isLValueForSetAccess[0] && isUsedForSetAccess);
}
underlyingVars.push_back(
{ var->getAttachedPropertyWrapperTypeInfo(0).projectedValueVar,
isLValue });
}
semantics = AccessSemantics::DirectToStorage;
selfAccessKind = SelfAccessorKind::Peer;
break;
}
}
// If the base is not 'self', default get access to nonmutating and set access to mutating.
bool getterMutatesBase = selfDecl && storage->isGetterMutating();
bool setterMutatesBase = !selfDecl || storage->isSetterMutating();
// If we're not accessing via a property wrapper, we don't need to adjust
// the mutability.
if (target == TargetImpl::Wrapper || target == TargetImpl::WrapperStorage) {
auto var = cast<VarDecl>(accessor->getStorage());
auto mutability = var->getPropertyWrapperMutability();
// Only adjust mutability if it's possible to mutate the base.
if (mutability && !var->isStatic() &&
!(selfDecl && selfTypeForAccess->hasReferenceSemantics())) {
getterMutatesBase = (mutability->Getter == PropertyWrapperMutability::Mutating);
setterMutatesBase = (mutability->Setter == PropertyWrapperMutability::Mutating);
}
}
// If the accessor is mutating, then the base should be referred as an l-value
bool isBaseLValue = (getterMutatesBase && isUsedForGetAccess) ||
(setterMutatesBase && isUsedForSetAccess);
if (!selfDecl) {
assert(target != TargetImpl::Super);
auto *storageDRE = new (ctx) DeclRefExpr(storage, DeclNameLoc(),
/*IsImplicit=*/true, semantics);
auto type = storage->getValueInterfaceType().subst(subs);
if (isBaseLValue)
type = LValueType::get(type);
storageDRE->setType(type);
return finish(storageDRE);
}
// Build self
Expr *selfDRE = buildSelfReference(selfDecl, selfAccessKind, isBaseLValue,
/*convertTy*/ selfTypeForAccess);
if (isBaseLValue)
selfTypeForAccess = LValueType::get(selfTypeForAccess);
if (!selfDRE->getType()->isEqual(selfTypeForAccess)) {
assert(selfAccessKind == SelfAccessorKind::Super);
selfDRE = new (ctx) DerivedToBaseExpr(selfDRE, selfTypeForAccess);
}
// Build self.member or equivalent
Expr *lookupExpr;
ConcreteDeclRef memberRef(storage, subs);
auto type = storage->getValueInterfaceType().subst(subs);
if (isMemberLValue)
type = LValueType::get(type);
// When we are performing access via a property wrapper's static subscript
// that accepts the enclosing self along with key paths, form that subscript
// operation now.
if (enclosingSelfAccess) {
Type storageType = storage->getValueInterfaceType().subst(subs);
// Metatype instance for the wrapper type itself.
TypeExpr *wrapperMetatype = TypeExpr::createImplicit(storageType, ctx);
// Key path referring to the property being accessed.
Expr *propertyKeyPath = new (ctx) KeyPathDotExpr(SourceLoc());
propertyKeyPath = UnresolvedDotExpr::createImplicit(ctx, propertyKeyPath,
enclosingSelfAccess->accessedProperty->getName());
propertyKeyPath = new (ctx) KeyPathExpr(
SourceLoc(), nullptr, propertyKeyPath, /*hasLeadingDot=*/true);
// Key path referring to the backing storage property.
Expr *storageKeyPath = new (ctx) KeyPathDotExpr(SourceLoc());
storageKeyPath = UnresolvedDotExpr::createImplicit(ctx, storageKeyPath,
storage->getName());
storageKeyPath = new (ctx) KeyPathExpr(SourceLoc(), nullptr, storageKeyPath,
/*hasLeadingDot=*/true);
Expr *args[3] = {selfDRE, propertyKeyPath, storageKeyPath};
SubscriptDecl *subscriptDecl = enclosingSelfAccess->subscript;
lookupExpr = SubscriptExpr::create(
ctx, wrapperMetatype, SourceLoc(), args,
subscriptDecl->getName().getArgumentNames(), { }, SourceLoc(),
/*trailingClosures=*/{}, subscriptDecl, /*Implicit=*/true);
// FIXME: Since we're not resolving overloads or anything, we should be
// building fully type-checked AST above; we already have all the
// information that we need.
if (!TypeChecker::typeCheckExpression(lookupExpr, accessor))
return nullptr;
// Make sure we produce an lvalue only when desired.
if (isMemberLValue != lookupExpr->getType()->is<LValueType>()) {
if (isMemberLValue) {
// Strip off an extraneous load.
if (auto load = dyn_cast<LoadExpr>(lookupExpr))
lookupExpr = load->getSubExpr();
} else {
lookupExpr = new (ctx) LoadExpr(
lookupExpr, lookupExpr->getType()->getRValueType());
}
}
} else if (isa<SubscriptDecl>(storage)) {
Expr *indices = buildSubscriptIndexReference(ctx, accessor);
lookupExpr = SubscriptExpr::create(ctx, selfDRE, indices, memberRef,
/*IsImplicit=*/true, semantics);
if (selfAccessKind == SelfAccessorKind::Super)
cast<LookupExpr>(lookupExpr)->setIsSuper(true);
lookupExpr->setType(type);
} else {
lookupExpr = new (ctx) MemberRefExpr(selfDRE, SourceLoc(), memberRef,
DeclNameLoc(), /*IsImplicit=*/true,
semantics);
if (selfAccessKind == SelfAccessorKind::Super)
cast<LookupExpr>(lookupExpr)->setIsSuper(true);
lookupExpr->setType(type);
}
// Build self.member.wrappedValue if applicable
return finish(lookupExpr);
}
/// 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,
TargetImpl target,
ASTContext &ctx) {
return buildStorageReference(accessor, storage, target,
/*isUsedForGetAccess=*/true,
/*isUsedForSetAccess=*/false,
ctx);
}
static ProtocolConformanceRef checkConformanceToNSCopying(VarDecl *var,
Type type) {
auto dc = var->getDeclContext();
auto &ctx = dc->getASTContext();
auto proto = ctx.getNSCopyingDecl();
if (proto) {
if (auto result = TypeChecker::conformsToProtocol(type, proto, dc))
return result;
}
ctx.Diags.diagnose(var->getLoc(), diag::nscopying_doesnt_conform);
return ProtocolConformanceRef::forInvalid();
}
static std::pair<Type, bool> getUnderlyingTypeOfVariable(VarDecl *var) {
Type type = var->getType()->getReferenceStorageReferent();
if (Type objectType = type->getOptionalObjectType()) {
return {objectType, true};
} else {
return {type, false};
}
}
ProtocolConformanceRef TypeChecker::checkConformanceToNSCopying(VarDecl *var) {
Type type = getUnderlyingTypeOfVariable(var).first;
return ::checkConformanceToNSCopying(var, type);
}
/// 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,
ASTContext &Ctx) {
// We support @NSCopying on class types (which conform to NSCopying),
// protocols which conform, and option types thereof.
auto underlyingTypeAndIsOptional = getUnderlyingTypeOfVariable(VD);
auto underlyingType = underlyingTypeAndIsOptional.first;
auto isOptional = underlyingTypeAndIsOptional.second;
// The element type must conform to NSCopying. If not, emit an error and just
// recovery by synthesizing without the copy call.
auto conformance = checkConformanceToNSCopying(VD, underlyingType);
if (!conformance)
return Val;
//- (id)copyWithZone:(NSZone *)zone;
DeclName copyWithZoneName(Ctx, Ctx.getIdentifier("copy"), { Ctx.Id_with });
FuncDecl *copyMethod = nullptr;
for (auto member : conformance.getRequirement()->getMembers()) {
if (auto func = dyn_cast<FuncDecl>(member)) {
if (func->getName() == copyWithZoneName) {
copyMethod = func;
break;
}
}
}
assert(copyMethod != nullptr);
// 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);
Val->setType(underlyingType);
}
SubstitutionMap subs =
SubstitutionMap::get(copyMethod->getGenericSignature(), {underlyingType},
ArrayRef<ProtocolConformanceRef>(conformance));
ConcreteDeclRef copyMethodRef(copyMethod, subs);
auto copyMethodType = copyMethod->getInterfaceType()
->castTo<GenericFunctionType>()
->substGenericArgs(subs);
auto DRE = new (Ctx) DeclRefExpr(copyMethodRef, DeclNameLoc(),
/*IsImplicit=*/true);
DRE->setType(copyMethodType);
// Drop the self type
copyMethodType = copyMethodType->getResult()->castTo<FunctionType>();
auto DSCE = new (Ctx) DotSyntaxCallExpr(DRE, SourceLoc(), Val);
DSCE->setImplicit();
DSCE->setType(copyMethodType);
DSCE->setThrows(false);
Expr *Nil = new (Ctx) NilLiteralExpr(SourceLoc(), /*implicit*/true);
Nil->setType(copyMethodType->getParams()[0].getParameterType());
auto *Call = CallExpr::createImplicit(Ctx, DSCE, { Nil }, { Ctx.Id_with });
Call->setType(copyMethodType->getResult());
Call->setThrows(false);
// If we're working with non-optional types, we're forcing the cast.
if (!isOptional) {
auto *const Cast =
ForcedCheckedCastExpr::createImplicit(Ctx, Call, underlyingType);
Cast->setCastKind(CheckedCastKind::ValueCast);
return Cast;
}
// We're working with optional types, so perform a conditional checked
// downcast.
auto *const Cast =
ConditionalCheckedCastExpr::createImplicit(Ctx, Call, underlyingType);
Cast->setCastKind(CheckedCastKind::ValueCast);
// Use OptionalEvaluationExpr to evaluate the "?".
auto *Result = new (Ctx) OptionalEvaluationExpr(Cast);
Result->setType(OptionalType::get(underlyingType));
return Result;
}
/// 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,
TargetImpl target,
SmallVectorImpl<ASTNode> &body,
ASTContext &ctx) {
// 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, ctx);
}
// Error recovery.
if (value->getType()->hasError())
return;
Expr *dest = buildStorageReference(accessor, storage, target,
/*isUsedForGetAccess=*/false,
/*isUsedForSetAccess=*/true,
ctx);
// Error recovery.
if (dest == nullptr)
return;
// A lazy property setter will store a value of type T into underlying storage
// of type T?.
auto destType = dest->getType()->getWithoutSpecifierType();
// Error recovery.
if (destType->hasError())
return;
if (!destType->isEqual(value->getType())) {
assert(destType->getOptionalObjectType());
assert(destType->getOptionalObjectType()->isEqual(value->getType()));
value = new (ctx) InjectIntoOptionalExpr(value, destType);
}
auto *assign = new (ctx) AssignExpr(dest, SourceLoc(), value,
/*IsImplicit=*/true);
assign->setType(ctx.TheEmptyTupleType);
body.push_back(assign);
}
LLVM_ATTRIBUTE_UNUSED
static bool isSynthesizedComputedProperty(AbstractStorageDecl *storage) {
return (storage->getAttrs().hasAttribute<LazyAttr>() ||
storage->getAttrs().hasAttribute<NSManagedAttr>() ||
(isa<VarDecl>(storage) &&
cast<VarDecl>(storage)->hasAttachedPropertyWrapper()));
}
/// 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 std::pair<BraceStmt *, bool>
synthesizeTrivialGetterBody(AccessorDecl *getter, TargetImpl target,
ASTContext &ctx) {
auto storage = getter->getStorage();
assert(!isSynthesizedComputedProperty(storage) ||
target == TargetImpl::Wrapper ||
target == TargetImpl::WrapperStorage);
SourceLoc loc = storage->getLoc();
Expr *result =
createPropertyLoadOrCallSuperclassGetter(getter, storage, target, ctx);
SmallVector<ASTNode, 2> body;
if (result != nullptr) {
ASTNode returnStmt = new (ctx) ReturnStmt(SourceLoc(), result,
/*IsImplicit=*/true);
body.push_back(returnStmt);
}
return { BraceStmt::create(ctx, loc, body, loc, true),
/*isTypeChecked=*/true };
}
/// Synthesize the body of a getter which just directly accesses the
/// underlying storage.
static std::pair<BraceStmt *, bool>
synthesizeTrivialGetterBody(AccessorDecl *getter, ASTContext &ctx) {
assert(getter->getStorage()->hasStorage());
return synthesizeTrivialGetterBody(getter, TargetImpl::Storage, ctx);
}
/// Synthesize the body of a getter which just delegates to its superclass
/// implementation.
static std::pair<BraceStmt *, bool>
synthesizeInheritedGetterBody(AccessorDecl *getter, ASTContext &ctx) {
// This should call the superclass getter.
return synthesizeTrivialGetterBody(getter, TargetImpl::Super, ctx);
}
/// Synthesize the body of a getter which just delegates to an addressor.
static std::pair<BraceStmt *, bool>
synthesizeAddressedGetterBody(AccessorDecl *getter, ASTContext &ctx) {
assert(getter->getStorage()->getParsedAccessor(AccessorKind::Address));
// This should call the addressor.
return synthesizeTrivialGetterBody(getter, TargetImpl::Implementation, ctx);
}
/// Synthesize the body of a getter which just delegates to a read
/// coroutine accessor.
static std::pair<BraceStmt *, bool>
synthesizeReadCoroutineGetterBody(AccessorDecl *getter, ASTContext &ctx) {
assert(getter->getStorage()->getParsedAccessor(AccessorKind::Read));
// This should call the read coroutine.
return synthesizeTrivialGetterBody(getter, TargetImpl::Implementation, ctx);
}
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);
}
}
// Unlike a closure, a TapExpr is not a DeclContext, so we need to
// recontextualize its variable and then anything else in its body.
// FIXME: Might be better to change walkToDeclPre() and walkToStmtPre()
// below, but I don't know what other effects that might have.
if (auto TE = dyn_cast<TapExpr>(E)) {
TE->getVar()->setDeclContext(NewDC);
for (auto node : TE->getBody()->getElements())
node.walk(RecontextualizeClosures(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 std::pair<BraceStmt *, bool>
synthesizeLazyGetterBody(AccessorDecl *Get, VarDecl *VD, VarDecl *Storage,
ASTContext &Ctx) {
// The getter checks the optional, storing the initial value in if nil. The
// specific pattern we generate is:
// get {
// if let tmp1 = storage {
// return tmp1
// }
// let tmp2 : Ty = <<initializer expression>>
// storage = tmp2
// return tmp2
// }
SmallVector<ASTNode, 6> Body;
// Load the existing storage and store it into the 'tmp1' temporary.
auto *Tmp1VD = new (Ctx) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Let,
SourceLoc(), Ctx.getIdentifier("tmp1"), Get);
Tmp1VD->setInterfaceType(VD->getValueInterfaceType());
Tmp1VD->setImplicit();
auto *Named = NamedPattern::createImplicit(Ctx, Tmp1VD);
Named->setType(Tmp1VD->getType());
auto *Let = BindingPattern::createImplicit(Ctx, /*let*/ true, Named);
Let->setType(Named->getType());
auto *Some = new (Ctx) OptionalSomePattern(Let, SourceLoc());
Some->setImplicit();
Some->setElementDecl(Ctx.getOptionalSomeDecl());
Some->setType(OptionalType::get(Let->getType()));
auto *StoredValueExpr =
createPropertyLoadOrCallSuperclassGetter(Get, Storage,
TargetImpl::Storage, Ctx);
SmallVector<StmtConditionElement, 1> Cond;
Cond.emplace_back(SourceLoc(), Some, StoredValueExpr);
// Build the early return inside the if.
auto *Tmp1DRE = new (Ctx) DeclRefExpr(Tmp1VD, DeclNameLoc(), /*Implicit*/true,
AccessSemantics::Ordinary);
Tmp1DRE->setType(Tmp1VD->getType());
auto *Return = new (Ctx) ReturnStmt(SourceLoc(), Tmp1DRE,
/*implicit*/true);
// Build the "if" around the early return.
Body.push_back(new (Ctx) IfStmt(LabeledStmtInfo(),
SourceLoc(), Ctx.AllocateCopy(Cond), Return,
/*elseloc*/SourceLoc(), /*else*/nullptr,
/*implicit*/ true));
auto *Tmp2VD = new (Ctx) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Let,
SourceLoc(), Ctx.getIdentifier("tmp2"),
Get);
Tmp2VD->setInterfaceType(VD->getValueInterfaceType());
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 PBD = VD->getParentPatternBinding();
unsigned entryIndex = PBD->getPatternEntryIndexForVarDecl(VD);
Expr *InitValue;
if (PBD->getInit(entryIndex)) {
PBD->setInitializerSubsumed(entryIndex);
if (!PBD->isInitializerChecked(entryIndex))
TypeChecker::typeCheckPatternBinding(PBD, entryIndex);
InitValue = PBD->getInit(entryIndex);
} else {
InitValue = new (Ctx) ErrorExpr(SourceRange(), Tmp2VD->getType());
}
// Recontextualize any closure declcontexts nested in the initializer to
// realize that they are in the getter function.
Get->getImplicitSelfDecl()->setDeclContext(Get);
InitValue->walk(RecontextualizeClosures(Get));
// Wrap the initializer in a LazyInitializerExpr to avoid walking it twice.
auto initType = InitValue->getType();
InitValue = new (Ctx) LazyInitializerExpr(InitValue);
InitValue->setType(initType);
Pattern *Tmp2PBDPattern = NamedPattern::createImplicit(Ctx, Tmp2VD);
Tmp2PBDPattern =
TypedPattern::createImplicit(Ctx, Tmp2PBDPattern, Tmp2VD->getType());
auto *Tmp2PBD = PatternBindingDecl::createImplicit(
Ctx, StaticSpellingKind::None, Tmp2PBDPattern, InitValue, Get,
/*VarLoc*/ InitValue->getStartLoc());
Body.push_back(Tmp2PBD);
Body.push_back(Tmp2VD);
// Assign tmp2 into storage.
auto Tmp2DRE = new (Ctx) DeclRefExpr(Tmp2VD, DeclNameLoc(), /*Implicit*/true,
AccessSemantics::DirectToStorage);
Tmp2DRE->setType(Tmp2VD->getType());
createPropertyStoreOrCallSuperclassSetter(Get, Tmp2DRE, Storage,
TargetImpl::Storage, Body, Ctx);
// Return tmp2.
Tmp2DRE = new (Ctx) DeclRefExpr(Tmp2VD, DeclNameLoc(), /*Implicit*/true,
AccessSemantics::DirectToStorage);
Tmp2DRE->setType(Tmp2VD->getType());
Body.push_back(new (Ctx) ReturnStmt(SourceLoc(), Tmp2DRE, /*implicit*/true));
auto Range = InitValue->getSourceRange();
return { BraceStmt::create(Ctx, Range.Start, Body, Range.End,
/*implicit*/true),
/*isTypeChecked=*/true };
}
/// Synthesize the body of a getter for a property wrapper, which
/// delegates to the wrapper's "value" property.
static std::pair<BraceStmt *, bool>
synthesizePropertyWrapperGetterBody(AccessorDecl *getter, ASTContext &ctx) {
return synthesizeTrivialGetterBody(getter, TargetImpl::Wrapper, ctx);
}
static std::pair<BraceStmt *, bool>
synthesizeInvalidAccessor(AccessorDecl *accessor, ASTContext &ctx) {
auto loc = accessor->getLoc();
return { BraceStmt::create(ctx, loc, ArrayRef<ASTNode>(), loc, true), true };
}
static std::pair<BraceStmt *, bool>
synthesizeGetterBody(AccessorDecl *getter, ASTContext &ctx) {
auto storage = getter->getStorage();
// Synthesize the getter for a lazy property or property wrapper.
if (auto var = dyn_cast<VarDecl>(storage)) {
if (var->getAttrs().hasAttribute<LazyAttr>()) {
auto *storage = var->getLazyStorageProperty();
return synthesizeLazyGetterBody(getter, var, storage, ctx);
}
if (var->hasAttachedPropertyWrapper()) {
return synthesizePropertyWrapperGetterBody(getter, ctx);
}
if (var->getOriginalWrappedProperty(
PropertyWrapperSynthesizedPropertyKind::Projection)) {
return synthesizeTrivialGetterBody(getter, TargetImpl::WrapperStorage,
ctx);
}
}
if (getter->hasForcedStaticDispatch()) {
return synthesizeTrivialGetterBody(getter, TargetImpl::Ordinary, ctx);
}
switch (getter->getStorage()->getReadImpl()) {
case ReadImplKind::Stored:
return synthesizeTrivialGetterBody(getter, ctx);
case ReadImplKind::Get:
return synthesizeInvalidAccessor(getter, ctx);
case ReadImplKind::Inherited:
return synthesizeInheritedGetterBody(getter, ctx);
case ReadImplKind::Address:
return synthesizeAddressedGetterBody(getter, ctx);
case ReadImplKind::Read:
return synthesizeReadCoroutineGetterBody(getter, ctx);
}
llvm_unreachable("bad ReadImplKind");
}
/// Synthesize the body of a setter which just stores to the given storage
/// declaration (which doesn't have to be the storage for the setter).
static std::pair<BraceStmt *, bool>
synthesizeTrivialSetterBodyWithStorage(AccessorDecl *setter,
TargetImpl target,
AbstractStorageDecl *storageToUse,
ASTContext &ctx) {
SourceLoc loc = setter->getStorage()->getLoc();
VarDecl *valueParamDecl = setter->getParameters()->get(0);
auto *valueDRE =
new (ctx) DeclRefExpr(valueParamDecl, DeclNameLoc(), /*IsImplicit=*/true);
valueDRE->setType(valueParamDecl->getType());
SmallVector<ASTNode, 1> setterBody;
createPropertyStoreOrCallSuperclassSetter(setter, valueDRE, storageToUse,
target, setterBody, ctx);
return { BraceStmt::create(ctx, loc, setterBody, loc, true),
/*isTypeChecked=*/true };
}
static std::pair<BraceStmt *, bool>
synthesizeTrivialSetterBody(AccessorDecl *setter, ASTContext &ctx) {
auto storage = setter->getStorage();
assert(!isSynthesizedComputedProperty(storage));
return synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::Storage,
storage, ctx);
}
/// Synthesize the body of a setter for a property wrapper, which
/// delegates to the wrapper's "value" property.
static std::pair<BraceStmt *, bool>
synthesizePropertyWrapperSetterBody(AccessorDecl *setter, ASTContext &ctx) {
return synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::Wrapper,
setter->getStorage(), ctx);
}
/// Synthesize the body of a setter which just delegates to a mutable
/// addressor.
static std::pair<BraceStmt *, bool>
synthesizeMutableAddressSetterBody(AccessorDecl *setter, ASTContext &ctx) {
// This should call the mutable addressor.
return synthesizeTrivialSetterBodyWithStorage(setter,
TargetImpl::Implementation,
setter->getStorage(), ctx);
}
/// Synthesize the body of a setter which just delegates to a modify
/// coroutine accessor.
static std::pair<BraceStmt *, bool>
synthesizeModifyCoroutineSetterBody(AccessorDecl *setter, ASTContext &ctx) {
// This should call the modify coroutine.
return synthesizeTrivialSetterBodyWithStorage(setter,
TargetImpl::Implementation,
setter->getStorage(), ctx);
}
static Expr *maybeWrapInOutExpr(Expr *expr, ASTContext &ctx) {
if (auto lvalueType = expr->getType()->getAs<LValueType>()) {
auto type = lvalueType->getObjectType();
return new (ctx) InOutExpr(SourceLoc(), expr, type, true);
}
return expr;
}
/// Given a VarDecl with a willSet: and/or didSet: specifier, synthesize the
/// setter which calls them.
static std::pair<BraceStmt *, bool>
synthesizeObservedSetterBody(AccessorDecl *Set, TargetImpl target,
ASTContext &Ctx, bool isLazy = false) {
auto VD = cast<VarDecl>(Set->getStorage());
SourceLoc Loc = VD->getLoc();
// Start by finding the decls for 'self' and 'value'.
auto *SelfDecl = Set->getImplicitSelfDecl();
VarDecl *ValueDecl = Set->getParameters()->get(0);
bool IsSelfLValue = VD->isSetterMutating();
SubstitutionMap subs;
if (auto *genericEnv = Set->getGenericEnvironment())
subs = genericEnv->getForwardingSubstitutionMap();
// 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;
auto callObserver = [&](AccessorDecl *observer, VarDecl *arg) {
ConcreteDeclRef ref(observer, subs);
auto type = observer->getInterfaceType().subst(subs);
Expr *Callee = new (Ctx) DeclRefExpr(ref, DeclNameLoc(), /*imp*/true);
Callee->setType(type);
DeclRefExpr *ValueDRE = nullptr;
if (arg) {
ValueDRE = new (Ctx) DeclRefExpr(arg, DeclNameLoc(), /*imp*/ true);
ValueDRE->setType(arg->getType());
}
if (SelfDecl) {
auto *SelfDRE =
buildSelfReference(SelfDecl, SelfAccessorKind::Peer, IsSelfLValue);
SelfDRE = maybeWrapInOutExpr(SelfDRE, Ctx);
auto *DSCE = new (Ctx) DotSyntaxCallExpr(Callee, SourceLoc(), SelfDRE);
if (auto funcType = type->getAs<FunctionType>())
type = funcType->getResult();
DSCE->setType(type);
DSCE->setThrows(false);
Callee = DSCE;
}
CallExpr *Call = nullptr;
if (arg) {
Call = CallExpr::createImplicit(Ctx, Callee, {ValueDRE}, {Identifier()});
} else {
Call = CallExpr::createImplicit(Ctx, Callee, {}, {});
}
if (auto funcType = type->getAs<FunctionType>())
type = funcType->getResult();
Call->setType(type);
Call->setThrows(false);
SetterBody.push_back(Call);
};
// If there is a didSet, it will take the old value. Load it into a temporary
// 'let' so we have it for later.
VarDecl *OldValue = nullptr;
if (auto didSet = VD->getParsedAccessor(AccessorKind::DidSet)) {
// Only do the load if the didSet body references the implicit oldValue
// parameter or it's provided explicitly in the parameter list.
if (!didSet->isSimpleDidSet()) {
Expr *OldValueExpr =
buildStorageReference(Set, VD, isLazy ? TargetImpl::Ordinary : target,
/*isUsedForGetAccess=*/true,
/*isUsedForSetAccess=*/true, Ctx);
// Error recovery.
if (OldValueExpr == nullptr) {
OldValueExpr = new (Ctx) ErrorExpr(SourceRange(), VD->getType());
} else {
OldValueExpr = new (Ctx) LoadExpr(OldValueExpr, VD->getType());
}
OldValue = new (Ctx) VarDecl(/*IsStatic*/ false, VarDecl::Introducer::Let,
SourceLoc(), Ctx.getIdentifier("tmp"), Set);
OldValue->setImplicit();
OldValue->setInterfaceType(VD->getValueInterfaceType());
auto *tmpPattern = NamedPattern::createImplicit(Ctx, OldValue);
auto *tmpPBD = PatternBindingDecl::createImplicit(
Ctx, StaticSpellingKind::None, tmpPattern, OldValueExpr, Set);
SetterBody.push_back(tmpPBD);
SetterBody.push_back(OldValue);
}
}
if (auto willSet = VD->getParsedAccessor(AccessorKind::WillSet))
callObserver(willSet, ValueDecl);
// Create an assignment into the storage or call to superclass setter.
auto *ValueDRE = new (Ctx) DeclRefExpr(ValueDecl, DeclNameLoc(), true);
ValueDRE->setType(ValueDecl->getType());
createPropertyStoreOrCallSuperclassSetter(
Set, ValueDRE, isLazy ? VD->getLazyStorageProperty() : VD, target,
SetterBody, Ctx);
if (auto didSet = VD->getParsedAccessor(AccessorKind::DidSet))
callObserver(didSet, OldValue);
return { BraceStmt::create(Ctx, Loc, SetterBody, Loc, true),
/*isTypeChecked=*/true };
}
static std::pair<BraceStmt *, bool>
synthesizeStoredWithObserversSetterBody(AccessorDecl *setter, ASTContext &ctx) {
return synthesizeObservedSetterBody(setter, TargetImpl::Storage, ctx);
}
static std::pair<BraceStmt *, bool>
synthesizeInheritedWithObserversSetterBody(AccessorDecl *setter,
ASTContext &ctx) {
return synthesizeObservedSetterBody(setter, TargetImpl::Super, ctx);
}
static std::pair<BraceStmt *, bool>
synthesizeSetterBody(AccessorDecl *setter, ASTContext &ctx) {
auto storage = setter->getStorage();
// Synthesize the setter for a lazy property or property wrapper.
if (auto var = dyn_cast<VarDecl>(storage)) {
if (var->getAttrs().hasAttribute<LazyAttr>()) {
// Lazy property setters write to the underlying storage.
if (var->hasObservers()) {
return synthesizeObservedSetterBody(setter, TargetImpl::Storage, ctx,
/*isLazy=*/true);
}
auto *storage = var->getLazyStorageProperty();
return synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::Storage,
storage, ctx);
}
if (var->hasAttachedPropertyWrapper()) {
if (var->hasObservers()) {
return synthesizeObservedSetterBody(setter, TargetImpl::Wrapper, ctx);
}
return synthesizePropertyWrapperSetterBody(setter, ctx);
}
// Synthesize a setter for the storage wrapper property of a property
// with an attached wrapper.
if (auto original = var->getOriginalWrappedProperty(
PropertyWrapperSynthesizedPropertyKind::Projection)) {
auto backingVar = original->getPropertyWrapperBackingProperty();
return synthesizeTrivialSetterBodyWithStorage(setter,
TargetImpl::WrapperStorage,
backingVar, ctx);
}
}
switch (storage->getWriteImpl()) {
case WriteImplKind::Immutable:
llvm_unreachable("synthesizing setter from immutable storage");
case WriteImplKind::Stored:
return synthesizeTrivialSetterBody(setter, ctx);
case WriteImplKind::StoredWithObservers:
return synthesizeStoredWithObserversSetterBody(setter, ctx);
case WriteImplKind::InheritedWithObservers:
return synthesizeInheritedWithObserversSetterBody(setter, ctx);
case WriteImplKind::Set:
return synthesizeInvalidAccessor(setter, ctx);
case WriteImplKind::MutableAddress:
return synthesizeMutableAddressSetterBody(setter, ctx);
case WriteImplKind::Modify:
return synthesizeModifyCoroutineSetterBody(setter, ctx);
}
llvm_unreachable("bad WriteImplKind");
}
static std::pair<BraceStmt *, bool>
synthesizeModifyCoroutineBodyWithSimpleDidSet(AccessorDecl *accessor,
ASTContext &ctx) {
auto storage = accessor->getStorage();
SourceLoc loc = storage->getLoc();
auto isOverride = storage->getOverriddenDecl();
auto target = isOverride ? TargetImpl::Super : TargetImpl::Storage;
SmallVector<ASTNode, 1> body;
Expr *ref = buildStorageReference(accessor, storage, target,
/*isUsedForGetAccess=*/true,
/*isUsedForSetAccess=*/true,
ctx);
ref = maybeWrapInOutExpr(ref, ctx);
YieldStmt *yield = YieldStmt::create(ctx, loc, loc, ref, loc, true);
body.push_back(yield);
auto Set = storage->getAccessor(AccessorKind::Set);
auto DidSet = storage->getAccessor(AccessorKind::DidSet);
auto *SelfDecl = accessor->getImplicitSelfDecl();
SubstitutionMap subs;
if (auto *genericEnv = Set->getGenericEnvironment())
subs = genericEnv->getForwardingSubstitutionMap();
auto callDidSet = [&]() {
ConcreteDeclRef ref(DidSet, subs);
auto type = DidSet->getInterfaceType().subst(subs);
Expr *Callee = new (ctx) DeclRefExpr(ref, DeclNameLoc(), /*imp*/ true);
Callee->setType(type);
if (SelfDecl) {
auto *SelfDRE = buildSelfReference(SelfDecl, SelfAccessorKind::Peer,
storage->isSetterMutating());
SelfDRE = maybeWrapInOutExpr(SelfDRE, ctx);
auto *DSCE = new (ctx) DotSyntaxCallExpr(Callee, SourceLoc(), SelfDRE);
if (auto funcType = type->getAs<FunctionType>())
type = funcType->getResult();
DSCE->setType(type);
DSCE->setThrows(false);
Callee = DSCE;
}
auto *Call = CallExpr::createImplicit(ctx, Callee, {}, {});
if (auto funcType = type->getAs<FunctionType>())
type = funcType->getResult();
Call->setType(type);
Call->setThrows(false);
body.push_back(Call);
};
callDidSet();
return {BraceStmt::create(ctx, loc, body, loc, true),
/*isTypeChecked=*/true};
}
static std::pair<BraceStmt *, bool>
synthesizeCoroutineAccessorBody(AccessorDecl *accessor, ASTContext &ctx) {
assert(accessor->isCoroutine());
auto storage = accessor->getStorage();
auto storageReadWriteImpl = storage->getReadWriteImpl();
auto target = (accessor->hasForcedStaticDispatch()
? TargetImpl::Ordinary
: TargetImpl::Implementation);
// If this is a variable with an attached property wrapper, then
// the accessors need to yield the wrappedValue or projectedValue.
if (accessor->getAccessorKind() == AccessorKind::Read ||
storageReadWriteImpl == ReadWriteImplKind::Modify) {
if (auto var = dyn_cast<VarDecl>(storage)) {
if (var->hasAttachedPropertyWrapper()) {
target = TargetImpl::Wrapper;
}
if (var->getOriginalWrappedProperty(
PropertyWrapperSynthesizedPropertyKind::Projection)) {
target = TargetImpl::WrapperStorage;
}
}
}
SourceLoc loc = storage->getLoc();
SmallVector<ASTNode, 1> body;
bool isModify = accessor->getAccessorKind() == AccessorKind::Modify;
// Special-case for a modify coroutine of a simple stored property with
// observers. We can yield a borrowed copy of the underlying storage
// in this case. However, if the accessor was synthesized on-demand,
// we do the more general thing, because on-demand accessors might be
// serialized, which prevents them from being able to directly reference
// didSet/willSet accessors, which are private.
if (isModify &&
!accessor->hasForcedStaticDispatch() &&
(storageReadWriteImpl == ReadWriteImplKind::StoredWithDidSet ||
storageReadWriteImpl == ReadWriteImplKind::InheritedWithDidSet) &&
storage->getParsedAccessor(AccessorKind::DidSet)->isSimpleDidSet()) {
return synthesizeModifyCoroutineBodyWithSimpleDidSet(accessor, ctx);
}
// Build a reference to the storage.
Expr *ref = buildStorageReference(accessor, storage, target,
/*isUsedForGetAccess=*/true,
/*isUsedForSetAccess=*/isModify,
ctx);
if (ref != nullptr) {
// Wrap it with an `&` marker if this is a modify.
ref = maybeWrapInOutExpr(ref, ctx);
// Yield it.
YieldStmt *yield = YieldStmt::create(ctx, loc, loc, ref, loc, true);
body.push_back(yield);
}
return { BraceStmt::create(ctx, loc, body, loc, true),
/*isTypeChecked=*/true };
}
/// Synthesize the body of a read coroutine.
static std::pair<BraceStmt *, bool>
synthesizeReadCoroutineBody(AccessorDecl *read, ASTContext &ctx) {
assert(read->getStorage()->getReadImpl() != ReadImplKind::Read);
return synthesizeCoroutineAccessorBody(read, ctx);
}
/// Synthesize the body of a modify coroutine.
static std::pair<BraceStmt *, bool>
synthesizeModifyCoroutineBody(AccessorDecl *modify, ASTContext &ctx) {
#ifndef NDEBUG
auto storage = modify->getStorage();
auto impl = storage->getReadWriteImpl();
auto hasWrapper = isa<VarDecl>(storage) &&
cast<VarDecl>(storage)->hasAttachedPropertyWrapper();
assert((hasWrapper || impl != ReadWriteImplKind::Modify) &&
impl != ReadWriteImplKind::Immutable);
#endif
return synthesizeCoroutineAccessorBody(modify, ctx);
}
static std::pair<BraceStmt *, bool>
synthesizeAccessorBody(AbstractFunctionDecl *fn, void *) {
auto *accessor = cast<AccessorDecl>(fn);
auto &ctx = accessor->getASTContext();
if (ctx.Stats)
++ctx.Stats->getFrontendCounters().NumAccessorBodiesSynthesized;
switch (accessor->getAccessorKind()) {
case AccessorKind::Get:
return synthesizeGetterBody(accessor, ctx);
case AccessorKind::Set:
return synthesizeSetterBody(accessor, ctx);
case AccessorKind::Read:
return synthesizeReadCoroutineBody(accessor, ctx);
case AccessorKind::Modify:
return synthesizeModifyCoroutineBody(accessor, ctx);
case AccessorKind::WillSet:
case AccessorKind::DidSet:
case AccessorKind::Address:
case AccessorKind::MutableAddress:
break;
}
llvm_unreachable("bad synthesized function kind");
}
static void finishImplicitAccessor(AccessorDecl *accessor,
ASTContext &ctx) {
accessor->setImplicit();
if (ctx.Stats)
++ctx.Stats->getFrontendCounters().NumAccessorsSynthesized;
if (doesAccessorHaveBody(accessor))
accessor->setBodySynthesizer(&synthesizeAccessorBody);
}
static AccessorDecl *createGetterPrototype(AbstractStorageDecl *storage,
ASTContext &ctx) {
SourceLoc loc = storage->getLoc();
ParamDecl *selfDecl = nullptr;
if (storage->getDeclContext()->isTypeContext()) {
if (storage->getAttrs().hasAttribute<LazyAttr>()) {
// For lazy properties, steal the 'self' from the initializer context.
auto *varDecl = cast<VarDecl>(storage);
auto *bindingDecl = varDecl->getParentPatternBinding();
const auto i = bindingDecl->getPatternEntryIndexForVarDecl(varDecl);
auto *bindingInit = cast<PatternBindingInitializer>(
bindingDecl->getInitContext(i));
selfDecl = bindingInit->getImplicitSelfDecl();
}
}
GenericParamList *genericParams = createAccessorGenericParams(storage);
// Add an index-forwarding clause.
auto *getterParams = buildIndexForwardingParamList(storage, {}, ctx);
SourceLoc staticLoc;
if (storage->isStatic())
staticLoc = storage->getLoc();
auto getter = AccessorDecl::create(
ctx, loc, /*AccessorKeywordLoc*/ loc,
AccessorKind::Get, storage,
staticLoc, StaticSpellingKind::None,
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
genericParams,
getterParams,
Type(),
storage->getDeclContext());
// If we're stealing the 'self' from a lazy initializer, set it now.
// Note that we don't re-parent the 'self' declaration to be part of
// the getter until we synthesize the body of the getter later.
if (selfDecl)
*getter->getImplicitSelfDeclStorage() = selfDecl;
if (storage->isGetterMutating())
getter->setSelfAccessKind(SelfAccessKind::Mutating);
else
getter->setSelfAccessKind(SelfAccessKind::NonMutating);
if (!storage->requiresOpaqueAccessor(AccessorKind::Get))
getter->setForcedStaticDispatch(true);
finishImplicitAccessor(getter, ctx);
return getter;
}
static AccessorDecl *createSetterPrototype(AbstractStorageDecl *storage,
ASTContext &ctx,
AccessorDecl *getter = nullptr) {
assert(storage->supportsMutation());
SourceLoc loc = storage->getLoc();
bool isMutating = storage->isSetterMutating();
GenericParamList *genericParams = createAccessorGenericParams(storage);
// Add a "(value : T, indices...)" argument list.
auto *param = new (ctx) ParamDecl(SourceLoc(), SourceLoc(),
Identifier(), loc,
ctx.getIdentifier("value"),
storage->getDeclContext());
param->setSpecifier(ParamSpecifier::Default);
param->setImplicit();
auto *params = buildIndexForwardingParamList(storage, param, ctx);
auto setter = AccessorDecl::create(
ctx, loc, /*AccessorKeywordLoc*/ SourceLoc(),
AccessorKind::Set, storage,
/*StaticLoc=*/SourceLoc(), StaticSpellingKind::None,
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
genericParams, params,
Type(),
storage->getDeclContext());
if (isMutating)
setter->setSelfAccessKind(SelfAccessKind::Mutating);
else
setter->setSelfAccessKind(SelfAccessKind::NonMutating);
// All mutable storage requires a setter.
assert(storage->requiresOpaqueAccessor(AccessorKind::Set));
// Copy availability from the accessor we'll synthesize the setter from.
SmallVector<Decl *, 2> asAvailableAs;
// That could be a property wrapper...
if (auto var = dyn_cast<VarDecl>(storage)) {
if (var->hasAttachedPropertyWrapper()) {
// The property wrapper info may not actually link back to a wrapper
// implementation, if there was a semantic error checking the wrapper.
auto info = var->getAttachedPropertyWrapperTypeInfo(0);
if (info.valueVar) {
if (auto setter = info.valueVar->getOpaqueAccessor(AccessorKind::Set)) {
asAvailableAs.push_back(setter);
}
}
} else if (auto wrapperSynthesizedKind
= var->getPropertyWrapperSynthesizedPropertyKind()) {
switch (*wrapperSynthesizedKind) {
case PropertyWrapperSynthesizedPropertyKind::Backing:
break;
case PropertyWrapperSynthesizedPropertyKind::Projection: {
if (auto origVar = var->getOriginalWrappedProperty(wrapperSynthesizedKind)) {
// The property wrapper info may not actually link back to a wrapper
// implementation, if there was a semantic error checking the wrapper.
auto info = origVar->getAttachedPropertyWrapperTypeInfo(0);
if (info.projectedValueVar) {
if (auto setter
= info.projectedValueVar->getOpaqueAccessor(AccessorKind::Set)){
asAvailableAs.push_back(setter);
}
}
}
break;
}
}
}
}
// ...or another accessor.
switch (storage->getWriteImpl()) {
case WriteImplKind::Immutable:
llvm_unreachable("synthesizing setter from immutable storage");
case WriteImplKind::Stored:
case WriteImplKind::StoredWithObservers:
case WriteImplKind::InheritedWithObservers:
case WriteImplKind::Set:
// Setter's availability shouldn't be externally influenced in these
// cases.
break;
case WriteImplKind::MutableAddress:
if (auto addr = storage->getOpaqueAccessor(AccessorKind::MutableAddress)) {
asAvailableAs.push_back(addr);
}
break;
case WriteImplKind::Modify:
if (auto mod = storage->getOpaqueAccessor(AccessorKind::Modify)) {
asAvailableAs.push_back(mod);
}
break;
}
if (!asAvailableAs.empty()) {
AvailabilityInference::applyInferredAvailableAttrs(
setter, asAvailableAs, ctx);
}
finishImplicitAccessor(setter, ctx);
return setter;
}
static AccessorDecl *
createCoroutineAccessorPrototype(AbstractStorageDecl *storage,
AccessorKind kind,
ASTContext &ctx) {
assert(kind == AccessorKind::Read || kind == AccessorKind::Modify);
SourceLoc loc = storage->getLoc();
bool isMutating = storage->isGetterMutating();
if (kind == AccessorKind::Modify)
isMutating |= storage->isSetterMutating();
auto dc = storage->getDeclContext();
// The forwarding index parameters.
auto *params = buildIndexForwardingParamList(storage, {}, ctx);
// Coroutine accessors always return ().
const Type retTy = TupleType::getEmpty(ctx);
GenericParamList *genericParams = createAccessorGenericParams(storage);
auto *accessor = AccessorDecl::create(
ctx, loc, /*AccessorKeywordLoc=*/SourceLoc(),
kind, storage,
/*StaticLoc=*/SourceLoc(), StaticSpellingKind::None,
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
genericParams, params, retTy, dc);
if (isMutating)
accessor->setSelfAccessKind(SelfAccessKind::Mutating);
else
accessor->setSelfAccessKind(SelfAccessKind::NonMutating);
// If the storage does not provide this accessor as an opaque accessor,
// we can't add a dynamically-dispatched method entry for the accessor,
// so force it to be statically dispatched. ("final" would be inappropriate
// because the property can still be overridden.)
if (!storage->requiresOpaqueAccessor(kind))
accessor->setForcedStaticDispatch(true);
// Make sure the coroutine 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->getParsedAccessor(AccessorKind::Get)) {
asAvailableAs.push_back(getter);
}
if (kind == AccessorKind::Modify) {
if (FuncDecl *setter = storage->getParsedAccessor(AccessorKind::Set)) {
asAvailableAs.push_back(setter);
}
}
AvailabilityInference::applyInferredAvailableAttrs(accessor,
asAvailableAs, ctx);
finishImplicitAccessor(accessor, ctx);
return accessor;
}
static AccessorDecl *
createReadCoroutinePrototype(AbstractStorageDecl *storage,
ASTContext &ctx) {
return createCoroutineAccessorPrototype(storage, AccessorKind::Read, ctx);
}
static AccessorDecl *
createModifyCoroutinePrototype(AbstractStorageDecl *storage,
ASTContext &ctx) {
return createCoroutineAccessorPrototype(storage, AccessorKind::Modify, ctx);
}
AccessorDecl *
SynthesizeAccessorRequest::evaluate(Evaluator &evaluator,
AbstractStorageDecl *storage,
AccessorKind kind) const {
auto &ctx = storage->getASTContext();
switch (kind) {
case AccessorKind::Get:
return createGetterPrototype(storage, ctx);
case AccessorKind::Set:
return createSetterPrototype(storage, ctx);
case AccessorKind::Read:
return createReadCoroutinePrototype(storage, ctx);
case AccessorKind::Modify:
return createModifyCoroutinePrototype(storage, ctx);
#define OPAQUE_ACCESSOR(ID, KEYWORD)
#define ACCESSOR(ID) \
case AccessorKind::ID:
#include "swift/AST/AccessorKinds.def"
llvm_unreachable("not an opaque accessor");
}
llvm_unreachable("Unhandled AccessorKind in switch");
}
bool
RequiresOpaqueAccessorsRequest::evaluate(Evaluator &evaluator,
VarDecl *var) const {
// Nameless vars from interface files should not have any accessors.
// TODO: Replace this check with a broader check that all storage decls
// from interface files have all their accessors up front.
if (var->getBaseName().empty())
return false;
// Computed properties always require opaque accessors.
if (!var->getImplInfo().isSimpleStored())
return true;
// The backing storage for a lazy property does require opaque accessors.
if (var->isLazyStorageProperty())
return false;
auto *dc = var->getDeclContext();
// Local stored variables don't require opaque accessors.
if (dc->isLocalContext()) {
return false;
} else if (dc->isModuleScopeContext()) {
// Fixed-layout global variables don't require opaque accessors.
if (!var->isResilient() && !var->shouldUseNativeDynamicDispatch())
return false;
// Stored properties imported from Clang don't require opaque accessors.
} else if (auto *structDecl = dyn_cast<StructDecl>(dc)) {
if (structDecl->hasClangNode())
return false;
}
// Stored properties in SIL mode don't get accessors.
// But we might need to create opaque accessors for them.
if (auto sourceFile = dc->getParentSourceFile()) {
if (sourceFile->Kind == SourceFileKind::SIL) {
if (!var->getParsedAccessor(AccessorKind::Get))
return false;
}
}
// Everything else requires opaque accessors.
return true;
}
bool
RequiresOpaqueModifyCoroutineRequest::evaluate(Evaluator &evaluator,
AbstractStorageDecl *storage) const {
// Only for mutable storage.
if (!storage->supportsMutation())
return false;
auto *dc = storage->getDeclContext();
// Local properties don't have an opaque modify coroutine.
if (dc->isLocalContext())
return false;
// Fixed-layout global properties don't have an opaque modify coroutine.
if (dc->isModuleScopeContext() && !storage->isResilient())
return false;
// Imported storage declarations don't have an opaque modify coroutine.
if (storage->hasClangNode())
return false;
// Dynamic storage does not have an opaque modify coroutine.
if (dc->getSelfClassDecl())
if (storage->shouldUseObjCDispatch())
return false;
// Requirements of ObjC protocols don't have an opaque modify coroutine.
if (auto protoDecl = dyn_cast<ProtocolDecl>(dc))
if (protoDecl->isObjC())
return false;
return true;
}
/// 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.
bool
IsAccessorTransparentRequest::evaluate(Evaluator &evaluator,
AccessorDecl *accessor) const {
auto *storage = accessor->getStorage();
if (storage->isTransparent())
return true;
if (accessor->getAttrs().hasAttribute<TransparentAttr>())
return true;
if (!accessor->isImplicit())
return false;
if (!doesAccessorHaveBody(accessor))
return false;
auto *DC = accessor->getDeclContext();
auto *nominalDecl = DC->getSelfNominalTypeDecl();
// Global variable accessors are not @_transparent.
if (!nominalDecl)
return false;
// Accessors for resilient properties are not @_transparent.
if (storage->isResilient())
return false;
// 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->checkAncestry(AncestryFlags::ObjC))
return false;
// Accessors synthesized on-demand are never transaprent.
if (accessor->hasForcedStaticDispatch())
return false;
if (accessor->getAccessorKind() == AccessorKind::Get ||
accessor->getAccessorKind() == AccessorKind::Set) {
// Getters and setters for lazy properties are not @_transparent.
if (storage->getAttrs().hasAttribute<LazyAttr>())
return false;
}
// Accessors for a property with a wrapper are not @_transparent if
// the backing variable has more-restrictive access than the original
// property. The same goes for its storage wrapper.
if (auto var = dyn_cast<VarDecl>(storage)) {
if (auto backingVar = var->getPropertyWrapperBackingProperty()) {
if (backingVar->getFormalAccess() < var->getFormalAccess())
return false;
}
if (auto original = var->getOriginalWrappedProperty(
PropertyWrapperSynthesizedPropertyKind::Projection)) {
auto backingVar = original->getPropertyWrapperBackingProperty();
if (backingVar->getFormalAccess() < var->getFormalAccess())
return false;
}
}
switch (accessor->getAccessorKind()) {
case AccessorKind::Get:
break;
case AccessorKind::Set:
switch (storage->getWriteImpl()) {
case WriteImplKind::Set:
// Setters for property wrapper are OK, unless there are observers.
// FIXME: This should be folded into the WriteImplKind below.
if (auto var = dyn_cast<VarDecl>(storage)) {
if (var->hasAttachedPropertyWrapper()) {
if (var->hasObservers())
return false;
break;
} else if (var->getOriginalWrappedProperty(
PropertyWrapperSynthesizedPropertyKind::Projection)) {
break;
}
}
// Anything else should not have a synthesized setter.
LLVM_FALLTHROUGH;
case WriteImplKind::Immutable:
if (accessor->getASTContext().LangOpts.AllowModuleWithCompilerErrors)
return false;
llvm_unreachable("should not be synthesizing accessor in this case");
case WriteImplKind::StoredWithObservers:
case WriteImplKind::InheritedWithObservers:
// Setters for observed properties are not @_transparent (because the
// observers are private) and cannot be referenced from a transparent
// method).
return false;
case WriteImplKind::Stored:
case WriteImplKind::MutableAddress:
case WriteImplKind::Modify:
break;
}
break;
case AccessorKind::Read:
case AccessorKind::Modify:
break;
case AccessorKind::WillSet:
case AccessorKind::DidSet:
case AccessorKind::Address:
case AccessorKind::MutableAddress:
llvm_unreachable("bad synthesized function kind");
}
switch (storage->getReadWriteImpl()) {
case ReadWriteImplKind::StoredWithDidSet:
case ReadWriteImplKind::InheritedWithDidSet:
if (storage->getAccessor(AccessorKind::DidSet)->isSimpleDidSet())
return false;
break;
default:
break;
}
return true;
}
VarDecl *
LazyStoragePropertyRequest::evaluate(Evaluator &evaluator,
VarDecl *VD) const {
assert(isa<SourceFile>(VD->getDeclContext()->getModuleScopeContext()));
assert(VD->getAttrs().hasAttribute<LazyAttr>());
auto &Context = VD->getASTContext();
// Create the storage property as an optional of VD's type.
SmallString<64> NameBuf;
NameBuf += "$__lazy_storage_$_";
NameBuf += VD->getName().str();
auto StorageName = Context.getIdentifier(NameBuf);
auto StorageInterfaceTy = OptionalType::get(VD->getInterfaceType());
auto StorageTy = OptionalType::get(VD->getType());
auto *Storage = new (Context) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Var,
VD->getLoc(), StorageName,
VD->getDeclContext());
Storage->setInterfaceType(StorageInterfaceTy);
Storage->setLazyStorageProperty(true);
Storage->setUserAccessible(false);
// The storage is implicit and private.
Storage->setImplicit();
Storage->overwriteAccess(AccessLevel::Private);
Storage->overwriteSetterAccess(AccessLevel::Private);
addMemberToContextIfNeeded(Storage, VD->getDeclContext(), VD);
// Create the pattern binding decl for the storage decl. This will get
// default initialized to nil.
Pattern *PBDPattern = NamedPattern::createImplicit(Context, Storage);
PBDPattern->setType(StorageTy);
PBDPattern = TypedPattern::createImplicit(Context, PBDPattern, StorageTy);
auto *InitExpr = new (Context) NilLiteralExpr(SourceLoc(), /*Implicit=*/true);
InitExpr->setType(Storage->getType());
auto *PBD = PatternBindingDecl::createImplicit(
Context, StaticSpellingKind::None, PBDPattern, InitExpr,
VD->getDeclContext(), /*VarLoc*/ VD->getLoc());
PBD->setInitializerChecked(0);
addMemberToContextIfNeeded(PBD, VD->getDeclContext(), Storage);
return Storage;
}
/// Synthesize a computed property `$foo` for a property with an attached
/// wrapper that has a `projectedValue` property.
static VarDecl *synthesizePropertyWrapperProjectionVar(
ASTContext &ctx, VarDecl *var, Type wrapperType,
VarDecl *wrapperVar) {
// If the original property has a @_projectedValueProperty attribute, use
// that to find the storage wrapper property.
if (auto attr = var->getAttrs().getAttribute<ProjectedValuePropertyAttr>()){
SmallVector<ValueDecl *, 2> declsFound;
DeclNameRef projectionName(attr->ProjectionPropertyName);
auto dc = var->getDeclContext();
if (dc->isTypeContext()) {
dc->lookupQualified(dc->getSelfNominalTypeDecl(), projectionName,
NL_QualifiedDefault, declsFound);
} else if (dc->isModuleScopeContext()) {
dc->lookupQualified(dc->getParentModule(), projectionName,
NL_QualifiedDefault, declsFound);
} else {
llvm_unreachable("Property wrappers don't work in local contexts");
}
if (declsFound.size() == 1 && isa<VarDecl>(declsFound.front())) {
auto property = cast<VarDecl>(declsFound.front());
property->setOriginalWrappedProperty(var);
return property;
}
ctx.Diags.diagnose(attr->getLocation(),
diag::property_wrapper_projection_value_missing,
projectionName);
attr->setInvalid();
}
// Compute the name of the storage type.
SmallString<64> nameBuf;
nameBuf = "$";
nameBuf += var->getName().str();
Identifier name = ctx.getIdentifier(nameBuf);
// Determine the type of the property.
Type propertyType = wrapperType->getTypeOfMember(
var->getModuleContext(), wrapperVar,
wrapperVar->getValueInterfaceType());
// Form the property.
auto dc = var->getDeclContext();
VarDecl *property = new (ctx) VarDecl(/*IsStatic=*/var->isStatic(),
VarDecl::Introducer::Var,
var->getLoc(),
name, dc);
property->setInterfaceType(propertyType);
property->setImplicit();
property->setOriginalWrappedProperty(var);
addMemberToContextIfNeeded(property, dc, var);
// Create the pattern binding declaration for the property.
Pattern *pbdPattern = NamedPattern::createImplicit(ctx, property);
pbdPattern->setType(propertyType);
pbdPattern = TypedPattern::createImplicit(ctx, pbdPattern, propertyType);
auto pbd = PatternBindingDecl::createImplicit(
ctx, property->getCorrectStaticSpelling(), pbdPattern,
/*init*/nullptr, dc, SourceLoc());
addMemberToContextIfNeeded(pbd, dc, var);
pbd->setStatic(var->isStatic());
// Determine the access level for the property.
property->overwriteAccess(var->getFormalAccess());
// Determine setter access.
property->overwriteSetterAccess(var->getSetterFormalAccess());
// Add the accessors we need.
bool hasSetter = wrapperVar->isSettable(nullptr) &&
wrapperVar->isSetterAccessibleFrom(var->getInnermostDeclContext());
if (hasSetter)
property->setImplInfo(StorageImplInfo::getMutableComputed());
else
property->setImplInfo(StorageImplInfo::getImmutableComputed());
var->getAttrs().add(
new (ctx) ProjectedValuePropertyAttr(name, SourceLoc(), SourceRange(),
/*Implicit=*/true));
return property;
}
static void typeCheckSynthesizedWrapperInitializer(
PatternBindingDecl *pbd, VarDecl *backingVar, PatternBindingDecl *parentPBD,
Expr *&initializer) {
// Figure out the context in which the initializer was written.
DeclContext *originalDC = parentPBD->getDeclContext();
if (!originalDC->isLocalContext()) {
auto initContext =
cast_or_null<PatternBindingInitializer>(parentPBD->getInitContext(0));
if (initContext)
originalDC = initContext;
}
// Type-check the initialization.
{
auto *wrappedVar = backingVar->getOriginalWrappedProperty();
auto i = parentPBD->getPatternEntryIndexForVarDecl(wrappedVar);
auto *pattern = parentPBD->getPattern(i);
TypeChecker::typeCheckBinding(pattern, initializer, originalDC,
wrappedVar->getType(), parentPBD, i);
}
const auto i = pbd->getPatternEntryIndexForVarDecl(backingVar);
if (auto initializerContext =
dyn_cast_or_null<Initializer>(pbd->getInitContext(i))) {
TypeChecker::contextualizeInitializer(initializerContext, initializer);
}
checkPropertyWrapperActorIsolation(pbd, initializer);
TypeChecker::checkPropertyWrapperEffects(pbd, initializer);
}
static PropertyWrapperMutability::Value
getGetterMutatingness(VarDecl *var) {
return var->isGetterMutating()
? PropertyWrapperMutability::Mutating
: PropertyWrapperMutability::Nonmutating;
}
static PropertyWrapperMutability::Value
getSetterMutatingness(VarDecl *var, DeclContext *dc) {
if (!var->isSettable(nullptr) ||
!var->isSetterAccessibleFrom(dc))
return PropertyWrapperMutability::DoesntExist;
return var->isSetterMutating()
? PropertyWrapperMutability::Mutating
: PropertyWrapperMutability::Nonmutating;
}
Optional<PropertyWrapperMutability>
PropertyWrapperMutabilityRequest::evaluate(Evaluator &,
VarDecl *var) const {
VarDecl *originalVar = var;
unsigned numWrappers = originalVar->getAttachedPropertyWrappers().size();
bool isProjectedValue = false;
if (numWrappers < 1) {
originalVar = var->getOriginalWrappedProperty(
PropertyWrapperSynthesizedPropertyKind::Projection);
if (!originalVar)
return None;
numWrappers = originalVar->getAttachedPropertyWrappers().size();
isProjectedValue = true;
}
// Make sure we don't ignore .swiftinterface files, because those will
// have the accessors printed
auto varSourceFile = var->getDeclContext()->getParentSourceFile();
auto isVarNotInInterfaceFile =
varSourceFile && varSourceFile->Kind != SourceFileKind::Interface;
if (var->getParsedAccessor(AccessorKind::Get) && isVarNotInInterfaceFile)
return None;
if (var->getParsedAccessor(AccessorKind::Set) && isVarNotInInterfaceFile)
return None;
// Figure out which member we're looking through.
auto varMember = isProjectedValue
? &PropertyWrapperTypeInfo::projectedValueVar
: &PropertyWrapperTypeInfo::valueVar;
// Start with the traits from the outermost wrapper.
auto firstWrapper = originalVar->getAttachedPropertyWrapperTypeInfo(0);
if (firstWrapper.*varMember == nullptr)
return None;
PropertyWrapperMutability result;
result.Getter = getGetterMutatingness(firstWrapper.*varMember);
result.Setter = getSetterMutatingness(firstWrapper.*varMember,
var->getInnermostDeclContext());
auto getCustomAttrTypeLoc = [](const CustomAttr *CA) -> TypeLoc {
return { CA->getTypeRepr(), CA->getType() };
};
// Compose the traits of the following wrappers.
for (unsigned i = 1; i < numWrappers && !isProjectedValue; ++i) {
assert(var == originalVar);
auto wrapper = var->getAttachedPropertyWrapperTypeInfo(i);
if (!wrapper.valueVar)
return None;
PropertyWrapperMutability nextResult;
nextResult.Getter =
result.composeWith(getGetterMutatingness(wrapper.valueVar));
// A property must have a getter, so we can't compose a wrapper that
// exposes a mutating getter wrapped inside a get-only wrapper.
if (nextResult.Getter == PropertyWrapperMutability::DoesntExist) {
auto &ctx = var->getASTContext();
ctx.Diags.diagnose(var->getAttachedPropertyWrappers()[i]->getLocation(),
diag::property_wrapper_mutating_get_composed_to_get_only,
getCustomAttrTypeLoc(var->getAttachedPropertyWrappers()[i]),
getCustomAttrTypeLoc(var->getAttachedPropertyWrappers()[i-1]));
return None;
}
nextResult.Setter =
result.composeWith(getSetterMutatingness(wrapper.valueVar,
var->getInnermostDeclContext()));
result = nextResult;
}
assert(result.Getter != PropertyWrapperMutability::DoesntExist
&& "getter must exist");
return result;
}
Optional<PropertyWrapperLValueness>
PropertyWrapperLValuenessRequest::evaluate(Evaluator &,
VarDecl *var) const {
VarDecl *VD = var;
unsigned numWrappers = var->getAttachedPropertyWrappers().size();
bool isProjectedValue = false;
if (numWrappers < 1) {
VD = var->getOriginalWrappedProperty(
PropertyWrapperSynthesizedPropertyKind::Projection);
numWrappers = 1; // Can't compose projected values
isProjectedValue = true;
}
if (!VD)
return None;
auto varMember = isProjectedValue
? &PropertyWrapperTypeInfo::projectedValueVar
: &PropertyWrapperTypeInfo::valueVar;
auto accessorMutability = [&](unsigned wrapperIndex) -> PropertyWrapperMutability {
PropertyWrapperMutability mutability;
auto wrapperInfo = VD->getAttachedPropertyWrapperTypeInfo(wrapperIndex);
mutability.Getter = getGetterMutatingness(wrapperInfo.*varMember);
mutability.Setter = getSetterMutatingness(wrapperInfo.*varMember,
var->getInnermostDeclContext());
return mutability;
};
// Calling the getter (or setter) on the nth property wrapper in the chain
// is done as follows:
// 1. call the getter on the (n-1)th property wrapper instance to get the
// nth property wrapper instance
// 2. call the getter (or setter) on the nth property wrapper instance
// 3. if (2) is a mutating access, call the setter on the (n-1)th property
// wrapper instance to write back the mutated value
// Below, we determine which of these property wrapper instances need to be
// accessed mutating-ly, and therefore should be l-values.
unsigned innermostWrapperIdx = numWrappers - 1;
auto lastAccess = accessorMutability(innermostWrapperIdx);
PropertyWrapperLValueness lvalueness(numWrappers);
lvalueness.isLValueForGetAccess[innermostWrapperIdx] =
lastAccess.Getter == PropertyWrapperMutability::Mutating;
lvalueness.isLValueForSetAccess[innermostWrapperIdx] =
lastAccess.Setter == PropertyWrapperMutability::Mutating;
auto lastAccessForGet = lastAccess.Getter;
auto lastAccessForSet = lastAccess.Setter;
for (int i = innermostWrapperIdx - 1; i >= 0; --i) {
auto access = accessorMutability(i);
lastAccessForGet = access.composeWith(lastAccessForGet);
lastAccessForSet = access.composeWith(lastAccessForSet);
lvalueness.isLValueForGetAccess[i] =
lastAccessForGet == PropertyWrapperMutability::Mutating;
lvalueness.isLValueForSetAccess[i] =
lastAccessForSet == PropertyWrapperMutability::Mutating;
}
return lvalueness;
}
PropertyWrapperBackingPropertyInfo
PropertyWrapperBackingPropertyInfoRequest::evaluate(Evaluator &evaluator,
VarDecl *var) const {
// Determine the type of the backing property.
auto wrapperType = var->getPropertyWrapperBackingPropertyType();
if (!wrapperType || wrapperType->hasError())
return PropertyWrapperBackingPropertyInfo();
auto wrapperInfo = var->getAttachedPropertyWrapperTypeInfo(0);
if (!wrapperInfo)
return PropertyWrapperBackingPropertyInfo();
// Compute the name of the storage type.
ASTContext &ctx = var->getASTContext();
SmallString<64> nameBuf;
nameBuf = "_";
nameBuf += var->getName().str();
Identifier name = ctx.getIdentifier(nameBuf);
// Determine the type of the storage.
auto dc = var->getDeclContext();
Type storageInterfaceType = wrapperType;
Type storageType = dc->mapTypeIntoContext(storageInterfaceType);
// Create the backing storage property and note it in the cache.
VarDecl *backingVar = new (ctx) VarDecl(/*IsStatic=*/var->isStatic(),
VarDecl::Introducer::Var,
var->getLoc(),
name, dc);
backingVar->setInterfaceType(storageInterfaceType);
backingVar->setImplicit();
backingVar->setOriginalWrappedProperty(var);
// The backing storage is 'private'.
backingVar->overwriteAccess(AccessLevel::Private);
backingVar->overwriteSetterAccess(AccessLevel::Private);
addMemberToContextIfNeeded(backingVar, dc, var);
// Create the pattern binding declaration for the backing property.
Pattern *pbdPattern = NamedPattern::createImplicit(ctx, backingVar);
pbdPattern->setType(storageType);
pbdPattern = TypedPattern::createImplicit(ctx, pbdPattern, storageType);
auto pbd = PatternBindingDecl::createImplicit(
ctx, var->getCorrectStaticSpelling(), pbdPattern,
/*init*/ nullptr, dc, SourceLoc());
addMemberToContextIfNeeded(pbd, dc, var);
pbd->setStatic(var->isStatic());
// Take the initializer from the original property.
auto parentPBD = var->getParentPatternBinding();
unsigned patternNumber = parentPBD->getPatternEntryIndexForVarDecl(var);
// Force the default initializer to come into existence, if there is one,
// and the wrapper doesn't provide its own.
if (!parentPBD->isInitialized(patternNumber)
&& parentPBD->isDefaultInitializable(patternNumber)
&& !wrapperInfo.defaultInit) {
auto ty = parentPBD->getPattern(patternNumber)->getType();
if (auto defaultInit = TypeChecker::buildDefaultInitializer(ty))
parentPBD->setInit(patternNumber, defaultInit);
}
if (parentPBD->isInitialized(patternNumber) &&
!parentPBD->isInitializerChecked(patternNumber)) {
TypeChecker::typeCheckPatternBinding(parentPBD, patternNumber);
}
Expr *initializer = nullptr;
PropertyWrapperValuePlaceholderExpr *wrappedValue = nullptr;
if ((initializer = parentPBD->getInit(patternNumber))) {
pbd->setInit(0, initializer);
pbd->setInitializerChecked(0);
wrappedValue = findWrappedValuePlaceholder(initializer);
} else {
if (!parentPBD->isInitialized(patternNumber) && wrapperInfo.defaultInit) {
// FIXME: Record this expression somewhere so that DI can perform the
// initialization itself.
Expr *initializer = nullptr;
typeCheckSynthesizedWrapperInitializer(pbd, backingVar, parentPBD,
initializer);
pbd->setInit(0, initializer);
pbd->setInitializerChecked(0);
} else if (var->hasObservers() && !dc->isTypeContext()) {
var->diagnose(diag::observingprop_requires_initializer);
}
if (var->getOpaqueResultTypeDecl()) {
var->diagnose(diag::opaque_type_var_no_underlying_type);
}
}
// If there is a projection property (projectedValue) in the wrapper,
// synthesize a computed property for '$foo'.
VarDecl *storageVar = nullptr;
if (wrapperInfo.projectedValueVar) {
storageVar = synthesizePropertyWrapperProjectionVar(
ctx, var, storageInterfaceType, wrapperInfo.projectedValueVar);
}
// If no initial wrapped value was provided via '=' and either:
// 1. Not all of the attached property wrappers have init(wrappedValue:), or
// 2. An initializer has already been synthesized from arguments in the
// property wrapper attribute,
// then this property wrapper cannot be initialized out-of-line with a wrapped
// value.
if (!wrappedValue && (!var->allAttachedPropertyWrappersHaveWrappedValueInit() ||
initializer)) {
return PropertyWrapperBackingPropertyInfo(
backingVar, storageVar, nullptr, nullptr);
}
// Form the initialization of the backing property from a value of the
// original property's type.
if (!initializer) {
initializer = PropertyWrapperValuePlaceholderExpr::create(
ctx, var->getSourceRange(), var->getType(), /*wrappedValue=*/nullptr);
typeCheckSynthesizedWrapperInitializer(
pbd, backingVar, parentPBD, initializer);
wrappedValue = findWrappedValuePlaceholder(initializer);
}
return PropertyWrapperBackingPropertyInfo(backingVar, storageVar,
initializer, wrappedValue);
}
/// Given a storage declaration in a protocol, set it up with the right
/// StorageImpl and add the right set of opaque accessors.
static void finishProtocolStorageImplInfo(AbstractStorageDecl *storage,
StorageImplInfo &info) {
if (auto *var = dyn_cast<VarDecl>(storage)) {
SourceLoc typeLoc;
if (auto *repr = var->getTypeReprOrParentPatternTypeRepr())
typeLoc = repr->getLoc();
if (info.hasStorage()) {
// Protocols cannot have stored properties.
if (var->isLet()) {
var->diagnose(diag::protocol_property_must_be_computed_var)
.fixItReplace(var->getParentPatternBinding()->getLoc(), "var")
.fixItInsertAfter(typeLoc, " { get }");
} else {
auto diag = var->diagnose(diag::protocol_property_must_be_computed);
auto braces = var->getBracesRange();
if (braces.isValid())
diag.fixItReplace(braces, "{ get <#set#> }");
else
diag.fixItInsertAfter(typeLoc, " { get <#set#> }");
}
}
}
auto protocol = cast<ProtocolDecl>(storage->getDeclContext());
if (protocol->isObjC()) {
info = StorageImplInfo::getComputed(info.supportsMutation());
} else {
info = StorageImplInfo::getOpaque(info.supportsMutation(),
storage->getOpaqueReadOwnership());
}
}
/// This emits a diagnostic with a fixit to remove the attribute.
template<typename ...ArgTypes>
void diagnoseAndRemoveAttr(Decl *D, DeclAttribute *attr,
ArgTypes &&...Args) {
auto &ctx = D->getASTContext();
ctx.Diags.diagnose(attr->getLocation(), std::forward<ArgTypes>(Args)...)
.fixItRemove(attr->getRangeWithAt());
}
static void finishLazyVariableImplInfo(VarDecl *var,
StorageImplInfo &info) {
auto *attr = var->getAttrs().getAttribute<LazyAttr>();
// It cannot currently be used on let's since we don't have a mutability model
// that supports it.
if (var->isLet())
diagnoseAndRemoveAttr(var, attr, diag::lazy_not_on_let);
// lazy must have an initializer.
if (!var->getParentInitializer())
diagnoseAndRemoveAttr(var, attr, diag::lazy_requires_initializer);
bool invalid = false;
if (isa<ProtocolDecl>(var->getDeclContext())) {
diagnoseAndRemoveAttr(var, attr, diag::lazy_not_in_protocol);
invalid = true;
}
// Lazy properties must be written as stored properties in the source.
if (info.getReadImpl() != ReadImplKind::Stored &&
(info.getWriteImpl() != WriteImplKind::Stored &&
info.getWriteImpl() != WriteImplKind::StoredWithObservers)) {
diagnoseAndRemoveAttr(var, attr, diag::lazy_not_on_computed);
invalid = true;
}
// The pattern binding must only bind a single variable.
if (!var->getParentPatternBinding()->getSingleVar())
diagnoseAndRemoveAttr(var, attr, diag::lazy_requires_single_var);
if (!invalid)
info = StorageImplInfo::getMutableComputed();
}
static void finishPropertyWrapperImplInfo(VarDecl *var,
StorageImplInfo &info) {
auto parentSF = var->getDeclContext()->getParentSourceFile();
if (!parentSF)
return;
// Properties with wrappers must not declare a getter or setter.
if (!info.hasStorage() && parentSF->Kind != SourceFileKind::Interface) {
auto &ctx = parentSF->getASTContext();
for (auto attr : var->getAttrs().getAttributes<CustomAttr>())
ctx.Diags.diagnose(attr->getLocation(), diag::property_wrapper_computed);
return;
}
bool wrapperSetterIsUsable = false;
if (var->getParsedAccessor(AccessorKind::Set)) {
wrapperSetterIsUsable = true;
} else if (parentSF && parentSF->Kind != SourceFileKind::Interface
&& !var->isLet()) {
if (auto comp = var->getPropertyWrapperMutability()) {
wrapperSetterIsUsable =
comp->Setter != PropertyWrapperMutability::DoesntExist;
} else {
wrapperSetterIsUsable = true;
}
}
if (!wrapperSetterIsUsable) {
info = StorageImplInfo::getImmutableComputed();
return;
}
if (var->hasObservers()) {
info = StorageImplInfo::getMutableComputed();
} else {
info = StorageImplInfo(ReadImplKind::Get, WriteImplKind::Set,
ReadWriteImplKind::Modify);
}
}
static void finishNSManagedImplInfo(VarDecl *var,
StorageImplInfo &info) {
auto *attr = var->getAttrs().getAttribute<NSManagedAttr>();
if (var->isLet())
diagnoseAndRemoveAttr(var, attr, diag::attr_NSManaged_let_property);
SourceFile *parentFile = var->getDeclContext()->getParentSourceFile();
auto diagnoseNotStored = [&](unsigned kind) {
// Skip diagnosing @NSManaged declarations in module interfaces. They are
// properties that are stored, but have specially synthesized observers
// and we should allow them to have getters and setters in a module
// interface.
if (parentFile && parentFile->Kind == SourceFileKind::Interface)
return;
diagnoseAndRemoveAttr(var, attr, diag::attr_NSManaged_not_stored, kind);
};
// @NSManaged properties must be written as stored.
if (info.isSimpleStored()) {
// @NSManaged properties end up being computed; complain if there is
// an initializer.
if (var->getParentInitializer()) {
auto &Diags = var->getASTContext().Diags;
Diags.diagnose(attr->getLocation(), diag::attr_NSManaged_initial_value)
.highlight(var->getParentInitializer()->getSourceRange());
}
// Otherwise, ok.
info = StorageImplInfo::getMutableComputed();
} else if (info.getReadImpl() == ReadImplKind::Address ||
info.getWriteImpl() == WriteImplKind::MutableAddress) {
diagnoseNotStored(/*addressed*/ 2);
} else if (info.getWriteImpl() == WriteImplKind::StoredWithObservers ||
info.getWriteImpl() == WriteImplKind::InheritedWithObservers) {
diagnoseNotStored(/*observing*/ 1);
} else {
diagnoseNotStored(/*computed*/ 0);
}
}
static void finishStorageImplInfo(AbstractStorageDecl *storage,
StorageImplInfo &info) {
auto dc = storage->getDeclContext();
if (auto var = dyn_cast<VarDecl>(storage)) {
if (!info.hasStorage()) {
if (auto *init = var->getParentInitializer()) {
auto &Diags = var->getASTContext().Diags;
Diags.diagnose(init->getLoc(), diag::getset_init)
.highlight(init->getSourceRange());
}
}
if (var->getAttrs().hasAttribute<LazyAttr>()) {
finishLazyVariableImplInfo(var, info);
} else if (var->getAttrs().hasAttribute<NSManagedAttr>()) {
finishNSManagedImplInfo(var, info);
} else if (var->hasAttachedPropertyWrapper()) {
finishPropertyWrapperImplInfo(var, info);
}
}
if (isa<ProtocolDecl>(dc))
finishProtocolStorageImplInfo(storage, info);
// If we have a stored property in an unsupported context, diagnose
// and change it to computed to avoid confusing SILGen.
// Note: Stored properties in protocols are diagnosed in
// finishProtocolStorageImplInfo().
if (info.hasStorage() && !storage->isStatic()) {
if (isa<EnumDecl>(dc)) {
storage->diagnose(diag::enum_stored_property);
info = StorageImplInfo::getMutableComputed();
} else if (isa<ExtensionDecl>(dc) &&
!storage->getAttrs().getAttribute<DynamicReplacementAttr>()) {
storage->diagnose(diag::extension_stored_property);
info = (info.supportsMutation()
? StorageImplInfo::getMutableComputed()
: StorageImplInfo::getImmutableComputed());
}
}
}
/// Gets the storage info of the provided storage decl if it has the
/// @_hasStorage attribute and it's not in SIL mode.
///
/// In this case, we say the decl is:
///
/// Read:
/// - Stored, always
/// Write:
/// - Stored, if the decl is a 'var'.
/// - StoredWithObservers, if the decl has a setter
/// - This indicates that the original decl had a 'didSet' and/or 'willSet'
/// - InheritedWithObservers, if the decl has a setter and is an overridde.
/// - Immutable, if the decl is a 'let' or it does not have a setter.
/// ReadWrite:
/// - Stored, if the decl has no accessors listed.
/// - Immutable, if the decl is a 'let' or it does not have a setter.
/// - MaterializeToTemporary, if the decl has a setter.
static StorageImplInfo classifyWithHasStorageAttr(VarDecl *var) {
WriteImplKind writeImpl;
ReadWriteImplKind readWriteImpl;
if (var->getParsedAccessor(AccessorKind::Get) &&
var->getParsedAccessor(AccessorKind::Set)) {
// If we see `@_hasStorage var x: T { get set }`, then our property has
// willSet/didSet observers.
writeImpl = var->getAttrs().hasAttribute<OverrideAttr>() ?
WriteImplKind::InheritedWithObservers :
WriteImplKind::StoredWithObservers;
readWriteImpl = ReadWriteImplKind::MaterializeToTemporary;
} else if (var->isLet()) {
writeImpl = WriteImplKind::Immutable;
readWriteImpl = ReadWriteImplKind::Immutable;
} else {
// Default to stored writes.
writeImpl = WriteImplKind::Stored;
readWriteImpl = ReadWriteImplKind::Stored;
}
// Always force Stored reads if @_hasStorage is present.
return StorageImplInfo(ReadImplKind::Stored, writeImpl, readWriteImpl);
}
StorageImplInfo
StorageImplInfoRequest::evaluate(Evaluator &evaluator,
AbstractStorageDecl *storage) const {
if (auto *param = dyn_cast<ParamDecl>(storage)) {
return StorageImplInfo::getSimpleStored(
param->isInOut()
? StorageIsMutable
: StorageIsNotMutable);
}
if (auto *var = dyn_cast<VarDecl>(storage)) {
// Allow the @_hasStorage attribute to override all the accessors we parsed
// when making the final classification.
if (var->getAttrs().hasAttribute<HasStorageAttr>()) {
// The SIL rules for @_hasStorage are slightly different from the non-SIL
// rules. In SIL mode, @_hasStorage marks that the type is simply stored,
// and the only thing that determines mutability is the existence of the
// setter.
//
// FIXME: SIL should not be special cased here. The behavior should be
// consistent between SIL and non-SIL.
// The strategy here should be to keep track of all opaque accessors
// along with enough information to access the storage trivially
// if allowed. This could be a representational change to
// StorageImplInfo such that it keeps a bitset of listed accessors
// and dynamically determines the access strategy from that.
auto *SF = storage->getDeclContext()->getParentSourceFile();
if (SF && SF->Kind == SourceFileKind::SIL)
return StorageImplInfo::getSimpleStored(
var->getParsedAccessor(AccessorKind::Set)
? StorageIsMutable
: StorageIsNotMutable);
return classifyWithHasStorageAttr(var);
}
}
bool hasWillSet = storage->getParsedAccessor(AccessorKind::WillSet);
bool hasDidSet = storage->getParsedAccessor(AccessorKind::DidSet);
bool hasSetter = storage->getParsedAccessor(AccessorKind::Set);
bool hasModify = storage->getParsedAccessor(AccessorKind::Modify);
bool hasMutableAddress = storage->getParsedAccessor(AccessorKind::MutableAddress);
// 'get', 'read', and a non-mutable addressor are all exclusive.
ReadImplKind readImpl;
if (storage->getParsedAccessor(AccessorKind::Get)) {
readImpl = ReadImplKind::Get;
} else if (storage->getParsedAccessor(AccessorKind::Read)) {
readImpl = ReadImplKind::Read;
} else if (storage->getParsedAccessor(AccessorKind::Address)) {
readImpl = ReadImplKind::Address;
// If there's a writing accessor of any sort, there must also be a
// reading accessor.
} else if (hasSetter || hasModify || hasMutableAddress) {
readImpl = ReadImplKind::Get;
// Subscripts always have to have some sort of accessor; they can't be
// purely stored.
} else if (isa<SubscriptDecl>(storage)) {
readImpl = ReadImplKind::Get;
// Check if we have observers.
} else if (hasWillSet || hasDidSet) {
if (storage->getAttrs().hasAttribute<OverrideAttr>() &&
storage->getDeclContext()->isTypeContext()) {
readImpl = ReadImplKind::Inherited;
} else {
readImpl = ReadImplKind::Stored;
}
// Extensions can't have stored properties. If there are braces, assume
// this is an incomplete computed property. This avoids an "extensions
// must not contain stored properties" error later on.
} else if (isa<ExtensionDecl>(storage->getDeclContext()) &&
storage->getBracesRange().isValid()) {
readImpl = ReadImplKind::Get;
// Otherwise, it's stored.
} else {
readImpl = ReadImplKind::Stored;
}
// Prefer using 'set' and 'modify' over a mutable addressor.
WriteImplKind writeImpl;
ReadWriteImplKind readWriteImpl;
if (hasSetter) {
writeImpl = WriteImplKind::Set;
if (hasModify) {
readWriteImpl = ReadWriteImplKind::Modify;
} else {
readWriteImpl = ReadWriteImplKind::MaterializeToTemporary;
}
} else if (hasModify) {
writeImpl = WriteImplKind::Modify;
readWriteImpl = ReadWriteImplKind::Modify;
} else if (hasMutableAddress) {
writeImpl = WriteImplKind::MutableAddress;
readWriteImpl = ReadWriteImplKind::MutableAddress;
// Check if we have observers.
} else if (readImpl == ReadImplKind::Inherited) {
writeImpl = WriteImplKind::InheritedWithObservers;
if (hasWillSet)
readWriteImpl = ReadWriteImplKind::MaterializeToTemporary;
else
readWriteImpl = ReadWriteImplKind::InheritedWithDidSet;
// Otherwise, it's stored.
} else if (readImpl == ReadImplKind::Stored &&
!cast<VarDecl>(storage)->isLet()) {
if (hasWillSet || hasDidSet) {
writeImpl = WriteImplKind::StoredWithObservers;
if (hasWillSet)
readWriteImpl = ReadWriteImplKind::MaterializeToTemporary;
else
readWriteImpl = ReadWriteImplKind::StoredWithDidSet;
} else {
writeImpl = WriteImplKind::Stored;
readWriteImpl = ReadWriteImplKind::Stored;
}
// Otherwise, it's immutable.
} else {
writeImpl = WriteImplKind::Immutable;
readWriteImpl = ReadWriteImplKind::Immutable;
}
StorageImplInfo info(readImpl, writeImpl, readWriteImpl);
finishStorageImplInfo(storage, info);
return info;
}
bool SimpleDidSetRequest::evaluate(Evaluator &evaluator,
AccessorDecl *decl) const {
class OldValueFinder : public ASTWalker {
const ParamDecl *OldValueParam;
bool foundOldValueRef = false;
public:
OldValueFinder(const ParamDecl *param) : OldValueParam(param) {}
virtual std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
if (!E)
return {true, E};
if (auto DRE = dyn_cast<DeclRefExpr>(E)) {
if (auto decl = DRE->getDecl()) {
if (decl == OldValueParam) {
foundOldValueRef = true;
return {false, nullptr};
}
}
}
return {true, E};
}
bool didFindOldValueRef() { return foundOldValueRef; }
};
// If this is not a didSet accessor, bail out.
if (decl->getAccessorKind() != AccessorKind::DidSet) {
return false;
}
// Always assume non-simple 'didSet' in code completion mode.
if (decl->getASTContext().SourceMgr.hasCodeCompletionBuffer())
return false;
// didSet must have a single parameter.
if (decl->getParameters()->size() != 1) {
return false;
}
auto param = decl->getParameters()->get(0);
// If this parameter is not implicit, then it means it has been explicitly
// provided by the user (i.e. 'didSet(oldValue)'). This means we cannot
// consider this a "simple" didSet because we have to fetch the oldValue
// regardless of whether it's referenced in the body or not.
if (!param->isImplicit()) {
return false;
}
// If we find a reference to the implicit 'oldValue' parameter, then it is
// not a "simple" didSet because we need to fetch it.
auto walker = OldValueFinder(param);
decl->getTypecheckedBody()->walk(walker);
auto hasOldValueRef = walker.didFindOldValueRef();
if (!hasOldValueRef) {
// If the body does not refer to implicit 'oldValue', it means we can
// consider this as a "simple" didSet. Let's also erase the implicit
// oldValue as it is never used.
auto &ctx = decl->getASTContext();
decl->setParameters(ParameterList::createEmpty(ctx));
return true;
}
return false;
}