Skip to content

[NCGenerics] add ~Escapable #70277

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 1 commit into from
Dec 10, 2023
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
18 changes: 6 additions & 12 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2680,19 +2680,12 @@ class ValueDecl : public Decl {
/// Whether this declaration produces an implicitly unwrapped
/// optional result.
unsigned isIUO : 1;

/// Whether the "isEscapable" bit has been computed yet.
unsigned isEscapable : 1;

/// Whether this declaration is escapable.
unsigned isEscapableComputed : 1;
} LazySemanticInfo = { };

friend class DynamicallyReplacedDeclRequest;
friend class OverriddenDeclsRequest;
friend class IsObjCRequest;
friend class IsFinalRequest;
friend class IsEscapableRequest;
friend class IsDynamicRequest;
friend class IsImplicitlyUnwrappedOptionalRequest;
friend class InterfaceTypeRequest;
Expand Down Expand Up @@ -2980,9 +2973,6 @@ class ValueDecl : public Decl {
/// Is this declaration 'final'?
bool isFinal() const;

/// Is this declaration escapable?
bool isEscapable() const;

/// Is this declaration marked with 'dynamic'?
bool isDynamic() const;

Expand Down Expand Up @@ -3190,8 +3180,12 @@ class TypeDecl : public ValueDecl {
/// Type if it `isNoncopyable` instead of using this.
bool canBeNoncopyable() const;

/// Determine how the ~Copyable was applied to this TypeDecl, if at all.
InverseMarking getNoncopyableMarking() const;
/// Is this declaration escapable?
bool isEscapable() const;

/// Determine how the given invertible protocol was written on this TypeDecl,
/// if at all.
InverseMarking getMarking(InvertibleProtocolKind ip) const;

static bool classof(const Decl *D) {
return D->getKind() >= DeclKind::First_TypeDecl &&
Expand Down
23 changes: 13 additions & 10 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -7555,27 +7555,27 @@ ERROR(accessor_macro_not_single_var, none,
//------------------------------------------------------------------------------
// MARK: Noncopyable Types Diagnostics
//------------------------------------------------------------------------------
ERROR(noncopyable_but_copyable, none,
"%kind0 required to be 'Copyable' but is marked with '~Copyable'",
(const ValueDecl *))
ERROR(noncopyable_generic_but_copyable, none,
ERROR(inverse_but_also_conforms, none,
"%kind0 required to be '%1' but is marked with '~%1'",
(const ValueDecl *, StringRef))
ERROR(inverse_generic_but_also_conforms, none,
"%0 required to be '%1' but is marked with '~%1'",
(Type, StringRef))
WARNING(redundant_inverse_constraint,none,
"redundant constraint %0 : '~%1'", (Type, StringRef))
ERROR(noncopyable_class, none,
"classes cannot be noncopyable",
())
ERROR(inverse_on_class, none,
"classes cannot be '~%0'",
(StringRef))
ERROR(inverse_extension, none,
"cannot apply inverse %0 to extension",
(Type))
ERROR(copyable_illegal_deinit, none,
"deinitializer cannot be declared in %kind0 that conforms to 'Copyable'",
(const ValueDecl *))
ERROR(noncopyable_type_member_in_copyable,none,
ERROR(inverse_type_member_in_conforming_type,none,
"%select{stored property %2|associated value %2}1 of "
"'Copyable'-conforming %kind3 has noncopyable type %0",
(Type, bool, DeclName, const ValueDecl *))
"'%4'-conforming %kind3 has non-%4 type %0",
(Type, bool, DeclName, const ValueDecl *, StringRef))
NOTE(add_inverse,none,
"consider adding '~%1' to %kind0",
(const ValueDecl *, StringRef))
Expand All @@ -7592,6 +7592,9 @@ NOTE(note_inverse_preventing_conformance_explicit,none,
NOTE(add_explicit_protocol_for_conformance,none,
"consider making %kind0 explicitly conform to the '%1' protocol",
(const ValueDecl *, StringRef))
ERROR(escapable_requires_feature_flag,none,
"type '~Escapable' requires -enable-experimental-feature NonEscapableTypes",
())

// -- older ones below --

Expand Down
4 changes: 2 additions & 2 deletions include/swift/AST/InverseMarking.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

namespace swift {

class NoncopyableAnnotationRequest;
class InvertibleAnnotationRequest;

/// Describes the way an inverse and its corresponding positive contraint
/// appears on a TypeDecl, i.e., the way it was marked.
Expand Down Expand Up @@ -87,7 +87,7 @@ struct InverseMarking {
Mark positive;

// This friend initializes the marks.
friend NoncopyableAnnotationRequest;
friend InvertibleAnnotationRequest;
public:

// Creates an empty marking.
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/KnownProtocols.def
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ PROTOCOL(AsyncIteratorProtocol)
PROTOCOL(FloatingPoint)

INVERTIBLE_PROTOCOL_WITH_NAME(Copyable, "Copyable")
INVERTIBLE_PROTOCOL_WITH_NAME(Escapable, "Escapable")
PROTOCOL_(BitwiseCopyable)

EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByArrayLiteral, "Array", false)
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/KnownProtocols.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ llvm::Optional<InvertibleProtocolKind>
/// Returns the KnownProtocolKind corresponding to an InvertibleProtocolKind.
KnownProtocolKind getKnownProtocolKind(InvertibleProtocolKind ip);

void simple_display(llvm::raw_ostream &out,
const InvertibleProtocolKind &value);

} // end namespace swift

#endif
25 changes: 13 additions & 12 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -428,10 +428,12 @@ class IsFinalRequest :
void cacheResult(bool value) const;
};

/// Determine the kind of noncopyable marking present for this declaration.
class NoncopyableAnnotationRequest
: public SimpleRequest<NoncopyableAnnotationRequest,
InverseMarking(TypeDecl *),
/// Determine the kind of invertible protocol markings for this declaration,
/// for example, if a conformance to IP or ~IP was written on it in the
/// inheritance clause and/or where clause.
class InvertibleAnnotationRequest
: public SimpleRequest<InvertibleAnnotationRequest,
InverseMarking(TypeDecl *, InvertibleProtocolKind),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;
Expand All @@ -440,31 +442,30 @@ class NoncopyableAnnotationRequest
friend SimpleRequest;

// Evaluation.
InverseMarking evaluate(Evaluator &evaluator, TypeDecl *decl) const;
InverseMarking evaluate(Evaluator &evaluator,
TypeDecl *decl, InvertibleProtocolKind ip) const;

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

/// Determine whether the given declaration is escapable.
/// Determine whether the given type is Escapable.
class IsEscapableRequest
: public SimpleRequest<IsEscapableRequest, bool(ValueDecl *),
RequestFlags::SeparatelyCached> {
: public SimpleRequest<IsEscapableRequest, bool(CanType),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

// Evaluation.
bool evaluate(Evaluator &evaluator, ValueDecl *decl) const;
bool evaluate(Evaluator &evaluator, CanType) const;

public:
// Separate caching.
// Caching.
bool isCached() const { return true; }
llvm::Optional<bool> getCachedResult() const;
void cacheResult(bool value) const;
};

/// Determine whether the given type is noncopyable. Assumes type parameters
Expand Down
9 changes: 5 additions & 4 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -210,14 +210,15 @@ SWIFT_REQUEST(TypeChecker, IsDynamicRequest, bool(ValueDecl *),
SeparatelyCached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, IsFinalRequest, bool(ValueDecl *), SeparatelyCached,
NoLocationInfo)
SWIFT_REQUEST(TypeChecker, NoncopyableAnnotationRequest,
InverseMarking(TypeDecl *),
SWIFT_REQUEST(TypeChecker, InvertibleAnnotationRequest,
InverseMarking(TypeDecl *, InvertibleProtocolKind),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, IsNoncopyableRequest,
bool (CanType),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, IsEscapableRequest, bool(ValueDecl *),
SeparatelyCached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, IsEscapableRequest,
bool (CanType),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, IsGetterMutatingRequest, bool(AbstractStorageDecl *),
SeparatelyCached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, IsImplicitlyUnwrappedOptionalRequest,
Expand Down
10 changes: 6 additions & 4 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -640,11 +640,13 @@ class alignas(1 << TypeAlignInBits) TypeBase

bool isPlaceholder();

/// DEPRECIATED: Returns true if this is a noncopyable type.
bool isNoncopyable();
/// Returns true if this type lacks conformance to Copyable in the context,
/// if provided.
bool isNoncopyable(const DeclContext *dc = nullptr);

/// Returns true if this type lacks conformance to Copyable in the context.
bool isNoncopyable(const DeclContext *dc);
/// Returns true if this type conforms to Escapable in the context,
/// if provided.
bool isEscapable(const DeclContext *dc = nullptr);

/// Does the type have outer parenthesis?
bool hasParenSugar() const { return getKind() == TypeKind::Paren; }
Expand Down
8 changes: 8 additions & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ KnownProtocolKind swift::getKnownProtocolKind(InvertibleProtocolKind ip) {
}
}

void swift::simple_display(llvm::raw_ostream &out,
const InvertibleProtocolKind &value) {
out << getProtocolName(getKnownProtocolKind(value));
}

namespace {
enum class SearchPathKind : uint8_t {
Import = 1 << 0,
Expand Down Expand Up @@ -6243,6 +6248,9 @@ BuiltinTupleDecl *ASTContext::getBuiltinTupleDecl() {
if (auto *proto = getProtocol(KnownProtocolKind::Copyable))
buildFakeExtension(proto);

if (auto *proto = getProtocol(KnownProtocolKind::Escapable))
buildFakeExtension(proto);

return result;
}

Expand Down
3 changes: 3 additions & 0 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4348,6 +4348,9 @@ namespace {
case InvertibleProtocolKind::Copyable:
printFlag("inverse_copyable");
break;
case InvertibleProtocolKind::Escapable:
printFlag("inverse_escapable");
break;
}
}

Expand Down
36 changes: 21 additions & 15 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3367,8 +3367,9 @@ static bool usesFeatureFlowSensitiveConcurrencyCaptures(Decl *decl) {

/// \param isRelevantInverse the function used to inspect a mark corresponding
/// to an inverse to determine whether it "has" an inverse that we care about.
static bool hasInverseCopyable(
static bool hasInverse(
Decl *decl,
InvertibleProtocolKind ip,
std::function<bool(InverseMarking const&)> isRelevantInverse) {

auto getTypeDecl = [](Type type) -> TypeDecl* {
Expand All @@ -3381,37 +3382,38 @@ static bool hasInverseCopyable(

if (auto *extension = dyn_cast<ExtensionDecl>(decl)) {
if (auto *nominal = extension->getSelfNominalTypeDecl())
if (isRelevantInverse(nominal->getNoncopyableMarking()))
if (isRelevantInverse(nominal->getMarking(ip)))
return true;
}

if (auto typeDecl = dyn_cast<TypeDecl>(decl)) {
if (isRelevantInverse(typeDecl->getNoncopyableMarking()))
if (isRelevantInverse(typeDecl->getMarking(ip)))
return true;

// Check the protocol's associated types too.
if (auto proto = dyn_cast<ProtocolDecl>(decl)) {
auto hasNoncopyable = llvm::any_of(proto->getAssociatedTypeMembers(),
[&](AssociatedTypeDecl *assocTyDecl) {
return isRelevantInverse(assocTyDecl->getNoncopyableMarking());
});
if (hasNoncopyable)
auto hasInverse =
llvm::any_of(proto->getAssociatedTypeMembers(),
[&](AssociatedTypeDecl *assocTyDecl) {
return isRelevantInverse(assocTyDecl->getMarking(ip));
});
if (hasInverse)
return true;
}
}

if (auto value = dyn_cast<ValueDecl>(decl)) {
// Check for noncopyable types in the types of this declaration.
if (Type type = value->getInterfaceType()) {
bool hasNoncopyable = type.findIf([&](Type type) {
bool hasInverse = type.findIf([&](Type type) {
if (auto *typeDecl = getTypeDecl(type))
if (isRelevantInverse(typeDecl->getNoncopyableMarking()))
if (isRelevantInverse(typeDecl->getMarking(ip)))
return true;

return false;
});

if (hasNoncopyable)
if (hasInverse)
return true;
}
}
Expand All @@ -3420,7 +3422,8 @@ static bool hasInverseCopyable(
}

static bool usesFeatureMoveOnly(Decl *decl) {
return hasInverseCopyable(decl, [](auto &marking) -> bool {
return hasInverse(decl, InvertibleProtocolKind::Copyable,
[](auto &marking) -> bool {
return marking.getInverse().is(InverseMarking::Kind::LegacyExplicit);
});
}
Expand Down Expand Up @@ -3462,17 +3465,20 @@ static bool usesFeatureMoveOnlyPartialConsumption(Decl *decl) {
}

static bool usesFeatureNoncopyableGenerics(Decl *decl) {
return hasInverseCopyable(decl, [](auto &marking) -> bool {
auto checkMarking = [](auto &marking) -> bool {
switch (marking.getInverse().getKind()) {
case InverseMarking::Kind::None:
case InverseMarking::Kind::LegacyExplicit: // covered by MoveOnly
case InverseMarking::Kind::LegacyExplicit: // covered by other checks.
return false;

case InverseMarking::Kind::Explicit:
case InverseMarking::Kind::Inferred:
return true;
}
});
};

return hasInverse(decl, InvertibleProtocolKind::Copyable, checkMarking)
|| hasInverse(decl, InvertibleProtocolKind::Escapable, checkMarking);
}

static bool usesFeatureOneWayClosureParameters(Decl *decl) {
Expand Down
18 changes: 9 additions & 9 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3684,12 +3684,6 @@ bool ValueDecl::isFinal() const {
getAttrs().hasAttribute<FinalAttr>());
}

bool ValueDecl::isEscapable() const {
return evaluateOrDefault(getASTContext().evaluator,
IsEscapableRequest{const_cast<ValueDecl *>(this)},
!getAttrs().hasAttribute<NonEscapableAttr>());
}

bool ValueDecl::isDynamic() const {
ASTContext &ctx = getASTContext();
return evaluateOrDefault(ctx.evaluator,
Expand Down Expand Up @@ -4903,16 +4897,21 @@ GenericParameterReferenceInfo ValueDecl::findExistentialSelfReferences(
llvm::None);
}

InverseMarking TypeDecl::getNoncopyableMarking() const {
InverseMarking TypeDecl::getMarking(InvertibleProtocolKind ip) const {
return evaluateOrDefault(
getASTContext().evaluator,
NoncopyableAnnotationRequest{const_cast<TypeDecl *>(this)},
InvertibleAnnotationRequest{const_cast<TypeDecl *>(this), ip},
InverseMarking::forInverse(InverseMarking::Kind::None)
);
}

bool TypeDecl::canBeNoncopyable() const {
return getNoncopyableMarking().getInverse().isPresent();
return getMarking(InvertibleProtocolKind::Copyable).getInverse().isPresent();
}

bool TypeDecl::isEscapable() const {
auto escapable = getMarking(InvertibleProtocolKind::Escapable);
return !escapable.getInverse().isPresent();
}

Type TypeDecl::getDeclaredInterfaceType() const {
Expand All @@ -4926,6 +4925,7 @@ Type TypeDecl::getDeclaredInterfaceType() const {
return ErrorType::get(ctx);
return DependentMemberType::get(
selfTy, const_cast<AssociatedTypeDecl *>(ATD));

}

return getInterfaceType()->getMetatypeInstanceType();
Expand Down
Loading