Skip to content

Commit ab6734a

Browse files
authored
Merge pull request #69590 from kavon/check-if-copyable-inferred
[NoncopyableGenerics] Synthesize conditional conformances
2 parents a4e0d25 + 28890ba commit ab6734a

36 files changed

+706
-297
lines changed

include/swift/AST/Decl.h

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ namespace swift {
9494
class NamedPattern;
9595
class EnumCaseDecl;
9696
class EnumElementDecl;
97+
struct InverseMarking;
9798
class ParameterList;
9899
class ParameterTypeFlags;
99100
class Pattern;
@@ -3117,15 +3118,6 @@ class TypeDecl : public ValueDecl {
31173118
private:
31183119
ArrayRef<InheritedEntry> Inherited;
31193120

3120-
struct {
3121-
/// Whether the "hasNoncopyableAnnotation" bit has been computed yet.
3122-
unsigned isNoncopyableAnnotationComputed : 1;
3123-
3124-
/// Whether this declaration had a noncopyable inverse written somewhere.
3125-
unsigned hasNoncopyableAnnotation : 1;
3126-
} LazySemanticInfo = { };
3127-
friend class HasNoncopyableAnnotationRequest;
3128-
31293121
protected:
31303122
TypeDecl(DeclKind K, llvm::PointerUnion<DeclContext *, ASTContext *> context,
31313123
Identifier name, SourceLoc NameLoc,
@@ -3153,9 +3145,13 @@ class TypeDecl : public ValueDecl {
31533145

31543146
void setInherited(ArrayRef<InheritedEntry> i) { Inherited = i; }
31553147

3156-
/// Is this type _always_ noncopyable? Will answer 'false' if the type is
3157-
/// conditionally copyable.
3158-
bool isNoncopyable() const;
3148+
/// Is it possible for this type to lack a Copyable constraint?
3149+
/// If you need a more precise answer, ask this Decl's corresponding
3150+
/// Type if it `isNoncopyable` instead of using this.
3151+
bool canBeNoncopyable() const;
3152+
3153+
/// Determine how the ~Copyable was applied to this TypeDecl, if at all.
3154+
InverseMarking getNoncopyableMarking() const;
31593155

31603156
static bool classof(const Decl *D) {
31613157
return D->getKind() >= DeclKind::First_TypeDecl &&

include/swift/AST/DiagnosticsParse.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,8 +429,6 @@ ERROR(destructor_decl_outside_class_or_noncopyable,none,
429429
"deinitializers may only be declared within a class, actor, or noncopyable type", ())
430430
ERROR(destructor_decl_on_noncopyable_enum,none,
431431
"deinitializers are not yet supported on noncopyable enums", ())
432-
ERROR(destructor_decl_on_objc_enum,none,
433-
"deinitializers cannot be declared on an @objc enum type", ())
434432
ERROR(expected_lbrace_destructor,PointsToFirstBadToken,
435433
"expected '{' for deinitializer", ())
436434
ERROR(destructor_has_name,PointsToFirstBadToken,

include/swift/AST/DiagnosticsSema.def

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3170,7 +3170,7 @@ ERROR(recursive_superclass_constraint,none,
31703170
ERROR(requires_same_concrete_type,none,
31713171
"generic signature requires types %0 and %1 to be the same", (Type, Type))
31723172
WARNING(redundant_conformance_constraint,none,
3173-
"redundant conformance constraint %0 : %1", (Type, ProtocolDecl *))
3173+
"redundant conformance constraint %0 : %1", (Type, Type))
31743174

31753175
WARNING(missing_protocol_refinement, none,
31763176
"protocol %0 should be declared to refine %1 due to a same-type constraint on 'Self'",
@@ -7533,22 +7533,34 @@ ERROR(accessor_macro_not_single_var, none,
75337533
//------------------------------------------------------------------------------
75347534
// MARK: Noncopyable Types Diagnostics
75357535
//------------------------------------------------------------------------------
7536-
7536+
ERROR(noncopyable_but_copyable, none,
7537+
"%kind0 required to be 'Copyable' but is marked with '~Copyable'",
7538+
(const ValueDecl *))
75377539
ERROR(noncopyable_class, none,
75387540
"classes cannot be noncopyable",
75397541
())
7542+
ERROR(inverse_extension, none,
7543+
"cannot apply inverse %0 to extension",
7544+
(Type))
7545+
ERROR(copyable_illegal_deinit, none,
7546+
"deinitializer cannot be declared in %kind0 that conforms to 'Copyable'",
7547+
(const ValueDecl *))
75407548
ERROR(noncopyable_type_member_in_copyable,none,
75417549
"%select{stored property %2|associated value %2}1 of "
75427550
"'Copyable'-conforming %kind3 has noncopyable type %0",
75437551
(Type, bool, DeclName, const ValueDecl *))
7544-
NOTE(add_inverse_for_containment,none,
7545-
"consider removing implicit '%1' conformance from %kind0",
7552+
NOTE(add_inverse,none,
7553+
"consider adding '~%1' to %kind0",
75467554
(const ValueDecl *, StringRef))
7547-
NOTE(remove_inverse_on_generic_parameter_for_conformance,none,
7548-
"consider removing '~%1' from generic parameter %0 so it conforms to the '%1' protocol",
7555+
NOTE(note_inverse_preventing_conformance,none,
7556+
"%0 has '~%1' constraint preventing implicit '%1' conformance",
75497557
(Type, StringRef))
7550-
NOTE(remove_inverse_on_nominal_for_conformance,none,
7551-
"consider removing '~%1' from %kind0 so it conforms to the '%1' protocol",
7558+
NOTE(note_inverse_preventing_conformance_implicit,none,
7559+
"%kind0 has '~%1' constraint on a generic parameter, "
7560+
"making its '%1' conformance conditional",
7561+
(const ValueDecl *, StringRef))
7562+
NOTE(note_inverse_preventing_conformance_explicit,none,
7563+
"%kind0 has '~%1' constraint preventing '%1' conformance",
75527564
(const ValueDecl *, StringRef))
75537565
NOTE(add_explicit_protocol_for_conformance,none,
75547566
"consider making %kind0 explicitly conform to the '%1' protocol",

include/swift/AST/InverseMarking.h

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//===- InverseMarking.h - Utilities for tracking inverse types -*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_LIB_SEMA_INVERSEMARKING_H
14+
#define SWIFT_LIB_SEMA_INVERSEMARKING_H
15+
16+
#include "swift/AST/KnownProtocols.h"
17+
#include "swift/Basic/SourceLoc.h"
18+
#include "swift/Basic/OptionalEnum.h"
19+
20+
namespace swift {
21+
22+
/// Describes the way an inverse and its corresponding positive contraint
23+
/// appears on a TypeDecl, i.e., the way it was marked.
24+
struct InverseMarking {
25+
enum class Kind : uint8_t {
26+
None, // No inverse marking is present
27+
Inferred, // Inverse is inferred based on generic parameters.
28+
Explicit, // Inverse is explicitly present.
29+
30+
LAST = Explicit
31+
};
32+
33+
// Describes what kind of mark was found, if any.
34+
struct Mark {
35+
private:
36+
OptionalEnum<Kind> kind;
37+
SourceLoc loc;
38+
public:
39+
// Creates an empty mark.
40+
Mark() {};
41+
42+
// Creates a mark.
43+
Mark(Kind k, SourceLoc l = SourceLoc())
44+
: kind(k), loc(l) {};
45+
46+
// Is there an inferred or explicit marking?
47+
bool isPresent() const {
48+
return getKind() != Kind::None;
49+
}
50+
operator bool() { return isPresent(); }
51+
52+
Kind getKind() const {
53+
return kind.getValueOr(Kind::None);
54+
}
55+
56+
SourceLoc getLoc() const { return loc; }
57+
58+
void set(Kind k, SourceLoc l = SourceLoc()) {
59+
assert(!kind.hasValue());
60+
kind = k;
61+
loc = l;
62+
}
63+
64+
void setIfUnset(Kind k, SourceLoc l = SourceLoc()) {
65+
if (kind.hasValue())
66+
return;
67+
set(k, l);
68+
}
69+
70+
void setIfUnset(Mark other) {
71+
if (kind.hasValue())
72+
return;
73+
kind = other.kind;
74+
loc = other.loc;
75+
}
76+
77+
Mark with(Kind k) {
78+
kind = k;
79+
return *this;
80+
}
81+
};
82+
83+
private:
84+
Mark inverse;
85+
Mark positive;
86+
public:
87+
88+
// Creates an empty marking.
89+
InverseMarking() {}
90+
91+
Mark &getInverse() { return inverse; }
92+
Mark &getPositive() { return positive; }
93+
94+
// Merge the results of another marking into this one.
95+
void merge(InverseMarking other) const {
96+
other.inverse.setIfUnset(other.inverse);
97+
other.positive.setIfUnset(other.positive);
98+
}
99+
100+
static InverseMarking forInverse(Kind kind, SourceLoc loc = SourceLoc()) {
101+
InverseMarking marking;
102+
marking.inverse.set(kind, loc);
103+
marking.positive.set(Kind::None);
104+
return marking;
105+
}
106+
};
107+
108+
}
109+
110+
#endif //SWIFT_LIB_SEMA_INVERSEMARKING_H

include/swift/AST/TypeCheckRequests.h

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -427,25 +427,23 @@ class IsFinalRequest :
427427
void cacheResult(bool value) const;
428428
};
429429

430-
/// Determine whether the given declaration has any markings that would cause it
431-
/// to not have an implicit, unconditional conformance to Copyable.
432-
class HasNoncopyableAnnotationRequest
433-
: public SimpleRequest<HasNoncopyableAnnotationRequest, bool(TypeDecl *),
434-
RequestFlags::SeparatelyCached> {
430+
/// Determine the kind of noncopyable marking present for this declaration.
431+
class NoncopyableAnnotationRequest
432+
: public SimpleRequest<NoncopyableAnnotationRequest,
433+
InverseMarking(TypeDecl *),
434+
RequestFlags::Cached> {
435435
public:
436436
using SimpleRequest::SimpleRequest;
437437

438438
private:
439439
friend SimpleRequest;
440440

441441
// Evaluation.
442-
bool evaluate(Evaluator &evaluator, TypeDecl *decl) const;
442+
InverseMarking evaluate(Evaluator &evaluator, TypeDecl *decl) const;
443443

444444
public:
445-
// Separate caching.
445+
// Caching.
446446
bool isCached() const { return true; }
447-
llvm::Optional<bool> getCachedResult() const;
448-
void cacheResult(bool value) const;
449447
};
450448

451449
/// Determine whether the given declaration is escapable.

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,9 @@ SWIFT_REQUEST(TypeChecker, IsDynamicRequest, bool(ValueDecl *),
210210
SeparatelyCached, NoLocationInfo)
211211
SWIFT_REQUEST(TypeChecker, IsFinalRequest, bool(ValueDecl *), SeparatelyCached,
212212
NoLocationInfo)
213-
SWIFT_REQUEST(TypeChecker, HasNoncopyableAnnotationRequest, bool(TypeDecl *), SeparatelyCached,
214-
NoLocationInfo)
213+
SWIFT_REQUEST(TypeChecker, NoncopyableAnnotationRequest,
214+
InverseMarking(TypeDecl *),
215+
Cached, NoLocationInfo)
215216
SWIFT_REQUEST(TypeChecker, IsNoncopyableRequest,
216217
bool (CanType),
217218
Cached, NoLocationInfo)

lib/AST/ASTPrinter.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3324,12 +3324,12 @@ static bool usesFeatureFlowSensitiveConcurrencyCaptures(Decl *decl) {
33243324
static bool usesFeatureMoveOnly(Decl *decl) {
33253325
if (auto *extension = dyn_cast<ExtensionDecl>(decl)) {
33263326
if (auto *nominal = extension->getSelfNominalTypeDecl())
3327-
if (nominal->isNoncopyable())
3327+
if (nominal->canBeNoncopyable())
33283328
return true;
33293329
}
33303330

33313331
if (auto typeDecl = dyn_cast<TypeDecl>(decl)) {
3332-
if (typeDecl->isNoncopyable())
3332+
if (typeDecl->canBeNoncopyable())
33333333
return true;
33343334
}
33353335

lib/AST/ASTVerifier.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3238,7 +3238,7 @@ class Verifier : public ASTWalker {
32383238
PrettyStackTraceDecl debugStack("verifying DestructorDecl", DD);
32393239

32403240
auto *ND = DD->getDeclContext()->getSelfNominalTypeDecl();
3241-
if (!isa<ClassDecl>(ND) && !ND->isNoncopyable() && !DD->isInvalid()) {
3241+
if (!isa<ClassDecl>(ND) && !ND->canBeNoncopyable() && !DD->isInvalid()) {
32423242
Out << "DestructorDecls outside classes/move only types should be "
32433243
"marked invalid\n";
32443244
abort();

lib/AST/Decl.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "swift/AST/GenericEnvironment.h"
3232
#include "swift/AST/GenericSignature.h"
3333
#include "swift/AST/Initializer.h"
34+
#include "swift/AST/InverseMarking.h"
3435
#include "swift/AST/LazyResolver.h"
3536
#include "swift/AST/MacroDefinition.h"
3637
#include "swift/AST/MacroDiscriminatorContext.h"
@@ -4876,12 +4877,16 @@ GenericParameterReferenceInfo ValueDecl::findExistentialSelfReferences(
48764877
llvm::None);
48774878
}
48784879

4879-
bool TypeDecl::isNoncopyable() const {
4880-
// NOTE: must answer true iff it is unconditionally noncopyable.
4881-
return evaluateOrDefault(getASTContext().evaluator,
4882-
HasNoncopyableAnnotationRequest{
4883-
const_cast<TypeDecl *>(this)},
4884-
true); // default to true for safety
4880+
InverseMarking TypeDecl::getNoncopyableMarking() const {
4881+
return evaluateOrDefault(
4882+
getASTContext().evaluator,
4883+
NoncopyableAnnotationRequest{const_cast<TypeDecl *>(this)},
4884+
InverseMarking::forInverse(InverseMarking::Kind::None)
4885+
);
4886+
}
4887+
4888+
bool TypeDecl::canBeNoncopyable() const {
4889+
return getNoncopyableMarking().getInverse().isPresent();
48854890
}
48864891

48874892
Type TypeDecl::getDeclaredInterfaceType() const {

lib/AST/Module.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2019,7 +2019,7 @@ LookupConformanceInModuleRequest::evaluate(
20192019
// We only need to do this until we are properly dealing with or
20202020
// omitting Copyable conformances in modules/interfaces.
20212021

2022-
if (nominal->isNoncopyable())
2022+
if (nominal->canBeNoncopyable())
20232023
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
20242024
else
20252025
return ProtocolConformanceRef(protocol);

lib/AST/ProtocolConformance.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1047,7 +1047,7 @@ void NominalTypeDecl::prepareConformanceTable() const {
10471047
return;
10481048

10491049
// No synthesized conformances for move-only nominals.
1050-
if (isNoncopyable()) {
1050+
if (canBeNoncopyable()) {
10511051
// assumption is Sendable gets synthesized elsewhere.
10521052
assert(!proto->isSpecificProtocol(KnownProtocolKind::Sendable));
10531053
return;

lib/AST/RequirementMachine/Diagnostics.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ bool swift::rewriting::diagnoseRequirementErrors(
227227
case RequirementKind::Conformance:
228228
ctx.Diags.diagnose(loc, diag::redundant_conformance_constraint,
229229
requirement.getFirstType(),
230-
requirement.getProtocolDecl());
230+
requirement.getSecondType());
231231
break;
232232
case RequirementKind::Superclass:
233233
ctx.Diags.diagnose(loc, diag::redundant_superclass_constraint,

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -823,8 +823,19 @@ void swift::rewriting::expandDefaultRequirements(ASTContext &ctx,
823823
return true;
824824
}
825825

826+
auto currentDefaults = defaults[subject];
827+
auto inverseKind = inverse->getInverseKind();
828+
829+
// Check if this inverse is redundant.
830+
if (!currentDefaults.contains(inverseKind)) {
831+
errors.push_back(
832+
RequirementError::forRedundantRequirement(req, structReq.loc));
833+
return true;
834+
}
835+
826836
// Apply the inverse to the subject's defaults.
827-
defaults[subject].remove(inverse->getInverseKind());
837+
currentDefaults.remove(inverseKind);
838+
defaults[subject] = currentDefaults;
828839

829840
// Remove this inverse conformance requirement.
830841
return true;

lib/AST/SwiftNameTranslation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ swift::cxx_translation::getDeclRepresentation(const ValueDecl *VD) {
234234
if (isa<ProtocolDecl>(typeDecl))
235235
return {Unsupported, UnrepresentableProtocol};
236236
// Swift's consume semantics are not yet supported in C++.
237-
if (typeDecl->isNoncopyable())
237+
if (typeDecl->canBeNoncopyable())
238238
return {Unsupported, UnrepresentableMoveOnly};
239239
if (typeDecl->isGeneric()) {
240240
if (isa<ClassDecl>(VD))

lib/AST/Type.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ bool TypeBase::isMarkerExistential() {
161161
/// that does not rely on conformances.
162162
static bool alwaysNoncopyable(Type ty) {
163163
if (auto *nominal = ty->getNominalOrBoundGenericNominal())
164-
return nominal->isNoncopyable();
164+
return nominal->canBeNoncopyable();
165165

166166
if (auto *expansion = ty->getAs<PackExpansionType>()) {
167167
return alwaysNoncopyable(expansion->getPatternType());

0 commit comments

Comments
 (0)