Skip to content

[SPI] Requestify, inherit SPI groups and better diagnostics #30215

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Mar 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/swift/AST/ASTTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,4 @@ SWIFT_TYPEID_NAMED(TypeAliasDecl *, TypeAliasDecl)
SWIFT_TYPEID_NAMED(ValueDecl *, ValueDecl)
SWIFT_TYPEID_NAMED(VarDecl *, VarDecl)
SWIFT_TYPEID(FingerprintAndMembers)
SWIFT_TYPEID(Identifier)
1 change: 1 addition & 0 deletions include/swift/AST/ASTTypeIDs.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ struct TypeWitnessAndDecl;
enum class AncestryFlags : uint8_t;
enum class ImplicitMemberAction : uint8_t;
struct FingerprintAndMembers;
class Identifier;

// Define the AST type zone (zone 1)
#define SWIFT_TYPEID_ZONE AST
Expand Down
22 changes: 17 additions & 5 deletions include/swift/AST/AccessScope.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ namespace swift {
/// a particular declaration can be accessed.
class AccessScope {
/// The declaration context (if not public) along with a bit saying
/// whether this scope is private (or not).
/// whether this scope is private, SPI or not.
/// If the declaration context is set, the bit means that the scope is
/// private or not. If the declaration context is null, the bit means that
/// this scope is SPI or not.
llvm::PointerIntPair<const DeclContext *, 1, bool> Value;

public:
AccessScope(const DeclContext *DC, bool isPrivate = false);
AccessScope(const DeclContext *DC, bool isPrivate = false, bool isSPI = false);

static AccessScope getPublic() { return AccessScope(nullptr); }
static AccessScope getPublic(bool isSPI = false) { return AccessScope(nullptr, false, isSPI); }

/// Check if private access is allowed. This is a lexical scope check in Swift
/// 3 mode. In Swift 4 mode, declarations and extensions of the same type will
Expand All @@ -46,18 +50,24 @@ class AccessScope {
}

bool isPublic() const { return !Value.getPointer(); }
bool isPrivate() const { return Value.getInt(); }
bool isPrivate() const { return Value.getPointer() && Value.getInt(); }
bool isFileScope() const;
bool isInternal() const;

// Is this a public scope
bool isSPI() const { return !Value.getPointer() && Value.getInt(); }

/// Returns true if this is a child scope of the specified other access scope.
///
/// \see DeclContext::isChildContextOf
bool isChildOf(AccessScope AS) const {
if (!isPublic() && !AS.isPublic())
return allowsPrivateAccess(getDeclContext(), AS.getDeclContext());
if (isPublic() && AS.isPublic())
if (isPublic() && AS.isPublic()) {
if (isSPI() != AS.isSPI())
return isSPI();
return false;
}
return AS.isPublic();
}

Expand All @@ -76,6 +86,8 @@ class AccessScope {
/// have common intersection, or None if scopes don't intersect.
const Optional<AccessScope> intersectWith(AccessScope accessScope) const {
if (hasEqualDeclContextWith(accessScope)) {
if (isSPI())
return *this;
if (isPrivate())
return *this;
return accessScope;
Expand Down
5 changes: 3 additions & 2 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -541,8 +541,9 @@ DECL_ATTR(derivative, Derivative,
97)

DECL_ATTR(_spi, SPIAccessControl,
OnFunc | OnExtension | OnGenericType | OnVar | OnSubscript | OnConstructor |
OnImport | AllowMultipleAttributes | UserInaccessible |
OnAbstractFunction | OnExtension | OnGenericType | OnVar | OnSubscript |
OnImport | OnAccessor | OnEnumElement |
AllowMultipleAttributes | UserInaccessible |
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
98)

Expand Down
10 changes: 10 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,16 @@ class alignas(1 << DeclAlignInBits) Decl {
/// @_originalDefinedIn attribute, this function returns this module name.
StringRef getAlternateModuleName() const;

// Is this Decl an SPI? It can be directly marked with @_spi or is defined in
// an @_spi context.
bool isSPI() const;

// List the SPI groups declared with @_spi or inherited by this decl.
//
// SPI groups are inherited from the parent contexts only if the local decl
// doesn't declare any @_spi.
ArrayRef<Identifier> getSPIGroups() const;

/// Emit a diagnostic tied to this declaration.
template<typename ...ArgTypes>
InFlightDiagnostic diagnose(
Expand Down
35 changes: 15 additions & 20 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -143,17 +143,17 @@ FIXIT(insert_type_qualification,"%0.",(Type))

ERROR(candidate_inaccessible,none,
"%0 is inaccessible due to "
"'%select{private|fileprivate|internal|SPI|SPI}1' protection level",
"'%select{private|fileprivate|internal|@_spi|@_spi}1' protection level",
(DeclBaseName, AccessLevel))

NOTE(note_candidate_inaccessible,none,
"%0 is inaccessible due to "
"'%select{private|fileprivate|internal|%error|%error}1' protection level",
"'%select{private|fileprivate|internal|@_spi|@_spi}1' protection level",
(DeclName, AccessLevel))

ERROR(init_candidate_inaccessible,none,
"%0 initializer is inaccessible due to "
"'%select{private|fileprivate|internal|SPI|SPI}1' protection level",
"'%select{private|fileprivate|internal|@_spi|@_spi}1' protection level",
(Type, AccessLevel))

ERROR(cannot_pass_rvalue_mutating_subelement,none,
Expand Down Expand Up @@ -1649,15 +1649,15 @@ ERROR(function_type_access,none,
"|cannot be declared "
"%select{in this context|fileprivate|internal|public|open}1}0 "
"because its %select{parameter|result}5 uses "
"%select{a private|a fileprivate|an internal|%error|%error}3 type",
"%select{a private|a fileprivate|an internal|an '@_spi'|an '@_spi'}3 type",
(bool, AccessLevel, bool, AccessLevel, unsigned, bool))
ERROR(function_type_spi,none,
"%select{function|method|initializer}0 "
"cannot be declared '@_spi' "
"because its %select{parameter|result}1 uses "
"%select{a private|a fileprivate|an internal|%error|%error}2 type "
"without a compatible '@_spi'",
(unsigned, bool, AccessLevel))
"%select{a private|a fileprivate|an internal|a public|an open}2 type"
"%select{| that is not '@_spi'}3",
(unsigned, bool, AccessLevel, bool))
WARNING(function_type_access_warn,none,
"%select{function|method|initializer}4 "
"%select{should be declared %select{private|fileprivate|internal|%error|%error}1"
Expand Down Expand Up @@ -2295,14 +2295,14 @@ ERROR(generic_param_access,none,
"|cannot be declared "
"%select{in this context|fileprivate|internal|public|open}2}1 "
"because its generic %select{parameter|requirement}5 uses "
"%select{a private|a fileprivate|an internal|%error|%error}3 type",
"%select{a private|a fileprivate|an internal|an '@_spi'|an '@_spi'}3 type",
(DescriptiveDeclKind, bool, AccessLevel, AccessLevel, bool, bool))
WARNING(generic_param_access_warn,none,
"%0 %select{should be declared "
"%select{private|fileprivate|internal|%error|%error}3"
"|should not be declared %select{in this context|fileprivate|internal|public|open}2}1 "
"because its generic %select{parameter|requirement}5 uses "
"%select{a private|a fileprivate|an internal|%error|%error}3 type",
"%select{a private|a fileprivate|an internal|an '@_spi'|an '@_spi'}3 type",
(DescriptiveDeclKind, bool, AccessLevel, AccessLevel, bool, bool))
ERROR(generic_param_usable_from_inline,none,
"type referenced from a "
Expand Down Expand Up @@ -3873,17 +3873,17 @@ ERROR(class_super_access,none,
"%select{private|fileprivate|internal|%error|%error}1|private or fileprivate}3"
"|cannot be declared %select{in this context|fileprivate|internal|public|open}1}0 "
"because its superclass "
"%select{is %select{private|fileprivate|internal|%error|%error}2"
"|uses %select{a private|a fileprivate|an internal|%error|%error}2 "
"%select{is %select{private|fileprivate|internal|'@_spi'|'@_spi'}2"
"|uses %select{a private|a fileprivate|an internal|an '@_spi'|an '@_spi'}2 "
"type as a generic parameter}4",
(bool, AccessLevel, AccessLevel, bool, bool))
WARNING(class_super_access_warn,none,
"class %select{should be declared "
"%select{private|fileprivate|internal|%error|%error}1"
"|should not be declared %select{in this context|fileprivate|internal|public|open}1}0 "
"because its superclass "
"%select{is %select{private|fileprivate|internal|%error|%error}2"
"|uses %select{a private|a fileprivate|an internal|%error|%error}2 "
"%select{is %select{private|fileprivate|internal|'@_spi'|'@_spi'}2"
"|uses %select{a private|a fileprivate|an internal|an '@_spi'|an '@_spi'}2 "
"type as a generic parameter}4",
(bool, AccessLevel, AccessLevel, bool, bool))
ERROR(class_super_not_usable_from_inline,none,
Expand Down Expand Up @@ -4594,20 +4594,15 @@ ERROR(local_type_in_inlinable_function,
(DeclName, unsigned))

ERROR(resilience_decl_unavailable,
none, DECL_OR_ACCESSOR "4 %1 is %select{private|fileprivate|internal|%error|%error}2 and "
none, DECL_OR_ACCESSOR "4 %1 is %select{private|fileprivate|internal|'@_spi'|'@_spi'}2 and "
"cannot be referenced from " FRAGILE_FUNC_KIND "3",
(DescriptiveDeclKind, DeclName, AccessLevel, unsigned, bool))

WARNING(resilience_decl_unavailable_warn,
none, DECL_OR_ACCESSOR "4 %1 is %select{private|fileprivate|internal|%error|%error}2 and "
none, DECL_OR_ACCESSOR "4 %1 is %select{private|fileprivate|internal|'@_spi'|'@_spi'}2 and "
"should not be referenced from " FRAGILE_FUNC_KIND "3",
(DescriptiveDeclKind, DeclName, AccessLevel, unsigned, bool))

ERROR(resilience_decl_unavailable_spi,
none, DECL_OR_ACCESSOR "4 %1 is imported as SPI; "
"it cannot be referenced from " FRAGILE_FUNC_KIND "3",
(DescriptiveDeclKind, DeclName, AccessLevel, unsigned, bool))

ERROR(inlinable_decl_ref_from_hidden_module,
none, "%0 %1 cannot be used in " FRAGILE_FUNC_KIND "2 "
"because %select{%3 was imported implementation-only|"
Expand Down
20 changes: 20 additions & 0 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -2025,6 +2025,26 @@ class PatternTypeRequest
}
};

/// List SPI group ids declared on a decl.
class SPIGroupsRequest :
public SimpleRequest<SPIGroupsRequest,
llvm::ArrayRef<Identifier>(const Decl *),
CacheKind::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

// Evaluation.
llvm::Expected<llvm::ArrayRef<Identifier>>
evaluate(Evaluator &evaluator, const Decl *decl) const;

public:
bool isCached() const { return true; }
};


/// Type-checks a `@differentiable` attribute and returns the resolved parameter
/// indices on success. On failure, emits diagnostics and returns `nullptr`.
///
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ SWIFT_REQUEST(TypeChecker, PreCheckFunctionBuilderRequest,
SWIFT_REQUEST(TypeChecker, ResolveImplicitMemberRequest,
bool(NominalTypeDecl *, ImplicitMemberAction), Uncached,
NoLocationInfo)
SWIFT_REQUEST(TypeChecker, SPIGroupsRequest,
llvm::ArrayRef<Identifier>(Decl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, SynthesizeMemberwiseInitRequest,
ConstructorDecl *(NominalTypeDecl *), Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, HasDefaultInitRequest,
Expand Down
23 changes: 16 additions & 7 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,11 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr,
if (D->getAttrs().hasAttribute<ImplementationOnlyAttr>())
return false;

// Skip anything that isn't 'public' or '@usableFromInline',
// or SPI if desired.
// Skip SPI decls if `PrintSPIs`.
if (!options.PrintSPIs && D->isSPI())
return false;

// Skip anything that isn't 'public' or '@usableFromInline'.
if (auto *VD = dyn_cast<ValueDecl>(D)) {
if (!isPublicOrUsableFromInline(VD)) {
// We do want to print private stored properties, without their
Expand All @@ -155,11 +158,6 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr,
if (contributesToParentTypeStorage(ASD))
return true;

// Always print SPI decls if `PrintSPIs`.
if (options.PrintSPIs &&
VD->getAttrs().hasAttribute<SPIAccessControlAttr>())
return true;

return false;
}
}
Expand Down Expand Up @@ -991,6 +989,17 @@ void PrintAST::printAttributes(const Decl *D) {
}
}

// SPI groups
if (Options.PrintSPIs) {
interleave(D->getSPIGroups(),
[&](Identifier spiName) {
Printer.printAttrName("_spi", true);
Printer << "(" << spiName << ") ";
},
[&] { Printer << ""; });
Options.ExcludeAttrList.push_back(DAK_SPIAccessControl);
}

// Don't print any contextual decl modifiers.
// We will handle 'mutating' and 'nonmutating' separately.
if (isa<AccessorDecl>(D)) {
Expand Down
Loading