blob: 072e9ae1122ebf462be060bf23914a5965daac49 [file] [log] [blame]
//===--- TypeCheckAttr.cpp - Type Checking for Attributes -----------------===//
//
// 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 attributes.
//
//===----------------------------------------------------------------------===//
#include "TypeChecker.h"
#include "MiscDiagnostics.h"
#include "swift/AST/GenericSignatureBuilder.h"
#include "swift/AST/ASTVisitor.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/Types.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/Parse/Lexer.h"
#include "llvm/Support/Debug.h"
using namespace swift;
namespace {
/// This emits a diagnostic with a fixit to remove the attribute.
template<typename ...ArgTypes>
void diagnoseAndRemoveAttr(TypeChecker &TC, Decl *D, DeclAttribute *attr,
ArgTypes &&...Args) {
assert(!D->hasClangNode() && "Clang imported propagated a bogus attribute");
if (!D->hasClangNode()) {
SourceLoc loc = attr->getLocation();
assert(loc.isValid() && "Diagnosing attribute with invalid location");
if (loc.isInvalid()) {
loc = D->getLoc();
}
if (loc.isValid()) {
TC.diagnose(loc, std::forward<ArgTypes>(Args)...)
.fixItRemove(attr->getRangeWithAt());
}
}
attr->setInvalid();
}
/// This visits each attribute on a decl early, before the majority of type
/// checking has been performed for the decl. The visitor should return true if
/// the attribute is invalid and should be marked as such.
class AttributeEarlyChecker : public AttributeVisitor<AttributeEarlyChecker> {
TypeChecker &TC;
Decl *D;
public:
AttributeEarlyChecker(TypeChecker &TC, Decl *D) : TC(TC), D(D) {}
/// This emits a diagnostic with a fixit to remove the attribute.
template<typename ...ArgTypes>
void diagnoseAndRemoveAttr(DeclAttribute *attr, ArgTypes &&...Args) {
::diagnoseAndRemoveAttr(TC, D, attr, std::forward<ArgTypes>(Args)...);
}
/// Deleting this ensures that all attributes are covered by the visitor
/// below.
bool visitDeclAttribute(DeclAttribute *A) = delete;
#define IGNORED_ATTR(X) void visit##X##Attr(X##Attr *) {}
IGNORED_ATTR(CDecl)
IGNORED_ATTR(SILGenName)
IGNORED_ATTR(Available)
IGNORED_ATTR(Convenience)
IGNORED_ATTR(Effects)
IGNORED_ATTR(Exported)
IGNORED_ATTR(FixedLayout)
IGNORED_ATTR(Infix)
IGNORED_ATTR(Inline)
IGNORED_ATTR(Inlineable)
IGNORED_ATTR(NSApplicationMain)
IGNORED_ATTR(NSCopying)
IGNORED_ATTR(NonObjC)
IGNORED_ATTR(ObjC)
IGNORED_ATTR(ObjCBridged)
IGNORED_ATTR(ObjCNonLazyRealization)
IGNORED_ATTR(ObjCRuntimeName)
IGNORED_ATTR(RestatedObjCConformance)
IGNORED_ATTR(Optional)
IGNORED_ATTR(Postfix)
IGNORED_ATTR(Prefix)
IGNORED_ATTR(RawDocComment)
IGNORED_ATTR(Required)
IGNORED_ATTR(RequiresStoredPropertyInits)
IGNORED_ATTR(Rethrows)
IGNORED_ATTR(Semantics)
IGNORED_ATTR(Specialize)
IGNORED_ATTR(SwiftNativeObjCRuntimeBase)
IGNORED_ATTR(SynthesizedProtocol)
IGNORED_ATTR(Testable)
IGNORED_ATTR(UIApplicationMain)
IGNORED_ATTR(UnsafeNoObjCTaggedPointer)
IGNORED_ATTR(Versioned)
IGNORED_ATTR(ShowInInterface)
IGNORED_ATTR(DiscardableResult)
IGNORED_ATTR(Implements)
IGNORED_ATTR(StaticInitializeObjCMetadata)
IGNORED_ATTR(DowngradeExhaustivityCheck)
#undef IGNORED_ATTR
// @noreturn has been replaced with a 'Never' return type.
void visitNoReturnAttr(NoReturnAttr *attr) {
if (auto FD = dyn_cast<FuncDecl>(D)) {
auto &SM = TC.Context.SourceMgr;
auto diag = TC.diagnose(attr->getLocation(),
diag::noreturn_not_supported);
auto range = attr->getRangeWithAt();
if (range.isValid())
range.End = range.End.getAdvancedLoc(1);
diag.fixItRemove(range);
auto *last = FD->getParameterList(FD->getNumParameterLists() - 1);
// If the declaration already has a result type, we're going
// to change it to 'Never'.
bool hadResultType = false;
bool isEndOfLine = false;
SourceLoc resultLoc;
if (FD->getBodyResultTypeLoc().hasLocation()) {
const auto &typeLoc = FD->getBodyResultTypeLoc();
hadResultType = true;
resultLoc = typeLoc.getSourceRange().Start;
// If the function 'throws', insert the result type after the
// 'throws'.
} else {
if (FD->getThrowsLoc().isValid()) {
resultLoc = FD->getThrowsLoc();
// Otherwise, insert the result type after the final parameter
// list.
} else if (last->getRParenLoc().isValid()) {
resultLoc = last->getRParenLoc();
}
if (Lexer::getLocForEndOfToken(SM, resultLoc).getAdvancedLoc(1) ==
Lexer::getLocForEndOfLine(SM, resultLoc))
isEndOfLine = true;
resultLoc = Lexer::getLocForEndOfToken(SM, resultLoc);
}
if (hadResultType) {
diag.fixItReplace(resultLoc, "Never");
} else {
std::string fix = " -> Never";
if (!isEndOfLine)
fix = fix + " ";
diag.fixItInsert(resultLoc, fix);
}
auto neverType = TC.Context.getNeverType();
if (neverType)
FD->getBodyResultTypeLoc() = TypeLoc::withoutLoc(neverType);
}
}
void visitAlignmentAttr(AlignmentAttr *attr) {
// Alignment must be a power of two.
unsigned value = attr->Value;
if (value == 0 || (value & (value - 1)) != 0)
TC.diagnose(attr->getLocation(), diag::alignment_not_power_of_two);
}
void visitTransparentAttr(TransparentAttr *attr);
void visitMutationAttr(DeclAttribute *attr);
void visitMutatingAttr(MutatingAttr *attr) { visitMutationAttr(attr); }
void visitNonMutatingAttr(NonMutatingAttr *attr) { visitMutationAttr(attr); }
void visitConsumingAttr(ConsumingAttr *attr) { visitMutationAttr(attr); }
void visitDynamicAttr(DynamicAttr *attr);
void visitOwnershipAttr(OwnershipAttr *attr) {
TC.checkOwnershipAttr(cast<VarDecl>(D), attr);
}
void visitFinalAttr(FinalAttr *attr) {
// Reject combining 'final' with 'open'.
if (auto accessAttr = D->getAttrs().getAttribute<AccessControlAttr>()) {
if (accessAttr->getAccess() == AccessLevel::Open) {
TC.diagnose(attr->getLocation(), diag::open_decl_cannot_be_final,
D->getDescriptiveKind());
return;
}
}
if (isa<ClassDecl>(D))
return;
// 'final' only makes sense in the context of a class declaration.
// Reject it on global functions, protocols, structs, enums, etc.
if (!D->getDeclContext()->getAsClassOrClassExtensionContext()) {
if (TC.Context.isSwiftVersion3() &&
D->getDeclContext()->getAsProtocolExtensionContext())
TC.diagnose(attr->getLocation(),
diag::protocol_extension_cannot_be_final)
.fixItRemove(attr->getRange());
else
TC.diagnose(attr->getLocation(), diag::member_cannot_be_final)
.fixItRemove(attr->getRange());
// Remove the attribute so child declarations are not flagged as final
// and duplicate the error message.
D->getAttrs().removeAttribute(attr);
return;
}
}
void visitIndirectAttr(IndirectAttr *attr) {
if (auto caseDecl = dyn_cast<EnumElementDecl>(D)) {
// An indirect case should have a payload.
if (caseDecl->getArgumentTypeLoc().isNull())
TC.diagnose(attr->getLocation(),
diag::indirect_case_without_payload, caseDecl->getName());
// If the enum is already indirect, its cases don't need to be.
else if (caseDecl->getParentEnum()->getAttrs()
.hasAttribute<IndirectAttr>())
TC.diagnose(attr->getLocation(),
diag::indirect_case_in_indirect_enum);
}
}
void visitWarnUnqualifiedAccessAttr(WarnUnqualifiedAccessAttr *attr) {
if (!D->getDeclContext()->isTypeContext()) {
diagnoseAndRemoveAttr(attr, diag::attr_methods_only, attr);
}
}
void visitIBActionAttr(IBActionAttr *attr);
void visitLazyAttr(LazyAttr *attr);
void visitIBDesignableAttr(IBDesignableAttr *attr);
void visitIBInspectableAttr(IBInspectableAttr *attr);
void visitGKInspectableAttr(GKInspectableAttr *attr);
void visitIBOutletAttr(IBOutletAttr *attr);
void visitLLDBDebuggerFunctionAttr(LLDBDebuggerFunctionAttr *attr);
void visitNSManagedAttr(NSManagedAttr *attr);
void visitOverrideAttr(OverrideAttr *attr);
void visitAccessControlAttr(AccessControlAttr *attr);
void visitSetterAccessAttr(SetterAccessAttr *attr);
bool visitAbstractAccessControlAttr(AbstractAccessControlAttr *attr);
void visitSILStoredAttr(SILStoredAttr *attr);
void visitObjCMembersAttr(ObjCMembersAttr *attr);
};
} // end anonymous namespace
void AttributeEarlyChecker::visitTransparentAttr(TransparentAttr *attr) {
DeclContext *Ctx = D->getDeclContext();
// Protocol declarations cannot be transparent.
if (isa<ProtocolDecl>(Ctx))
return diagnoseAndRemoveAttr(attr,
diag::transparent_in_protocols_not_supported);
// Class declarations cannot be transparent.
if (isa<ClassDecl>(Ctx)) {
// @transparent is always ok on implicitly generated accessors: they can
// be dispatched (even in classes) when the references are within the
// class themself.
if (!(isa<FuncDecl>(D) && cast<FuncDecl>(D)->isAccessor() &&
D->isImplicit()))
return diagnoseAndRemoveAttr(attr,
diag::transparent_in_classes_not_supported);
}
if (auto *VD = dyn_cast<VarDecl>(D)) {
// Stored properties and variables can't be transparent.
if (VD->hasStorage())
return diagnoseAndRemoveAttr(attr, diag::transparent_stored_property);
}
}
void AttributeEarlyChecker::visitMutationAttr(DeclAttribute *attr) {
FuncDecl *FD = cast<FuncDecl>(D);
SelfAccessKind attrModifier;
switch (attr->getKind()) {
case DeclAttrKind::DAK_Consuming:
attrModifier = SelfAccessKind::__Consuming;
break;
case DeclAttrKind::DAK_Mutating:
attrModifier = SelfAccessKind::Mutating;
break;
case DeclAttrKind::DAK_NonMutating:
attrModifier = SelfAccessKind::NonMutating;
break;
default:
llvm_unreachable("unhandled attribute kind");
}
// mutation attributes may only appear in type context.
auto contextTy = FD->getDeclContext()->getDeclaredInterfaceType();
if (!contextTy)
return diagnoseAndRemoveAttr(attr, diag::mutating_invalid_global_scope,
unsigned(attrModifier));
// 'mutating' and 'nonmutating' are not valid on types
// with reference semantics.
if (contextTy->hasReferenceSemantics()) {
if (attrModifier != SelfAccessKind::__Consuming)
return diagnoseAndRemoveAttr(attr, diag::mutating_invalid_classes,
unsigned(attrModifier));
}
// Verify we don't have more than one of mutating, nonmutating,
// and __consuming.
if ((FD->getAttrs().hasAttribute<MutatingAttr>() +
FD->getAttrs().hasAttribute<NonMutatingAttr>() +
FD->getAttrs().hasAttribute<ConsumingAttr>()) > 1) {
if (auto *NMA = FD->getAttrs().getAttribute<NonMutatingAttr>()) {
if (attrModifier != SelfAccessKind::NonMutating) {
diagnoseAndRemoveAttr(NMA, diag::functions_mutating_and_not,
unsigned(SelfAccessKind::NonMutating),
unsigned(attrModifier));
}
}
if (auto *MUA = FD->getAttrs().getAttribute<MutatingAttr>()) {
if (attrModifier != SelfAccessKind::Mutating) {
diagnoseAndRemoveAttr(MUA, diag::functions_mutating_and_not,
unsigned(SelfAccessKind::Mutating),
unsigned(attrModifier));
}
}
if (auto *CSA = FD->getAttrs().getAttribute<ConsumingAttr>()) {
if (attrModifier != SelfAccessKind::__Consuming) {
diagnoseAndRemoveAttr(CSA, diag::functions_mutating_and_not,
unsigned(SelfAccessKind::__Consuming),
unsigned(attrModifier));
}
}
}
// Verify that we don't have a static function.
if (FD->isStatic())
return diagnoseAndRemoveAttr(attr, diag::static_functions_not_mutating);
}
void AttributeEarlyChecker::visitDynamicAttr(DynamicAttr *attr) {
// Only instance members of classes can be dynamic.
auto classDecl = D->getDeclContext()->getAsClassOrClassExtensionContext();
if (!classDecl)
return diagnoseAndRemoveAttr(attr, diag::dynamic_not_in_class);
// Members cannot be both dynamic and final.
if (D->getAttrs().hasAttribute<FinalAttr>())
return diagnoseAndRemoveAttr(attr, diag::dynamic_with_final);
// Members cannot be both dynamic and @nonobjc.
if (D->getAttrs().hasAttribute<NonObjCAttr>())
return diagnoseAndRemoveAttr(attr, diag::dynamic_with_nonobjc);
}
void AttributeEarlyChecker::visitIBActionAttr(IBActionAttr *attr) {
// Only instance methods returning () can be IBActions.
const FuncDecl *FD = cast<FuncDecl>(D);
if (!FD->isPotentialIBActionTarget())
return diagnoseAndRemoveAttr(attr, diag::invalid_ibaction_decl);
}
void AttributeEarlyChecker::visitIBDesignableAttr(IBDesignableAttr *attr) {
if (auto *ED = dyn_cast<ExtensionDecl>(D)) {
if (auto extendedType = ED->getExtendedType()) {
if (auto *nominalDecl = extendedType->getAnyNominal()) {
if (!isa<ClassDecl>(nominalDecl))
return diagnoseAndRemoveAttr(attr, diag::invalid_ibdesignable_extension);
}
}
}
}
void AttributeEarlyChecker::visitIBInspectableAttr(IBInspectableAttr *attr) {
// Only instance properties can be 'IBInspectable'.
auto *VD = cast<VarDecl>(D);
if (!VD->getDeclContext()->getAsClassOrClassExtensionContext() ||
VD->isStatic())
return diagnoseAndRemoveAttr(attr, diag::invalid_ibinspectable,
attr->getAttrName());
}
void AttributeEarlyChecker::visitGKInspectableAttr(GKInspectableAttr *attr) {
// Only instance properties can be 'GKInspectable'.
auto *VD = cast<VarDecl>(D);
if (!VD->getDeclContext()->getAsClassOrClassExtensionContext() ||
VD->isStatic())
return diagnoseAndRemoveAttr(attr, diag::invalid_ibinspectable,
attr->getAttrName());
}
void AttributeEarlyChecker::visitSILStoredAttr(SILStoredAttr *attr) {
auto *VD = cast<VarDecl>(D);
if (VD->getDeclContext()->getAsClassOrClassExtensionContext())
return;
auto nominalDecl = VD->getDeclContext()
->getAsNominalTypeOrNominalTypeExtensionContext();
if (nominalDecl && isa<StructDecl>(nominalDecl))
return;
return diagnoseAndRemoveAttr(attr, diag::invalid_decl_attribute_simple);
}
static Optional<Diag<bool,Type>>
isAcceptableOutletType(Type type, bool &isArray, TypeChecker &TC) {
if (type->isObjCExistentialType() || type->isAny())
return None; // @objc existential types are okay
auto nominal = type->getAnyNominal();
if (auto classDecl = dyn_cast_or_null<ClassDecl>(nominal)) {
if (classDecl->isObjC())
return None; // @objc class types are okay.
return diag::iboutlet_nonobjc_class;
}
if (nominal == TC.Context.getStringDecl()) {
// String is okay because it is bridged to NSString.
// FIXME: BridgesTypes.def is almost sufficient for this.
return None;
}
if (nominal == TC.Context.getArrayDecl()) {
// Arrays of arrays are not allowed.
if (isArray)
return diag::iboutlet_nonobject_type;
isArray = true;
// Handle Array<T>. T must be an Objective-C class or protocol.
auto boundTy = type->castTo<BoundGenericStructType>();
auto boundArgs = boundTy->getGenericArgs();
assert(boundArgs.size() == 1 && "invalid Array declaration");
Type elementTy = boundArgs.front();
return isAcceptableOutletType(elementTy, isArray, TC);
}
if (type->isExistentialType())
return diag::iboutlet_nonobjc_protocol;
// No other types are permitted.
return diag::iboutlet_nonobject_type;
}
void AttributeEarlyChecker::visitIBOutletAttr(IBOutletAttr *attr) {
// Only instance properties can be 'IBOutlet'.
auto *VD = cast<VarDecl>(D);
if (!VD->getDeclContext()->getAsClassOrClassExtensionContext() ||
VD->isStatic())
return diagnoseAndRemoveAttr(attr, diag::invalid_iboutlet);
if (!VD->isSettable(nullptr))
return diagnoseAndRemoveAttr(attr, diag::iboutlet_only_mutable);
// Verify that the field type is valid as an outlet.
auto type = VD->getType();
if (VD->isInvalid())
return;
// Look through ownership types, and optionals.
type = type->getReferenceStorageReferent();
bool wasOptional = false;
if (Type underlying = type->getAnyOptionalObjectType()) {
type = underlying;
wasOptional = true;
}
bool isArray = false;
if (auto isError = isAcceptableOutletType(type, isArray, TC))
return diagnoseAndRemoveAttr(attr, isError.getValue(),
/*array=*/isArray, type);
// If the type wasn't optional, an array, or unowned, complain.
if (!wasOptional && !isArray) {
auto symbolLoc = Lexer::getLocForEndOfToken(
TC.Context.SourceMgr,
VD->getTypeSourceRangeForDiagnostics().End);
TC.diagnose(attr->getLocation(), diag::iboutlet_non_optional,
type);
TC.diagnose(symbolLoc, diag::note_make_optional,
OptionalType::get(type))
.fixItInsert(symbolLoc, "?");
TC.diagnose(symbolLoc, diag::note_make_implicitly_unwrapped_optional,
ImplicitlyUnwrappedOptionalType::get(type))
.fixItInsert(symbolLoc, "!");
}
}
void AttributeEarlyChecker::visitNSManagedAttr(NSManagedAttr *attr) {
// @NSManaged only applies to instance methods and properties within a class.
if (cast<ValueDecl>(D)->isStatic() ||
!D->getDeclContext()->getAsClassOrClassExtensionContext()) {
return diagnoseAndRemoveAttr(attr,
diag::attr_NSManaged_not_instance_member);
}
if (auto *method = dyn_cast<FuncDecl>(D)) {
// Separate out the checks for methods.
if (method->hasBody())
return diagnoseAndRemoveAttr(attr, diag::attr_NSManaged_method_body);
return;
}
// Everything below deals with restrictions on @NSManaged properties.
auto *VD = cast<VarDecl>(D);
if (VD->isLet())
return diagnoseAndRemoveAttr(attr, diag::attr_NSManaged_let_property);
auto diagnoseNotStored = [&](unsigned kind) {
TC.diagnose(attr->getLocation(), diag::attr_NSManaged_not_stored, kind);
return attr->setInvalid();
};
// @NSManaged properties must be written as stored.
switch (VD->getStorageKind()) {
case AbstractStorageDecl::Stored:
// @NSManaged properties end up being computed; complain if there is
// an initializer.
if (VD->getParentInitializer()) {
TC.diagnose(attr->getLocation(), diag::attr_NSManaged_initial_value)
.highlight(VD->getParentInitializer()->getSourceRange());
auto PBD = VD->getParentPatternBinding();
PBD->setInit(PBD->getPatternEntryIndexForVarDecl(VD), nullptr);
}
// Otherwise, ok.
break;
case AbstractStorageDecl::StoredWithTrivialAccessors:
llvm_unreachable("Already created accessors?");
case AbstractStorageDecl::ComputedWithMutableAddress:
case AbstractStorageDecl::Computed:
return diagnoseNotStored(/*computed*/ 0);
case AbstractStorageDecl::StoredWithObservers:
case AbstractStorageDecl::InheritedWithObservers:
return diagnoseNotStored(/*observing*/ 1);
case AbstractStorageDecl::Addressed:
case AbstractStorageDecl::AddressedWithTrivialAccessors:
case AbstractStorageDecl::AddressedWithObservers:
return diagnoseNotStored(/*addressed*/ 2);
}
// @NSManaged properties cannot be @NSCopying
if (auto *NSCopy = VD->getAttrs().getAttribute<NSCopyingAttr>())
return diagnoseAndRemoveAttr(NSCopy, diag::attr_NSManaged_NSCopying);
}
void AttributeEarlyChecker::
visitLLDBDebuggerFunctionAttr(LLDBDebuggerFunctionAttr *attr) {
// This is only legal when debugger support is on.
if (!D->getASTContext().LangOpts.DebuggerSupport)
return diagnoseAndRemoveAttr(attr, diag::attr_for_debugger_support_only);
}
void AttributeEarlyChecker::visitOverrideAttr(OverrideAttr *attr) {
if (!isa<ClassDecl>(D->getDeclContext()) &&
!isa<ExtensionDecl>(D->getDeclContext()))
return diagnoseAndRemoveAttr(attr, diag::override_nonclass_decl);
}
void AttributeEarlyChecker::visitLazyAttr(LazyAttr *attr) {
// lazy may only be used on properties.
auto *VD = cast<VarDecl>(D);
// It cannot currently be used on let's since we don't have a mutability model
// that supports it.
if (VD->isLet())
return diagnoseAndRemoveAttr(attr, diag::lazy_not_on_let);
// lazy is not allowed on a protocol requirement.
auto varDC = VD->getDeclContext();
if (isa<ProtocolDecl>(varDC))
return diagnoseAndRemoveAttr(attr, diag::lazy_not_in_protocol);
// It only works with stored properties.
if (!VD->hasStorage())
return diagnoseAndRemoveAttr(attr, diag::lazy_not_on_computed);
// lazy is not allowed on a lazily initialized global variable or on a
// static property (which is already lazily initialized).
if (VD->isStatic() ||
(varDC->isModuleScopeContext() &&
!varDC->getParentSourceFile()->isScriptMode()))
return diagnoseAndRemoveAttr(attr, diag::lazy_on_already_lazy_global);
// lazy must have an initializer, and the pattern binding must be a simple
// one.
if (!VD->getParentInitializer())
return diagnoseAndRemoveAttr(attr, diag::lazy_requires_initializer);
if (!VD->getParentPatternBinding()->getSingleVar())
return diagnoseAndRemoveAttr(attr, diag::lazy_requires_single_var);
// TODO: we can't currently support lazy properties on non-type-contexts.
if (!VD->getDeclContext()->isTypeContext())
return diagnoseAndRemoveAttr(attr, diag::lazy_must_be_property);
// TODO: Lazy properties can't yet be observed.
switch (VD->getStorageKind()) {
case AbstractStorageDecl::Stored:
case AbstractStorageDecl::StoredWithTrivialAccessors:
break;
case AbstractStorageDecl::StoredWithObservers:
return diagnoseAndRemoveAttr(attr, diag::lazy_not_observable);
case AbstractStorageDecl::InheritedWithObservers:
case AbstractStorageDecl::ComputedWithMutableAddress:
case AbstractStorageDecl::Computed:
case AbstractStorageDecl::Addressed:
case AbstractStorageDecl::AddressedWithTrivialAccessors:
case AbstractStorageDecl::AddressedWithObservers:
llvm_unreachable("non-stored variable not filtered out?");
}
}
bool AttributeEarlyChecker::visitAbstractAccessControlAttr(
AbstractAccessControlAttr *attr) {
// Access control attr may only be used on value decls and extensions.
if (!isa<ValueDecl>(D) && !isa<ExtensionDecl>(D)) {
diagnoseAndRemoveAttr(attr, diag::invalid_decl_modifier, attr);
return true;
}
if (auto extension = dyn_cast<ExtensionDecl>(D)) {
if (!extension->getInherited().empty()) {
diagnoseAndRemoveAttr(attr, diag::extension_access_with_conformances,
attr);
return true;
}
}
// And not on certain value decls.
if (isa<DestructorDecl>(D) || isa<EnumElementDecl>(D)) {
diagnoseAndRemoveAttr(attr, diag::invalid_decl_modifier, attr);
return true;
}
// Or within protocols.
if (isa<ProtocolDecl>(D->getDeclContext())) {
diagnoseAndRemoveAttr(attr, diag::access_control_in_protocol, attr);
return true;
}
return false;
}
void AttributeEarlyChecker::visitAccessControlAttr(AccessControlAttr *attr) {
visitAbstractAccessControlAttr(attr);
}
void AttributeEarlyChecker::visitSetterAccessAttr(
SetterAccessAttr *attr) {
auto storage = dyn_cast<AbstractStorageDecl>(D);
if (!storage)
return diagnoseAndRemoveAttr(attr, diag::access_control_setter,
attr->getAccess());
if (visitAbstractAccessControlAttr(attr))
return;
if (!storage->isSettable(storage->getDeclContext())) {
// This must stay in sync with diag::access_control_setter_read_only.
enum {
SK_Constant = 0,
SK_Variable,
SK_Property,
SK_Subscript
} storageKind;
if (isa<SubscriptDecl>(storage))
storageKind = SK_Subscript;
else if (storage->getDeclContext()->isTypeContext())
storageKind = SK_Property;
else if (cast<VarDecl>(storage)->isLet())
storageKind = SK_Constant;
else
storageKind = SK_Variable;
return diagnoseAndRemoveAttr(attr, diag::access_control_setter_read_only,
attr->getAccess(), storageKind);
}
}
void AttributeEarlyChecker::visitObjCMembersAttr(ObjCMembersAttr *attr) {
if (!isa<ClassDecl>(D))
return diagnoseAndRemoveAttr(attr, diag::objcmembers_attribute_nonclass);
}
void TypeChecker::checkDeclAttributesEarly(Decl *D) {
// Don't perform early attribute validation more than once.
// FIXME: Crummy way to get idempotency.
if (D->didEarlyAttrValidation())
return;
D->setEarlyAttrValidation();
AttributeEarlyChecker Checker(*this, D);
for (auto attr : D->getAttrs()) {
if (!attr->isValid()) continue;
// If Attr.def says that the attribute cannot appear on this kind of
// declaration, diagnose it and disable it.
if (attr->canAppearOnDecl(D)) {
// Otherwise, check it.
Checker.visit(attr);
continue;
}
// Otherwise, this attribute cannot be applied to this declaration. If the
// attribute is only valid on one kind of declaration (which is pretty
// common) give a specific helpful error.
unsigned PossibleDeclKinds = attr->getOptions() & DeclAttribute::OnAnyDecl;
StringRef OnlyKind;
switch (PossibleDeclKinds) {
case DeclAttribute::OnImport:
OnlyKind = "import";
break;
case DeclAttribute::OnVar:
OnlyKind = "var";
break;
case DeclAttribute::OnFunc:
OnlyKind = "func";
break;
case DeclAttribute::OnClass:
OnlyKind = "class";
break;
case DeclAttribute::OnStruct:
OnlyKind = "struct";
break;
case DeclAttribute::OnConstructor:
OnlyKind = "init";
break;
case DeclAttribute::OnProtocol:
OnlyKind = "protocol";
break;
case DeclAttribute::OnParam:
OnlyKind = "parameter";
break;
default:
break;
}
if (!OnlyKind.empty())
Checker.diagnoseAndRemoveAttr(attr, diag::attr_only_one_decl_kind,
attr, OnlyKind);
else if (attr->isDeclModifier())
Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_modifier, attr);
else
Checker.diagnoseAndRemoveAttr(attr, diag::invalid_decl_attribute, attr);
}
}
namespace {
class AttributeChecker : public AttributeVisitor<AttributeChecker> {
TypeChecker &TC;
Decl *D;
/// This emits a diagnostic with a fixit to remove the attribute.
template<typename ...ArgTypes>
void diagnoseAndRemoveAttr(DeclAttribute *attr, ArgTypes &&...Args) {
::diagnoseAndRemoveAttr(TC, D, attr, std::forward<ArgTypes>(Args)...);
}
public:
AttributeChecker(TypeChecker &TC, Decl *D) : TC(TC), D(D) {}
/// Deleting this ensures that all attributes are covered by the visitor
/// below.
void visitDeclAttribute(DeclAttribute *A) = delete;
#define IGNORED_ATTR(CLASS) \
void visit##CLASS##Attr(CLASS##Attr *) {}
IGNORED_ATTR(Alignment)
IGNORED_ATTR(Consuming)
IGNORED_ATTR(Convenience)
IGNORED_ATTR(Dynamic)
IGNORED_ATTR(Effects)
IGNORED_ATTR(Exported)
IGNORED_ATTR(GKInspectable)
IGNORED_ATTR(IBDesignable)
IGNORED_ATTR(IBInspectable)
IGNORED_ATTR(IBOutlet) // checked early.
IGNORED_ATTR(Indirect)
IGNORED_ATTR(Inline)
IGNORED_ATTR(Lazy) // checked early.
IGNORED_ATTR(LLDBDebuggerFunction)
IGNORED_ATTR(Mutating)
IGNORED_ATTR(NonMutating)
IGNORED_ATTR(NonObjC)
IGNORED_ATTR(NoReturn)
IGNORED_ATTR(NSManaged) // checked early.
IGNORED_ATTR(ObjC)
IGNORED_ATTR(ObjCBridged)
IGNORED_ATTR(ObjCNonLazyRealization)
IGNORED_ATTR(ObjCRuntimeName)
IGNORED_ATTR(RestatedObjCConformance)
IGNORED_ATTR(Optional)
IGNORED_ATTR(Ownership)
IGNORED_ATTR(Override)
IGNORED_ATTR(RawDocComment)
IGNORED_ATTR(Semantics)
IGNORED_ATTR(SILGenName)
IGNORED_ATTR(Transparent)
IGNORED_ATTR(SynthesizedProtocol)
IGNORED_ATTR(RequiresStoredPropertyInits)
IGNORED_ATTR(SILStored)
IGNORED_ATTR(Testable)
IGNORED_ATTR(WarnUnqualifiedAccess)
IGNORED_ATTR(ShowInInterface)
IGNORED_ATTR(ObjCMembers)
IGNORED_ATTR(StaticInitializeObjCMetadata)
IGNORED_ATTR(DowngradeExhaustivityCheck)
#undef IGNORED_ATTR
void visitAvailableAttr(AvailableAttr *attr);
void visitCDeclAttr(CDeclAttr *attr);
void visitFinalAttr(FinalAttr *attr);
void visitIBActionAttr(IBActionAttr *attr);
void visitNSCopyingAttr(NSCopyingAttr *attr);
void visitRequiredAttr(RequiredAttr *attr);
void visitRethrowsAttr(RethrowsAttr *attr);
void visitAccessControlAttr(AccessControlAttr *attr);
void visitSetterAccessAttr(SetterAccessAttr *attr);
void checkApplicationMainAttribute(DeclAttribute *attr,
Identifier Id_ApplicationDelegate,
Identifier Id_Kit,
Identifier Id_ApplicationMain);
void visitNSApplicationMainAttr(NSApplicationMainAttr *attr);
void visitUIApplicationMainAttr(UIApplicationMainAttr *attr);
void visitUnsafeNoObjCTaggedPointerAttr(UnsafeNoObjCTaggedPointerAttr *attr);
void visitSwiftNativeObjCRuntimeBaseAttr(
SwiftNativeObjCRuntimeBaseAttr *attr);
void checkOperatorAttribute(DeclAttribute *attr);
void visitInfixAttr(InfixAttr *attr) { checkOperatorAttribute(attr); }
void visitPostfixAttr(PostfixAttr *attr) { checkOperatorAttribute(attr); }
void visitPrefixAttr(PrefixAttr *attr) { checkOperatorAttribute(attr); }
void visitSpecializeAttr(SpecializeAttr *attr);
void visitFixedLayoutAttr(FixedLayoutAttr *attr);
void visitVersionedAttr(VersionedAttr *attr);
void visitInlineableAttr(InlineableAttr *attr);
void visitDiscardableResultAttr(DiscardableResultAttr *attr);
void visitImplementsAttr(ImplementsAttr *attr);
};
} // end anonymous namespace
static bool checkObjectOrOptionalObjectType(TypeChecker &TC, Decl *D,
ParamDecl *param) {
Type ty = param->getType();
if (auto unwrapped = ty->getAnyOptionalObjectType())
ty = unwrapped;
if (auto classDecl = ty->getClassOrBoundGenericClass()) {
// @objc class types are okay.
if (!classDecl->isObjC()) {
TC.diagnose(D, diag::ibaction_nonobjc_class_argument,
param->getType())
.highlight(param->getSourceRange());
return true;
}
} else if (ty->isObjCExistentialType() || ty->isAny()) {
// @objc existential types are okay, as is Any.
// Nothing to do.
} else {
// No other types are permitted.
TC.diagnose(D, diag::ibaction_nonobject_argument,
param->getType())
.highlight(param->getSourceRange());
return true;
}
return false;
}
static bool isiOS(TypeChecker &TC) {
return TC.getLangOpts().Target.isiOS();
}
static bool iswatchOS(TypeChecker &TC) {
return TC.getLangOpts().Target.isWatchOS();
}
static bool isRelaxedIBAction(TypeChecker &TC) {
return isiOS(TC) || iswatchOS(TC);
}
void AttributeChecker::visitIBActionAttr(IBActionAttr *attr) {
// IBActions instance methods must have type Class -> (...) -> ().
auto *FD = cast<FuncDecl>(D);
Type CurriedTy = FD->getMethodInterfaceType();
Type ResultTy = CurriedTy->castTo<AnyFunctionType>()->getResult();
if (!ResultTy->isEqual(TupleType::getEmpty(TC.Context))) {
TC.diagnose(D, diag::invalid_ibaction_result, ResultTy);
attr->setInvalid();
return;
}
auto paramList = FD->getParameterList(1);
bool relaxedIBActionUsedOnOSX = false;
bool Valid = true;
switch (paramList->size()) {
case 0:
// (iOS only) No arguments.
if (!isRelaxedIBAction(TC)) {
relaxedIBActionUsedOnOSX = true;
break;
}
break;
case 1:
// One argument. May be a scalar on iOS/watchOS (because of WatchKit).
if (isRelaxedIBAction(TC)) {
// Do a rough check to allow any ObjC-representable struct or enum type
// on iOS.
Type ty = paramList->get(0)->getType();
if (auto nominal = ty->getAnyNominal())
if (isa<StructDecl>(nominal) || isa<EnumDecl>(nominal))
if (nominal->classifyAsOptionalType() == OTK_None)
if (ty->isTriviallyRepresentableIn(ForeignLanguage::ObjectiveC,
cast<FuncDecl>(D)))
break; // Looks ok.
}
if (checkObjectOrOptionalObjectType(TC, D, paramList->get(0)))
Valid = false;
break;
case 2:
// (iOS/watchOS only) Two arguments, the second of which is a UIEvent.
// We don't currently enforce the UIEvent part.
if (!isRelaxedIBAction(TC)) {
relaxedIBActionUsedOnOSX = true;
break;
}
if (checkObjectOrOptionalObjectType(TC, D, paramList->get(0)))
Valid = false;
if (checkObjectOrOptionalObjectType(TC, D, paramList->get(1)))
Valid = false;
break;
default:
// No platform allows an action signature with more than two arguments.
TC.diagnose(D, diag::invalid_ibaction_argument_count,
isRelaxedIBAction(TC));
Valid = false;
break;
}
if (relaxedIBActionUsedOnOSX) {
TC.diagnose(D, diag::invalid_ibaction_argument_count,
/*relaxedIBAction=*/false);
Valid = false;
}
if (!Valid)
attr->setInvalid();
}
/// Get the innermost enclosing declaration for a declaration.
static Decl *getEnclosingDeclForDecl(Decl *D) {
// If the declaration is an accessor, treat its storage declaration
// as the enclosing declaration.
if (auto *FD = dyn_cast<FuncDecl>(D)) {
if (FD->isAccessor()) {
return FD->getAccessorStorageDecl();
}
}
return D->getDeclContext()->getInnermostDeclarationDeclContext();
}
void AttributeChecker::visitAvailableAttr(AvailableAttr *attr) {
if (TC.getLangOpts().DisableAvailabilityChecking)
return;
if (!attr->isActivePlatform(TC.Context) || !attr->Introduced.hasValue())
return;
SourceLoc attrLoc = attr->getLocation();
Optional<Diag<>> MaybeNotAllowed =
TC.diagnosticIfDeclCannotBePotentiallyUnavailable(D);
if (MaybeNotAllowed.hasValue()) {
TC.diagnose(attrLoc, MaybeNotAllowed.getValue());
}
// Find the innermost enclosing declaration with an availability
// range annotation and ensure that this attribute's available version range
// is fully contained within that declaration's range. If there is no such
// enclosing declaration, then there is nothing to check.
Optional<AvailabilityContext> EnclosingAnnotatedRange;
Decl *EnclosingDecl = getEnclosingDeclForDecl(D);
while (EnclosingDecl) {
EnclosingAnnotatedRange =
AvailabilityInference::annotatedAvailableRange(EnclosingDecl,
TC.Context);
if (EnclosingAnnotatedRange.hasValue())
break;
EnclosingDecl = getEnclosingDeclForDecl(EnclosingDecl);
}
if (!EnclosingDecl)
return;
AvailabilityContext AttrRange{
VersionRange::allGTE(attr->Introduced.getValue())};
if (!AttrRange.isContainedIn(EnclosingAnnotatedRange.getValue())) {
TC.diagnose(attr->getLocation(),
diag::availability_decl_more_than_enclosing);
TC.diagnose(EnclosingDecl->getLoc(),
diag::availability_decl_more_than_enclosing_enclosing_here);
}
}
void AttributeChecker::visitCDeclAttr(CDeclAttr *attr) {
// Only top-level func decls are currently supported.
if (D->getDeclContext()->isTypeContext())
TC.diagnose(attr->getLocation(),
diag::cdecl_not_at_top_level);
// The name must not be empty.
if (attr->Name.empty())
TC.diagnose(attr->getLocation(),
diag::cdecl_empty_name);
}
void AttributeChecker::visitUnsafeNoObjCTaggedPointerAttr(
UnsafeNoObjCTaggedPointerAttr *attr) {
// Only class protocols can have the attribute.
auto proto = dyn_cast<ProtocolDecl>(D);
if (!proto) {
TC.diagnose(attr->getLocation(),
diag::no_objc_tagged_pointer_not_class_protocol);
attr->setInvalid();
}
if (!proto->requiresClass()
&& !proto->getAttrs().hasAttribute<ObjCAttr>()) {
TC.diagnose(attr->getLocation(),
diag::no_objc_tagged_pointer_not_class_protocol);
attr->setInvalid();
}
}
void AttributeChecker::visitSwiftNativeObjCRuntimeBaseAttr(
SwiftNativeObjCRuntimeBaseAttr *attr) {
// Only root classes can have the attribute.
auto theClass = dyn_cast<ClassDecl>(D);
if (!theClass) {
TC.diagnose(attr->getLocation(),
diag::swift_native_objc_runtime_base_not_on_root_class);
attr->setInvalid();
return;
}
if (theClass->hasSuperclass()) {
TC.diagnose(attr->getLocation(),
diag::swift_native_objc_runtime_base_not_on_root_class);
attr->setInvalid();
return;
}
}
void AttributeChecker::visitFinalAttr(FinalAttr *attr) {
// final on classes marks all members with final.
if (isa<ClassDecl>(D))
return;
// We currently only support final on var/let, func and subscript
// declarations.
if (!isa<VarDecl>(D) && !isa<FuncDecl>(D) && !isa<SubscriptDecl>(D)) {
TC.diagnose(attr->getLocation(), diag::final_not_allowed_here)
.fixItRemove(attr->getRange());
return;
}
if (auto *FD = dyn_cast<FuncDecl>(D)) {
if (FD->isAccessor() && !attr->isImplicit()) {
unsigned Kind = 2;
if (auto *VD = dyn_cast<VarDecl>(FD->getAccessorStorageDecl()))
Kind = VD->isLet() ? 1 : 0;
TC.diagnose(attr->getLocation(), diag::final_not_on_accessors, Kind)
.fixItRemove(attr->getRange());
return;
}
}
}
/// Return true if this is a builtin operator that cannot be defined in user
/// code.
static bool isBuiltinOperator(StringRef name, DeclAttribute *attr) {
return ((isa<PrefixAttr>(attr) && name == "&") || // lvalue to inout
(isa<PostfixAttr>(attr) && name == "!") || // optional unwrapping
(isa<PostfixAttr>(attr) && name == "?") || // optional chaining
(isa<InfixAttr>(attr) && name == "?") || // ternary operator
(isa<PostfixAttr>(attr) && name == ">") || // generic argument list
(isa<PrefixAttr>(attr) && name == "<")); // generic argument list
}
void AttributeChecker::checkOperatorAttribute(DeclAttribute *attr) {
// Check out the operator attributes. They may be attached to an operator
// declaration or a function.
if (auto *OD = dyn_cast<OperatorDecl>(D)) {
// Reject attempts to define builtin operators.
if (isBuiltinOperator(OD->getName().str(), attr)) {
TC.diagnose(D->getStartLoc(), diag::redefining_builtin_operator,
attr->getAttrName(), OD->getName().str());
attr->setInvalid();
return;
}
// Otherwise, the attribute is always ok on an operator.
return;
}
// Operators implementations may only be defined as functions.
auto *FD = dyn_cast<FuncDecl>(D);
if (!FD) {
TC.diagnose(D->getLoc(), diag::operator_not_func);
attr->setInvalid();
return;
}
// Only functions with an operator identifier can be declared with as an
// operator.
if (!FD->isOperator()) {
TC.diagnose(D->getStartLoc(), diag::attribute_requires_operator_identifier,
attr->getAttrName());
attr->setInvalid();
return;
}
// Reject attempts to define builtin operators.
if (isBuiltinOperator(FD->getName().str(), attr)) {
TC.diagnose(D->getStartLoc(), diag::redefining_builtin_operator,
attr->getAttrName(), FD->getName().str());
attr->setInvalid();
return;
}
// Otherwise, must be unary.
if (!FD->isUnaryOperator()) {
TC.diagnose(attr->getLocation(), diag::attribute_requires_single_argument,
attr->getAttrName());
attr->setInvalid();
return;
}
}
void AttributeChecker::visitNSCopyingAttr(NSCopyingAttr *attr) {
// The @NSCopying attribute is only allowed on stored properties.
auto *VD = cast<VarDecl>(D);
// It may only be used on class members.
auto classDecl = D->getDeclContext()->getAsClassOrClassExtensionContext();
if (!classDecl) {
TC.diagnose(attr->getLocation(), diag::nscopying_only_on_class_properties);
attr->setInvalid();
return;
}
if (!VD->isSettable(VD->getDeclContext())) {
TC.diagnose(attr->getLocation(), diag::nscopying_only_mutable);
attr->setInvalid();
return;
}
if (!VD->hasStorage()) {
TC.diagnose(attr->getLocation(), diag::nscopying_only_stored_property);
attr->setInvalid();
return;
}
assert(VD->getOverriddenDecl() == nullptr &&
"Can't have value with storage that is an override");
// Check the type. It must be must be [unchecked]optional, weak, a normal
// class, AnyObject, or classbound protocol.
// must conform to the NSCopying protocol.
}
void AttributeChecker::checkApplicationMainAttribute(DeclAttribute *attr,
Identifier Id_ApplicationDelegate,
Identifier Id_Kit,
Identifier Id_ApplicationMain) {
// %select indexes for ApplicationMain diagnostics.
enum : unsigned {
UIApplicationMainClass,
NSApplicationMainClass,
};
unsigned applicationMainKind;
if (isa<UIApplicationMainAttr>(attr))
applicationMainKind = UIApplicationMainClass;
else if (isa<NSApplicationMainAttr>(attr))
applicationMainKind = NSApplicationMainClass;
else
llvm_unreachable("not an ApplicationMain attr");
auto *CD = dyn_cast<ClassDecl>(D);
// The applicant not being a class should have been diagnosed by the early
// checker.
if (!CD) return;
// The class cannot be generic.
if (CD->isGenericContext()) {
TC.diagnose(attr->getLocation(),
diag::attr_generic_ApplicationMain_not_supported,
applicationMainKind);
attr->setInvalid();
return;
}
// @XXApplicationMain classes must conform to the XXApplicationDelegate
// protocol.
auto &C = D->getASTContext();
auto KitModule = C.getLoadedModule(Id_Kit);
ProtocolDecl *ApplicationDelegateProto = nullptr;
if (KitModule) {
auto lookupOptions = defaultUnqualifiedLookupOptions;
lookupOptions |= NameLookupFlags::KnownPrivate;
auto lookup = TC.lookupUnqualifiedType(KitModule, Id_ApplicationDelegate,
SourceLoc(),
lookupOptions);
if (lookup.size() == 1)
ApplicationDelegateProto = dyn_cast<ProtocolDecl>(
lookup[0].getValueDecl());
}
if (!ApplicationDelegateProto ||
!TC.conformsToProtocol(CD->getDeclaredType(), ApplicationDelegateProto,
CD, None)) {
TC.diagnose(attr->getLocation(),
diag::attr_ApplicationMain_not_ApplicationDelegate,
applicationMainKind);
attr->setInvalid();
}
if (attr->isInvalid())
return;
// Register the class as the main class in the module. If there are multiples
// they will be diagnosed.
auto *SF = cast<SourceFile>(CD->getModuleScopeContext());
if (SF->registerMainClass(CD, attr->getLocation()))
attr->setInvalid();
// Check that we have the needed symbols in the frameworks.
auto lookupOptions = defaultUnqualifiedLookupOptions;
lookupOptions |= NameLookupFlags::KnownPrivate;
auto lookupMain = TC.lookupUnqualified(KitModule, Id_ApplicationMain,
SourceLoc(), lookupOptions);
for (const auto &result : lookupMain) {
TC.validateDecl(result.getValueDecl());
}
auto Foundation = TC.Context.getLoadedModule(C.Id_Foundation);
if (Foundation) {
auto lookupString = TC.lookupUnqualified(
Foundation,
C.getIdentifier("NSStringFromClass"),
SourceLoc(),
lookupOptions);
for (const auto &result : lookupString) {
TC.validateDecl(result.getValueDecl());
}
}
}
void AttributeChecker::visitNSApplicationMainAttr(NSApplicationMainAttr *attr) {
auto &C = D->getASTContext();
checkApplicationMainAttribute(attr,
C.getIdentifier("NSApplicationDelegate"),
C.getIdentifier("AppKit"),
C.getIdentifier("NSApplicationMain"));
}
void AttributeChecker::visitUIApplicationMainAttr(UIApplicationMainAttr *attr) {
auto &C = D->getASTContext();
checkApplicationMainAttribute(attr,
C.getIdentifier("UIApplicationDelegate"),
C.getIdentifier("UIKit"),
C.getIdentifier("UIApplicationMain"));
}
/// Determine whether the given context is an extension to an Objective-C class
/// where the class is defined in the Objective-C module and the extension is
/// defined within its module.
static bool isObjCClassExtensionInOverlay(DeclContext *dc) {
// Check whether we have an extension.
auto ext = dyn_cast<ExtensionDecl>(dc);
if (!ext)
return false;
// Find the extended class.
auto classDecl = ext->getExtendedType()->getClassOrBoundGenericClass();
if (!classDecl)
return false;
return isInOverlayModuleForImportedModule(ext, classDecl);
}
void AttributeChecker::visitRequiredAttr(RequiredAttr *attr) {
// The required attribute only applies to constructors.
auto ctor = cast<ConstructorDecl>(D);
auto parentTy = ctor->getDeclContext()->getDeclaredInterfaceType();
if (!parentTy) {
// Constructor outside of nominal type context; we've already complained
// elsewhere.
attr->setInvalid();
return;
}
// Only classes can have required constructors.
if (parentTy->getClassOrBoundGenericClass()) {
// The constructor must be declared within the class itself.
// FIXME: Allow an SDK overlay to add a required initializer to a class
// defined in Objective-C
if (!isa<ClassDecl>(ctor->getDeclContext()) &&
!isObjCClassExtensionInOverlay(ctor->getDeclContext())) {
TC.diagnose(ctor, diag::required_initializer_in_extension, parentTy)
.highlight(attr->getLocation());
attr->setInvalid();
return;
}
} else {
if (!parentTy->hasError()) {
TC.diagnose(ctor, diag::required_initializer_nonclass, parentTy)
.highlight(attr->getLocation());
}
attr->setInvalid();
return;
}
}
static bool hasThrowingFunctionParameter(CanType type) {
// Only consider throwing function types.
if (auto fnType = dyn_cast<AnyFunctionType>(type)) {
return fnType->getExtInfo().throws();
}
// Look through tuples.
if (auto tuple = dyn_cast<TupleType>(type)) {
for (auto eltType : tuple.getElementTypes()) {
if (hasThrowingFunctionParameter(eltType))
return true;
}
return false;
}
// Suppress diagnostics in the presence of errors.
if (type->hasError()) {
return true;
}
return false;
}
void AttributeChecker::visitRethrowsAttr(RethrowsAttr *attr) {
// 'rethrows' only applies to functions that take throwing functions
// as parameters.
auto fn = cast<AbstractFunctionDecl>(D);
for (auto paramList : fn->getParameterLists()) {
for (auto param : *paramList)
if (hasThrowingFunctionParameter(param->getType()
->lookThroughAllAnyOptionalTypes()
->getCanonicalType()))
return;
}
TC.diagnose(attr->getLocation(), diag::rethrows_without_throwing_parameter);
attr->setInvalid();
}
void AttributeChecker::visitAccessControlAttr(AccessControlAttr *attr) {
if (auto extension = dyn_cast<ExtensionDecl>(D)) {
if (attr->getAccess() == AccessLevel::Open) {
TC.diagnose(attr->getLocation(), diag::access_control_extension_open)
.fixItReplace(attr->getRange(), "public");
attr->setInvalid();
return;
}
Type extendedTy = extension->getExtendedType();
AccessLevel typeAccess = extendedTy->getAnyNominal()->getFormalAccess();
if (attr->getAccess() > typeAccess) {
TC.diagnose(attr->getLocation(), diag::access_control_extension_more,
typeAccess,
extendedTy->getAnyNominal()->getDescriptiveKind(),
attr->getAccess())
.fixItRemove(attr->getRange());
attr->setInvalid();
return;
}
} else if (auto extension = dyn_cast<ExtensionDecl>(D->getDeclContext())) {
TC.computeDefaultAccessLevel(extension);
AccessLevel maxAccess = extension->getMaxAccessLevel();
if (std::min(attr->getAccess(), AccessLevel::Public) > maxAccess) {
// FIXME: It would be nice to say what part of the requirements actually
// end up being problematic.
auto diag =
TC.diagnose(attr->getLocation(),
diag::access_control_ext_requirement_member_more,
attr->getAccess(),
D->getDescriptiveKind(),
maxAccess);
swift::fixItAccess(diag, cast<ValueDecl>(D), maxAccess);
return;
}
auto extAttr = extension->getAttrs().getAttribute<AccessControlAttr>();
if (extAttr && attr->getAccess() > extAttr->getAccess()) {
auto diag = TC.diagnose(attr->getLocation(),
diag::access_control_ext_member_more,
attr->getAccess(),
D->getDescriptiveKind(),
extAttr->getAccess());
swift::fixItAccess(diag, cast<ValueDecl>(D), extAttr->getAccess());
return;
}
}
if (attr->getAccess() == AccessLevel::Open) {
if (!isa<ClassDecl>(D) && !D->isPotentiallyOverridable() &&
!attr->isInvalid()) {
TC.diagnose(attr->getLocation(), diag::access_control_open_bad_decl)
.fixItReplace(attr->getRange(), "public");
attr->setInvalid();
}
}
}
void
AttributeChecker::visitSetterAccessAttr(SetterAccessAttr *attr) {
auto getterAccess = cast<ValueDecl>(D)->getFormalAccess();
if (attr->getAccess() > getterAccess) {
// This must stay in sync with diag::access_control_setter_more.
enum {
SK_Variable = 0,
SK_Property,
SK_Subscript
} storageKind;
if (isa<SubscriptDecl>(D))
storageKind = SK_Subscript;
else if (D->getDeclContext()->isTypeContext())
storageKind = SK_Property;
else
storageKind = SK_Variable;
TC.diagnose(attr->getLocation(), diag::access_control_setter_more,
getterAccess, storageKind, attr->getAccess());
attr->setInvalid();
return;
}
}
/// Collect all used generic parameter types from a given type.
static void collectUsedGenericParameters(
Type Ty, SmallPtrSet<TypeBase *, 4> &ConstrainedGenericParams) {
if (!Ty)
return;
if (!Ty->hasTypeParameter())
return;
// Add used generic parameters/archetypes.
Ty.visit([&](Type Ty) {
if (auto GP = dyn_cast<GenericTypeParamType>(Ty->getCanonicalType())) {
ConstrainedGenericParams.insert(GP);
}
});
}
/// Perform some sanity checks for the requirements provided by
/// the @_specialize attribute.
static void checkSpecializeAttrRequirements(
SpecializeAttr *attr,
AbstractFunctionDecl *FD,
ArrayRef<RequirementRepr> requirements,
SmallPtrSet<TypeBase *, 4> constrainedGenericParams,
TypeChecker &TC) {
auto genericSig = FD->getGenericSignature();
bool isInvalidAttr = false;
// Check that requirements are defined only on
// generic parameter types and not on their associated or dependent types or
// other generic types using these parameters.
for (auto &req : requirements) {
if (req.getKind() == RequirementReprKind::SameType) {
auto firstType = req.getFirstType();
auto secondType = req.getSecondType();
if (!firstType || firstType->is<ErrorType>() ||
firstType->hasArchetype()) {
isInvalidAttr = true;
continue;
}
if (!secondType || secondType->is<ErrorType>() ||
secondType->hasArchetype()) {
isInvalidAttr = true;
continue;
}
collectUsedGenericParameters(firstType, constrainedGenericParams);
collectUsedGenericParameters(secondType, constrainedGenericParams);
// One of the types should be concrete and the other one should not.
bool isFirstTypeNonConcrete = firstType->hasTypeParameter();
bool isSecondTypeNonConcrete = secondType->hasTypeParameter();
if (isFirstTypeNonConcrete && isSecondTypeNonConcrete) {
TC.diagnose(attr->getLocation(),
diag::specialize_attr_non_concrete_same_type_req)
.highlight(req.getSourceRange());
continue;
}
if (!(isFirstTypeNonConcrete ^ isSecondTypeNonConcrete)) {
TC.diagnose(attr->getLocation(),
diag::specialize_attr_only_one_concrete_same_type_req)
.highlight(req.getSourceRange());
continue;
}
if (isFirstTypeNonConcrete) {
if (!isa<GenericTypeParamType>(firstType->getCanonicalType())) {
TC.diagnose(attr->getLocation(),
diag::specialize_attr_only_generic_param_req)
.highlight(req.getFirstTypeRepr()->getSourceRange());
}
}
if (isSecondTypeNonConcrete) {
if (!isa<GenericTypeParamType>(secondType->getCanonicalType())) {
TC.diagnose(attr->getLocation(),
diag::specialize_attr_only_generic_param_req)
.highlight(req.getSecondTypeRepr()->getSourceRange());
}
}
continue;
}
if (req.getKind() == RequirementReprKind::LayoutConstraint ||
req.getKind() == RequirementReprKind::TypeConstraint) {
auto subjectType = req.getSubject();
// Skip any unknown or error types.
if (!subjectType || subjectType->is<ErrorType>() ||
subjectType->hasArchetype()) {
isInvalidAttr = true;
continue;
}
if (req.getKind() == RequirementReprKind::TypeConstraint) {
auto constraint = req.getConstraint();
if (!constraint || constraint->hasError() ||
constraint->hasArchetype()) {
isInvalidAttr = true;
continue;
}
auto nominalTy = constraint->getNominalOrBoundGenericNominal();
if (!nominalTy) {
TC.diagnose(attr->getLocation(),
diag::specialize_attr_non_nominal_type_constraint_req)
.highlight(req.getSourceRange());
continue;
}
auto proto = dyn_cast<ProtocolDecl>(nominalTy);
if (!proto) {
TC.diagnose(attr->getLocation(),
diag::specialize_attr_non_protocol_type_constraint_req)
.highlight(req.getSourceRange());
}
}
bool isSubjectNonConcrete = subjectType->hasTypeParameter();
if (isSubjectNonConcrete) {
collectUsedGenericParameters(subjectType, constrainedGenericParams);
if (!isa<GenericTypeParamType>(subjectType->getCanonicalType())) {
TC.diagnose(attr->getLocation(),
diag::specialize_attr_only_generic_param_req)
.highlight(req.getSubjectRepr()->getSourceRange());
}
}
if (req.getKind() == RequirementReprKind::TypeConstraint) {
TC.diagnose(attr->getLocation(),
diag::specialize_attr_unsupported_kind_of_req)
.highlight(req.getSourceRange());
}
continue;
}
TC.diagnose(attr->getLocation(),
diag::specialize_attr_unsupported_kind_of_req)
.highlight(req.getSourceRange());
}
if (isInvalidAttr) {
attr->setInvalid();
}
if (!attr->isFullSpecialization())
return;
if (constrainedGenericParams.size() == genericSig->getGenericParams().size())
return;
TC.diagnose(
attr->getLocation(), diag::specialize_attr_type_parameter_count_mismatch,
genericSig->getGenericParams().size(), constrainedGenericParams.size(),
constrainedGenericParams.size() < genericSig->getGenericParams().size());
if (constrainedGenericParams.size() < genericSig->getGenericParams().size()) {
// Figure out which archetypes are not constrained.
for (auto gp : genericSig->getGenericParams()) {
if (constrainedGenericParams.count(gp->getCanonicalType().getPointer()))
continue;
auto gpDecl = gp->getDecl();
if (gpDecl) {
TC.diagnose(attr->getLocation(),
diag::specialize_attr_missing_constraint,
gpDecl->getFullName());
}
}
}
}
/// Type check that a set of requirements provided by @_specialize.
/// Store the set of requirements in the attribute.
void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) {
DeclContext *DC = D->getDeclContext();
auto *FD = cast<AbstractFunctionDecl>(D);
auto *genericSig = FD->getGenericSignature();
auto *genericEnv = FD->getGenericEnvironment();
auto *trailingWhereClause = attr->getTrailingWhereClause();
if (!trailingWhereClause) {
// Report a missing "where" clause.
TC.diagnose(attr->getLocation(), diag::specialize_missing_where_clause);
return;
}
if (trailingWhereClause->getRequirements().empty()) {
// Report an empty "where" clause.
TC.diagnose(attr->getLocation(), diag::specialize_empty_where_clause);
return;
}
if (!genericSig) {
// Only generic functions are permitted to have trailing where clauses.
TC.diagnose(attr->getLocation(),
diag::specialize_attr_nongeneric_trailing_where,
FD->getFullName())
.highlight(trailingWhereClause->getSourceRange());
return;
}
// Form a new generic signature based on the old one.
GenericSignatureBuilder Builder(D->getASTContext(),
TypeChecker::LookUpConformance(TC, DC));
// First, add the old generic signature.
Builder.addGenericSignature(genericSig);
SmallVector<Requirement, 4> convertedRequirements;
SmallVector<RequirementRepr, 4> resolvedRequirements;
// Add all requirements from the "where" clause to the old signature
// to check if there are any inconsistencies.
auto options = TypeResolutionOptions();
// Set of generic parameters being constrained. It is used to
// determine if a full specialization misses requirements for
// some of the generic parameters.
SmallPtrSet<TypeBase *, 4> constrainedGenericParams;
// Go over the set of requirements and resolve their types.
for (auto &req : trailingWhereClause->getRequirements()) {
if (req.getKind() == RequirementReprKind::SameType) {
auto firstType = TC.resolveType(req.getFirstTypeRepr(), FD, options);
auto secondType = TC.resolveType(req.getSecondTypeRepr(), FD, options);
Type interfaceFirstType;
Type interfaceSecondType;
// Map types to their interface types.
if (firstType)
interfaceFirstType = genericEnv->mapTypeOutOfContext(firstType);
if (secondType)
interfaceSecondType = genericEnv->mapTypeOutOfContext(secondType);
collectUsedGenericParameters(interfaceFirstType,
constrainedGenericParams);
collectUsedGenericParameters(interfaceSecondType,
constrainedGenericParams);
// Skip any unknown or error types.
if (!firstType || firstType->is<ErrorType>() || !secondType ||
secondType->is<ErrorType>())
continue;
Type genericType;
Type concreteType;
if (interfaceFirstType->hasTypeParameter()) {
genericType = interfaceFirstType;
concreteType = interfaceSecondType;
} else {
genericType = interfaceSecondType;
concreteType = interfaceFirstType;
}
// Add a resolved requirement.
if (interfaceFirstType->hasTypeParameter()) {
resolvedRequirements.push_back(RequirementRepr::getSameType(
TypeLoc(req.getFirstTypeRepr(), genericType), req.getEqualLoc(),
TypeLoc(req.getSecondTypeRepr(), concreteType)));
} else {
resolvedRequirements.push_back(RequirementRepr::getSameType(
TypeLoc(req.getFirstTypeRepr(), concreteType), req.getEqualLoc(),
TypeLoc(req.getSecondTypeRepr(), genericType)));
}
// Convert the requirement into a form which uses canonical interface
// types.
Requirement convertedRequirement(RequirementKind::SameType,
genericType->getCanonicalType(),
concreteType->getCanonicalType());
convertedRequirements.push_back(convertedRequirement);
continue;
}
if (req.getKind() == RequirementReprKind::LayoutConstraint) {
auto subjectType = TC.resolveType(req.getSubjectRepr(), FD, options);
Type interfaceSubjectType;
// Map types to their interface types.
if (subjectType)
interfaceSubjectType = genericEnv->mapTypeOutOfContext(subjectType);
collectUsedGenericParameters(interfaceSubjectType,
constrainedGenericParams);
// Skip any unknown or error types.
if (!subjectType || subjectType->is<ErrorType>() ||
!req.getLayoutConstraint() ||
!req.getLayoutConstraint()->isKnownLayout())
continue;
// Re-create a requirement using the resolved interface types.
auto resolvedReq = RequirementRepr::getLayoutConstraint(
TypeLoc(req.getSubjectRepr(), interfaceSubjectType),
req.getColonLoc(),
req.getLayoutConstraintLoc());
// Add a resolved requirement.
resolvedRequirements.push_back(resolvedReq);
// Convert the requirement into a form which uses canonical interface
// types.
Requirement convertedRequirement(
RequirementKind::Layout,
interfaceSubjectType->getCanonicalType(),
req.getLayoutConstraint());
convertedRequirements.push_back(convertedRequirement);
continue;
}
if (req.getKind() == RequirementReprKind::TypeConstraint) {
auto subjectType = TC.resolveType(req.getSubjectRepr(), FD, options);
auto constraint = TC.resolveType(
req.getConstraintLoc().getTypeRepr(), FD, options);
Type interfaceSubjectType;
// Map types to their interface types.
if (subjectType)
interfaceSubjectType = genericEnv->mapTypeOutOfContext(subjectType);
collectUsedGenericParameters(interfaceSubjectType,
constrainedGenericParams);
// Skip any unknown or error types.
if (!subjectType || subjectType->hasError() ||
!constraint || constraint->hasError())
continue;
auto interfaceLayoutConstraint =
genericEnv->mapTypeOutOfContext(constraint);
// Re-create a requirement using the resolved interface types.
auto resolvedReq = RequirementRepr::getTypeConstraint(
TypeLoc(req.getSubjectRepr(), interfaceSubjectType),
req.getColonLoc(),
TypeLoc(req.getConstraintRepr(), interfaceLayoutConstraint));
// Add a resolved requirement.
resolvedRequirements.push_back(resolvedReq);
// Convert the requirement into a form which uses canonical interface
// types.
Requirement convertedRequirement(
RequirementKind::Conformance,
interfaceSubjectType->getCanonicalType(),
interfaceLayoutConstraint->getCanonicalType());
convertedRequirements.push_back(convertedRequirement);
continue;
}
}
// Check the validity of provided requirements.
checkSpecializeAttrRequirements(attr, FD, resolvedRequirements,
constrainedGenericParams, TC);
// Store converted requirements in the attribute so that they are
// serialized later.
attr->setRequirements(DC->getASTContext(), convertedRequirements);
// Add the requirements to the builder.
for (auto &req : resolvedRequirements)
Builder.addRequirement(&req, DC->getParentModule());
// Check the result.
(void)std::move(Builder).computeGenericSignature(
attr->getLocation(),
/*allowConcreteGenericParams=*/true);
}
void AttributeChecker::visitFixedLayoutAttr(FixedLayoutAttr *attr) {
auto *VD = cast<ValueDecl>(D);
auto access = VD->getFormalAccess(/*useDC=*/nullptr,
/*respectVersionedAttr=*/true);
if (access < AccessLevel::Public) {
TC.diagnose(attr->getLocation(),
diag::fixed_layout_attr_on_internal_type,
VD->getFullName(),
access)
.fixItRemove(attr->getRangeWithAt());
attr->setInvalid();
}
}
void AttributeChecker::visitVersionedAttr(VersionedAttr *attr) {
auto *VD = cast<ValueDecl>(D);
// FIXME: Once protocols can contain nominal types, do we want to allow
// these nominal types to have access control (and also @_versioned)?
if (isa<ProtocolDecl>(VD->getDeclContext())) {
TC.diagnose(attr->getLocation(),
diag::versioned_attr_in_protocol)
.fixItRemove(attr->getRangeWithAt());
attr->setInvalid();
return;
}
// @_versioned can only be applied to internal declarations.
if (VD->getFormalAccess() != AccessLevel::Internal) {
TC.diagnose(attr->getLocation(), diag::versioned_attr_with_explicit_access,
VD->getFullName(),
VD->getFormalAccess())
.fixItRemove(attr->getRangeWithAt());
attr->setInvalid();
return;
}
}
void AttributeChecker::visitInlineableAttr(InlineableAttr *attr) {
// @_inlineable cannot be applied to stored properties.
//
// If the type is fixed-layout, the accessors are inlineable anyway;
// if the type is resilient, the accessors cannot be inlineable
// because clients cannot directly access storage.
if (auto *VD = dyn_cast<VarDecl>(D)) {
if (VD->hasStorage() || VD->getAttrs().hasAttribute<LazyAttr>()) {
TC.diagnose(attr->getLocation(),
diag::inlineable_stored_property)
.fixItRemove(attr->getRangeWithAt());
attr->setInvalid();
}
}
auto *VD = cast<ValueDecl>(D);
// @_inlineable can only be applied to public or @_versioned
// declarations.
auto access = VD->getFormalAccess(/*useDC=*/nullptr,
/*respectVersionedAttr=*/true);
if (access < AccessLevel::Public) {
TC.diagnose(attr->getLocation(),
diag::inlineable_decl_not_public,
VD->getBaseName(),
access)
.fixItRemove(attr->getRangeWithAt());
attr->setInvalid();
return;
}
}
void AttributeChecker::visitDiscardableResultAttr(DiscardableResultAttr *attr) {
if (auto *FD = dyn_cast<FuncDecl>(D)) {
if (auto result = FD->getResultInterfaceType()) {
auto resultIsVoid = result->isVoid();
if (resultIsVoid || result->isUninhabited()) {
auto warn = diag::discardable_result_on_void_never_function;
auto diagnostic = TC.diagnose(D->getStartLoc(), warn, resultIsVoid);
diagnostic.fixItRemove(attr->getRangeWithAt());
attr->setInvalid();
}
}
}
}
void AttributeChecker::visitImplementsAttr(ImplementsAttr *attr) {
TypeLoc &ProtoTypeLoc = attr->getProtocolType();
TypeResolutionOptions options;
options |= TR_AllowUnboundGenerics;
DeclContext *DC = D->getDeclContext();
Type T = TC.resolveType(ProtoTypeLoc.getTypeRepr(),
DC, options);
ProtoTypeLoc.setType(T);
// Definite error-types were already diagnosed in resolveType.
if (!T || T->hasError())
return;
// Check that we got a ProtocolType.
if (auto PT = T->getAs<ProtocolType>()) {
ProtocolDecl *PD = PT->getDecl();
// Check that the ProtocolType has the specified member.
LookupResult R = TC.lookupMember(PD->getDeclContext(),
PT, attr->getMemberName());
if (!R) {
TC.diagnose(attr->getLocation(),
diag::implements_attr_protocol_lacks_member,
PD->getBaseName(), attr->getMemberName())
.highlight(attr->getMemberNameLoc().getSourceRange());
}
// Check that the decl we're decorating is a member of a type that actually
// conforms to the specified protocol.
NominalTypeDecl *NTD = DC->getAsNominalTypeOrNominalTypeExtensionContext();
SmallVector<ProtocolConformance *, 2> conformances;
if (!NTD->lookupConformance(DC->getParentModule(), PD, conformances)) {
TC.diagnose(attr->getLocation(),
diag::implements_attr_protocol_not_conformed_to,
NTD->getFullName(), PD->getFullName())
.highlight(ProtoTypeLoc.getTypeRepr()->getSourceRange());
}
} else {
TC.diagnose(attr->getLocation(),
diag::implements_attr_non_protocol_type)
.highlight(ProtoTypeLoc.getTypeRepr()->getSourceRange());
}
}
void TypeChecker::checkDeclAttributes(Decl *D) {
AttributeChecker Checker(*this, D);
for (auto attr : D->getAttrs()) {
if (attr->isValid())
Checker.visit(attr);
}
}
void TypeChecker::checkTypeModifyingDeclAttributes(VarDecl *var) {
if (!var->hasType())
return;
if (auto *attr = var->getAttrs().getAttribute<OwnershipAttr>())
checkOwnershipAttr(var, attr);
}
void TypeChecker::checkOwnershipAttr(VarDecl *var, OwnershipAttr *attr) {
Type type = var->getType();
Type interfaceType = var->getInterfaceType();
// Just stop if we've already processed this declaration.
if (type->is<ReferenceStorageType>())
return;
auto ownershipKind = attr->get();
// A weak variable must have type R? or R! for some ownership-capable type R.
Type underlyingType = type;
switch (ownershipKind) {
case Ownership::Strong:
llvm_unreachable("Cannot specify 'strong' in an ownership attribute");
case Ownership::Unowned:
case Ownership::Unmanaged:
break;
case Ownership::Weak:
if (var->isLet()) {
diagnose(var->getStartLoc(), diag::invalid_weak_let);
attr->setInvalid();
}
if (Type objType = type->getAnyOptionalObjectType()) {
underlyingType = objType;
} else {
// @IBOutlet must be optional, but not necessarily weak. Let it diagnose.
if (!var->getAttrs().hasAttribute<IBOutletAttr>()) {
diagnose(var->getStartLoc(), diag::invalid_weak_ownership_not_optional,
OptionalType::get(type));
}
attr->setInvalid();
}
break;
}
if (!underlyingType->allowsOwnership()) {
auto D = diag::invalid_ownership_type;
if (underlyingType->isExistentialType() ||
underlyingType->is<ArchetypeType>()) {
// Suggest the possibility of adding a class bound.
D = diag::invalid_ownership_protocol_type;
}
diagnose(var->getStartLoc(), D, (unsigned) ownershipKind, underlyingType);
attr->setInvalid();
}
if (attr->isInvalid())
return;
// Change the type to the appropriate reference storage type.
var->setType(ReferenceStorageType::get(
type, ownershipKind, Context));
var->setInterfaceType(ReferenceStorageType::get(
interfaceType, ownershipKind, Context));
}
Optional<Diag<>>
TypeChecker::diagnosticIfDeclCannotBePotentiallyUnavailable(const Decl *D) {
DeclContext *DC = D->getDeclContext();
// Do not permit potential availability of script-mode global variables;
// their initializer expression is not lazily evaluated, so this would
// not be safe.
if (isa<VarDecl>(D) && DC->isModuleScopeContext() &&
DC->getParentSourceFile()->isScriptMode()) {
return diag::availability_global_script_no_potential;
}
// For now, we don't allow stored properties to be potentially unavailable.
// We will want to support these eventually, but we haven't figured out how
// this will interact with Definite Initialization, deinitializers and
// resilience yet.
if (auto *VD = dyn_cast<VarDecl>(D)) {
// Globals and statics are lazily initialized, so they are safe
// for potential unavailability. Note that if D is a global in script
// mode (which are not lazy) then we will already have returned
// a diagnosis above.
bool lazilyInitializedStored = VD->isStatic() ||
VD->getAttrs().hasAttribute<LazyAttr>() ||
DC->isModuleScopeContext();
if (VD->hasStorage() && !lazilyInitializedStored) {
return diag::availability_stored_property_no_potential;
}
}
return None;
}