Skip to content

Commit 8a7fd33

Browse files
authored
Merge pull request #71560 from kavon/ncgenerics-test-fixes-kavon-v10
Ncgenerics test fixes kavon v10
2 parents 06d4d8c + 3a45393 commit 8a7fd33

File tree

9 files changed

+128
-53
lines changed

9 files changed

+128
-53
lines changed

include/swift/AST/InverseMarking.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ struct InverseMarking {
5757
}
5858
operator bool() const { return isPresent(); }
5959

60+
// Is there any kind of explicit marking?
61+
bool isAnyExplicit() const {
62+
return is(Kind::Explicit) || is(Kind::LegacyExplicit);
63+
}
64+
6065
SourceLoc getLoc() const { return loc; }
6166

6267
void set(Kind k, SourceLoc l = SourceLoc()) {

include/swift/AST/ProtocolConformance.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,19 @@ class BuiltinProtocolConformance final : public RootProtocolConformance {
11571157
return getBuiltinConformanceKind() == BuiltinConformanceKind::Missing;
11581158
}
11591159

1160+
bool isInvalid() const {
1161+
switch (getBuiltinConformanceKind()) {
1162+
case BuiltinConformanceKind::Synthesized:
1163+
return false;
1164+
case BuiltinConformanceKind::Missing:
1165+
return true;
1166+
}
1167+
}
1168+
1169+
SourceLoc getLoc() const {
1170+
return SourceLoc();
1171+
}
1172+
11601173
/// Get any requirements that must be satisfied for this conformance to apply.
11611174
llvm::Optional<ArrayRef<Requirement>>
11621175
getConditionalRequirementsIfAvailable() const {
@@ -1191,6 +1204,10 @@ class BuiltinProtocolConformance final : public RootProtocolConformance {
11911204
llvm_unreachable("builtin-conformances never have associated types");
11921205
}
11931206

1207+
bool hasWitness(ValueDecl *requirement) const {
1208+
llvm_unreachable("builtin-conformances never have requirement witnesses");
1209+
}
1210+
11941211
/// Retrieve the type witness and type decl (if one exists)
11951212
/// for the given associated type.
11961213
TypeWitnessAndDecl
@@ -1199,6 +1216,10 @@ class BuiltinProtocolConformance final : public RootProtocolConformance {
11991216
llvm_unreachable("builtin-conformances never have associated types");
12001217
}
12011218

1219+
Witness getWitness(ValueDecl *requirement) const {
1220+
llvm_unreachable("builtin-conformances never have requirement witnesses");
1221+
}
1222+
12021223
/// Given that the requirement signature of the protocol directly states
12031224
/// that the given dependent type must conform to the given protocol,
12041225
/// return its associated conformance.

lib/AST/ConformanceLookup.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "swift/AST/DiagnosticsSema.h"
3030
#include "swift/AST/ExistentialLayout.h"
3131
#include "swift/AST/GenericEnvironment.h"
32+
#include "swift/AST/InverseMarking.h"
3233
#include "swift/AST/NameLookup.h"
3334
#include "swift/AST/NameLookupRequests.h"
3435
#include "swift/AST/PackConformance.h"
@@ -402,6 +403,41 @@ static ProtocolConformanceRef getBuiltinMetaTypeTypeConformance(
402403
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
403404
}
404405

406+
static ProtocolConformanceRef
407+
getBuiltinInvertibleProtocolConformance(NominalTypeDecl *nominal,
408+
Type type,
409+
ProtocolDecl *protocol) {
410+
assert(isa<ClassDecl>(nominal));
411+
ASTContext &ctx = protocol->getASTContext();
412+
413+
auto ip = protocol->getInvertibleProtocolKind();
414+
switch (*ip) {
415+
case InvertibleProtocolKind::Copyable:
416+
// If move-only classes is enabled, we'll check the markings.
417+
if (ctx.LangOpts.hasFeature(Feature::MoveOnlyClasses)) {
418+
auto marking = nominal->getMarking(*ip);
419+
switch (marking.getInverse().getKind()) {
420+
case InverseMarking::Kind::LegacyExplicit:
421+
case InverseMarking::Kind::Explicit:
422+
// An inverse ~Copyable prevents conformance.
423+
return ProtocolConformanceRef::forInvalid();
424+
425+
case InverseMarking::Kind::Inferred: // ignore "inferred" inverse marking
426+
case InverseMarking::Kind::None:
427+
break;
428+
}
429+
}
430+
break;
431+
case InvertibleProtocolKind::Escapable:
432+
// Always conforms.
433+
break;
434+
}
435+
436+
return ProtocolConformanceRef(
437+
ctx.getBuiltinConformance(type, protocol,
438+
BuiltinConformanceKind::Synthesized));
439+
}
440+
405441
/// Synthesize a builtin type conformance to the given protocol, if
406442
/// appropriate.
407443
static ProtocolConformanceRef
@@ -472,6 +508,9 @@ LookupConformanceInModuleRequest::evaluate(
472508
auto *protocol = desc.PD;
473509
ASTContext &ctx = mod->getASTContext();
474510

511+
// Remove SIL reference ownership wrapper, if present.
512+
type = type->getReferenceStorageReferent();
513+
475514
// A dynamic Self type conforms to whatever its underlying type
476515
// conforms to.
477516
if (auto selfType = type->getAs<DynamicSelfType>())
@@ -588,6 +627,13 @@ LookupConformanceInModuleRequest::evaluate(
588627
if (!nominal || isa<ProtocolDecl>(nominal))
589628
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
590629

630+
// We specially avoid recording conformances to invertible protocols in a
631+
// class's conformance table. This prevents an evaluator cycle.
632+
if (ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)
633+
&& isa<ClassDecl>(nominal)
634+
&& protocol->getInvertibleProtocolKind())
635+
return getBuiltinInvertibleProtocolConformance(nominal, type, protocol);
636+
591637
// Expand conformances added by extension macros.
592638
//
593639
// FIXME: This expansion should only be done if the

lib/AST/ProtocolConformance.cpp

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,10 @@ switch (getKind()) { \
102102
return cast<NormalProtocolConformance>(this)->Method Args; \
103103
case ProtocolConformanceKind::Self: \
104104
return cast<SelfProtocolConformance>(this)->Method Args; \
105+
case ProtocolConformanceKind::Builtin: \
106+
return cast<BuiltinProtocolConformance>(this)->Method Args; \
105107
case ProtocolConformanceKind::Specialized: \
106108
case ProtocolConformanceKind::Inherited: \
107-
case ProtocolConformanceKind::Builtin: \
108109
llvm_unreachable("not a root conformance"); \
109110
} \
110111
llvm_unreachable("bad ProtocolConformanceKind");
@@ -1090,19 +1091,21 @@ void NominalTypeDecl::prepareConformanceTable() const {
10901091
// Synthesize the unconditional conformances to invertible protocols.
10911092
// For conditional ones, see findSynthesizedConformances .
10921093
if (ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
1093-
bool missingOne = false;
1094-
for (auto ip : InvertibleProtocolSet::full()) {
1095-
auto invertible = getMarking(ip);
1096-
if (!invertible.getInverse() || bool(invertible.getPositive()))
1097-
addSynthesized(ctx.getProtocol(getKnownProtocolKind(ip)));
1098-
else
1099-
missingOne = true;
1100-
}
1101-
1102-
// FIXME: rdar://122289155 (NCGenerics: convert Equatable, Hashable, and RawRepresentable to ~Copyable.)
1103-
if (missingOne)
1104-
return;
1094+
// Classes get their conformances during ModuleDecl::lookupConformance.
1095+
if (!isa<ClassDecl>(this)) {
1096+
bool missingOne = false;
1097+
for (auto ip : InvertibleProtocolSet::full()) {
1098+
auto invertible = getMarking(ip);
1099+
if (!invertible.getInverse() || bool(invertible.getPositive()))
1100+
addSynthesized(ctx.getProtocol(getKnownProtocolKind(ip)));
1101+
else
1102+
missingOne = true;
1103+
}
11051104

1105+
// FIXME: rdar://122289155 (NCGenerics: convert Equatable, Hashable, and RawRepresentable to ~Copyable.)
1106+
if (missingOne)
1107+
return;
1108+
}
11061109
} else if (!canBeCopyable()) {
11071110
return; // No synthesized conformances for move-only nominals.
11081111
}

lib/Frontend/Frontend.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,8 +1501,8 @@ bool CompilerInstance::loadStdlibIfNeeded() {
15011501

15021502
// If we failed to load, we should have already diagnosed.
15031503
if (M->failedToLoad()) {
1504-
// assert(Diagnostics.hadAnyError() &&
1505-
// "Module failed to load but nothing was diagnosed?");
1504+
assert(Diagnostics.hadAnyError() &&
1505+
"stdlib module failed to load but nothing was diagnosed?");
15061506
return true;
15071507
}
15081508
return false;

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3183,6 +3183,29 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
31833183
}
31843184
}
31853185

3186+
static void diagnoseInverseOnClass(ClassDecl *decl) {
3187+
auto &ctx = decl->getASTContext();
3188+
3189+
for (auto ip : InvertibleProtocolSet::full()) {
3190+
auto marking = decl->getMarking(ip);
3191+
3192+
// Inferred inverses are already ignored for classes.
3193+
// FIXME: we can also diagnose @_moveOnly here if we use `isAnyExplicit`
3194+
if (!marking.getInverse().is(InverseMarking::Kind::Explicit))
3195+
continue;
3196+
3197+
// Allow ~Copyable when MoveOnlyClasses is enabled
3198+
if (ip == InvertibleProtocolKind::Copyable
3199+
&& ctx.LangOpts.hasFeature(Feature::MoveOnlyClasses))
3200+
continue;
3201+
3202+
3203+
ctx.Diags.diagnose(marking.getInverse().getLoc(),
3204+
diag::inverse_on_class,
3205+
getProtocolName(getKnownProtocolKind(ip)));
3206+
}
3207+
}
3208+
31863209
/// check to see if a move-only type can ever conform to the given type.
31873210
/// \returns true iff a diagnostic was emitted because it was not compatible
31883211
static bool diagnoseIncompatibleWithMoveOnlyType(SourceLoc loc,
@@ -3415,6 +3438,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
34153438

34163439
diagnoseIncompatibleProtocolsForMoveOnlyType(CD);
34173440

3441+
diagnoseInverseOnClass(CD);
34183442
}
34193443

34203444
void visitProtocolDecl(ProtocolDecl *PD) {

lib/Sema/TypeCheckInvertible.cpp

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -171,20 +171,14 @@ static bool checkInvertibleConformanceCommon(ProtocolConformance *conformance,
171171
auto &ctx = nom->getASTContext();
172172
bool conforms = true;
173173

174-
// An explicit `~IP` prevents conformance if any of these are true:
174+
// An explicit `~IP` prevents conformance if it appears on the same
175+
// declaration that also declares the conformance.
175176
//
176-
// 1. It appears on a class.
177-
// 2. Appears on the same declaration that also declares the conformance.
178-
// So, if the nominal has `~Copyable` but this conformance is
179-
// written in an extension, then we do not raise an error.
177+
// So, if the nominal has `~Copyable` but this conformance is
178+
// written in an extension, then we do not raise an error.
180179
auto marking = nom->getMarking(ip);
181-
if (marking.getInverse().getKind() == InverseMarking::Kind::Explicit) {
182-
if (isa<ClassDecl>(nom)) {
183-
ctx.Diags.diagnose(marking.getInverse().getLoc(),
184-
diag::inverse_on_class,
185-
getProtocolName(kp));
186-
conforms &= false;
187-
} else if (conformance->getDeclContext() == nom) {
180+
if (marking.getInverse().isAnyExplicit()) {
181+
if (conformance->getDeclContext() == nom) {
188182
ctx.Diags.diagnose(marking.getInverse().getLoc(),
189183
diag::inverse_but_also_conforms,
190184
nom, getProtocolName(kp));
@@ -198,7 +192,7 @@ static bool checkInvertibleConformanceCommon(ProtocolConformance *conformance,
198192

199193
// Protocols do not directly define any storage.
200194
if (isa<ProtocolDecl, BuiltinTupleDecl>(nom))
201-
llvm_unreachable("unexpected nominal to check Copyable conformance");
195+
llvm_unreachable("unexpected nominal to check invertible's conformance");
202196

203197
// A deinit prevents a struct or enum from conforming to Copyable.
204198
if (ip == InvertibleProtocolKind::Copyable) {
@@ -343,6 +337,7 @@ ProtocolConformance *deriveConformanceForInvertible(Evaluator &evaluator,
343337
if (!ip)
344338
llvm_unreachable("not an invertible protocol");
345339

340+
assert(!isa<ClassDecl>(nominal) && "classes aren't handled here");
346341
auto file = cast<FileUnit>(nominal->getModuleScopeContext());
347342

348343
// Generates a conformance for the nominal to the protocol.
@@ -403,20 +398,6 @@ ProtocolConformance *deriveConformanceForInvertible(Evaluator &evaluator,
403398
return generateConformance(ext);
404399
};
405400

406-
switch (*ip) {
407-
case InvertibleProtocolKind::Copyable:
408-
// If move-only classes is enabled, we'll check the markings.
409-
if (ctx.LangOpts.hasFeature(Feature::MoveOnlyClasses))
410-
break;
411-
412-
LLVM_FALLTHROUGH;
413-
case InvertibleProtocolKind::Escapable:
414-
// Always derive unconditional IP conformance for classes
415-
if (isa<ClassDecl>(nominal))
416-
return generateConformance(nominal);
417-
break;
418-
}
419-
420401
auto marking = nominal->getMarking(*ip);
421402

422403
// Unexpected to have any positive marking for IP if we're deriving it.
@@ -430,10 +411,8 @@ ProtocolConformance *deriveConformanceForInvertible(Evaluator &evaluator,
430411
return nullptr; // No positive IP conformance will be inferred.
431412

432413
case InverseMarking::Kind::Inferred:
433-
if (!isa<ClassDecl>(nominal))
434-
return generateConditionalConformance();
414+
return generateConditionalConformance();
435415

436-
LLVM_FALLTHROUGH;
437416
case InverseMarking::Kind::None:
438417
// All types already start with conformances to the invertible protocols in
439418
// this case, within `NominalTypeDecl::prepareConformanceTable`.

lib/Sema/TypeCheckType.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,14 +1083,6 @@ Type TypeResolution::applyUnboundGenericArguments(
10831083
if (didDiagnoseMoveOnlyGenericArgs(ctx, loc, resultType, genericArgs, dc))
10841084
return ErrorType::get(ctx);
10851085

1086-
if (options.contains(TypeResolutionFlags::SILType)) {
1087-
if (auto nominal = dyn_cast<NominalTypeDecl>(decl)) {
1088-
if (nominal->isOptionalDecl()) {
1089-
skipRequirementsCheck = true;
1090-
}
1091-
}
1092-
}
1093-
10941086
// Get the substitutions for outer generic parameters from the parent
10951087
// type.
10961088
if (parentTy) {

test/Generics/inverse_generics.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,12 @@ protocol NeedsCopyable {}
169169

170170
struct Silly: ~Copyable, Copyable {} // expected-error {{struct 'Silly' required to be 'Copyable' but is marked with '~Copyable'}}
171171
enum Sally: Copyable, ~Copyable, NeedsCopyable {} // expected-error {{enum 'Sally' required to be 'Copyable' but is marked with '~Copyable'}}
172+
172173
class NiceTry: ~Copyable, Copyable {} // expected-error {{classes cannot be '~Copyable'}}
174+
// expected-error@-1 {{class 'NiceTry' required to be 'Copyable' but is marked with '~Copyable}}
175+
176+
@_moveOnly class NiceTry2: Copyable {} // expected-error {{'@_moveOnly' attribute is only valid on structs or enums}}
177+
// expected-error@-1 {{class 'NiceTry2' required to be 'Copyable' but is marked with '~Copyable'}}
173178

174179
struct OopsConformance1: ~Copyable, NeedsCopyable {}
175180
// expected-error@-1 {{type 'OopsConformance1' does not conform to protocol 'NeedsCopyable'}}

0 commit comments

Comments
 (0)