Skip to content

Commit 00bfe00

Browse files
committed
prevent uses of Copyable and bogus errors when ~Copyable appears
Rather than try to omit explicit uses of `Copyable` in the output module for backwards compatability, we instead make it an error to use `Copyable` as a type when not suppressed. While it's straightforward to omit it for generic parameters, for uses of `Copyable` as an existential, we'd need to emit `Any` in its place. We'd also need to find and omit or modify explicit type constraints or same-type requirements involving Copyable. All of that sounded rather gross, and not really needed right now since any uses of `Copyable` explicitly is just not needed.
1 parent 6778ca4 commit 00bfe00

15 files changed

+294
-112
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6887,8 +6887,11 @@ ERROR(noncopyable_objc_enum, none,
68876887
ERROR(suppress_cannot_suppress,none,
68886888
"cannot suppress implicit conformance to %0", (Type))
68896889

6890+
ERROR(suppress_cannot_suppress_here,none,
6891+
"suppression of %0 conformance cannot appear here", (Type))
6892+
68906893
ERROR(suppress_on_wrong_decl,none,
6891-
"cannot suppress conformance on %0", (DescriptiveDeclKind))
6894+
"cannot suppress conformance to %0 on %1", (Type, DescriptiveDeclKind))
68926895

68936896
ERROR(suppress_duplicate,none,
68946897
"implicit conformance to %0 already suppressed", (Type))
@@ -6898,6 +6901,14 @@ NOTE(suppress_previously_here,none,
68986901
ERROR(suppress_not_protocol,none,
68996902
"suppression of non-protocol type %0", (Type))
69006903

6904+
ERROR(copyable_only_suppression,none,
6905+
"'Copyable' can only be suppressed via '~Copyable' at this time",
6906+
())
6907+
6908+
ERROR(suppression_illegal_here,none,
6909+
"suppression cannot appear here",
6910+
())
6911+
69016912
//------------------------------------------------------------------------------
69026913
// MARK: Type inference from default expressions
69036914
//------------------------------------------------------------------------------

include/swift/Parse/Parser.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1205,7 +1205,6 @@ class Parser {
12051205
PI_Default = 0,
12061206
PI_AllowClassRequirement = 1 << 1,
12071207
PI_AllowAnyObject = 1 << 2,
1208-
PI_AllowSuppression = 1 << 3,
12091208
};
12101209

12111210
using ParseInheritanceOptions = OptionSet<ParseInheritanceFlags>;

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,6 @@ void swift::rewriting::realizeInheritedRequirements(
726726
// handle suppressed requirements
727727
if (inheritedEntries[index].isSuppressed) {
728728
// TODO: we should be adding a requirement for Copyable if not suppressed.
729-
assert(false && "unexpected suppressed entry");
730729
continue;
731730
}
732731

lib/Parse/ParseDecl.cpp

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5733,31 +5733,12 @@ ParserStatus Parser::parseInheritance(
57335733
continue;
57345734
}
57355735

5736-
// parse the "without" operator
5737-
SourceLoc withoutLoc;
5738-
if (Tok.isTilde()) {
5739-
auto loc = consumeToken();
5740-
5741-
if (!options.contains(PI_AllowSuppression))
5742-
diagnose(loc, diag::suppress_illegal_here);
5743-
else
5744-
withoutLoc = loc;
5745-
}
5746-
57475736
auto ParsedTypeResult = parseType();
57485737
Status |= ParsedTypeResult;
57495738

5750-
// If it's a single type, record the type under the appropriate entry.
5751-
if (ParsedTypeResult.isNonNull()) {
5752-
TypeRepr *typeRepr = ParsedTypeResult.get();
5739+
if (ParsedTypeResult.isNonNull())
5740+
Inherited.push_back(InheritedEntry(ParsedTypeResult.get()));
57535741

5754-
// is the parsed type meant to be a suppressed entry?
5755-
if (withoutLoc) {
5756-
typeRepr = new (Context) SuppressedTypeRepr(typeRepr, withoutLoc);
5757-
}
5758-
5759-
Inherited.push_back(InheritedEntry(typeRepr));
5760-
}
57615742
} while (HasNextType);
57625743

57635744
return Status;
@@ -8219,7 +8200,7 @@ ParserResult<EnumDecl> Parser::parseDeclEnum(ParseDeclOptions Flags,
82198200
// Parse optional inheritance clause within the context of the enum.
82208201
if (Tok.is(tok::colon)) {
82218202
SmallVector<InheritedEntry, 2> Inherited;
8222-
Status |= parseInheritance(Inherited, {PI_AllowSuppression});
8203+
Status |= parseInheritance(Inherited, {});
82238204
ED->setAllInheritedEntries(Context.AllocateCopy(Inherited));
82248205
}
82258206

@@ -8480,7 +8461,7 @@ ParserResult<StructDecl> Parser::parseDeclStruct(ParseDeclOptions Flags,
84808461
// Parse optional inheritance clause within the context of the struct.
84818462
if (Tok.is(tok::colon)) {
84828463
SmallVector<InheritedEntry, 2> Inherited;
8483-
Status |= parseInheritance(Inherited, {PI_AllowSuppression});
8464+
Status |= parseInheritance(Inherited, {});
84848465
SD->setAllInheritedEntries(Context.AllocateCopy(Inherited));
84858466
}
84868467

@@ -8571,7 +8552,7 @@ ParserResult<ClassDecl> Parser::parseDeclClass(ParseDeclOptions Flags,
85718552
// Parse optional inheritance clause within the context of the class.
85728553
if (Tok.is(tok::colon)) {
85738554
SmallVector<InheritedEntry, 2> Inherited;
8574-
Status |= parseInheritance(Inherited, {PI_AllowSuppression});
8555+
Status |= parseInheritance(Inherited, {});
85758556
CD->setAllInheritedEntries(Context.AllocateCopy(Inherited));
85768557

85778558
// Parse python style inheritance clause and replace parentheses with a colon

lib/Parse/ParseGeneric.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ Parser::parseGenericParametersBeforeWhere(SourceLoc LAngleLoc,
9999
ParserResult<TypeRepr> Ty;
100100

101101
if (Tok.isAny(tok::identifier, tok::code_complete, tok::kw_protocol,
102-
tok::kw_Any)) {
102+
tok::kw_Any) || Tok.isTilde()) {
103103
Ty = parseType();
104104
} else if (Tok.is(tok::kw_class)) {
105105
diagnose(Tok, diag::unexpected_class_constraint);

lib/Parse/ParseType.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,11 @@ ParserResult<TypeRepr> Parser::parseTypeScalar(
417417
return parseSILBoxType(generics, attrs);
418418
}
419419

420+
// parse the "without" operator
421+
SourceLoc withoutLoc;
422+
if (Tok.isTilde())
423+
withoutLoc = consumeToken();
424+
420425
ParserResult<TypeRepr> ty = parseTypeSimpleOrComposition(MessageID, reason);
421426
status |= ParserStatus(ty);
422427
if (ty.isNull())
@@ -566,6 +571,11 @@ ParserResult<TypeRepr> Parser::parseTypeScalar(
566571
tyR->walk(walker);
567572
}
568573

574+
// Wrap the type more tightly to "without" than other attributes.
575+
// TODO: require parens around the composition if we saw a "without".
576+
if (withoutLoc)
577+
tyR = new(Context) SuppressedTypeRepr(tyR, withoutLoc);
578+
569579
return makeParserResult(
570580
status,
571581
applyAttributeToType(tyR, attrs, specifier, specifierLoc, isolatedLoc,

lib/Sema/TypeCheckAccess.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -413,9 +413,9 @@ void AccessControlCheckerBase::checkGenericParamAccess(
413413
if (allInheritedEntries.empty())
414414
continue;
415415
assert(allInheritedEntries.size() == 1);
416-
assert(allInheritedEntries.front().isSuppressed == false);
417-
checkTypeAccessImpl(allInheritedEntries.front().getType(),
418-
allInheritedEntries.front().getTypeRepr(),
416+
auto entry = allInheritedEntries.front();
417+
checkTypeAccessImpl(entry.getType(),
418+
entry.getTypeRepr(),
419419
accessScope, DC, /*mayBeInferred*/false,
420420
callback);
421421
}
@@ -1923,9 +1923,8 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
19231923
if (allInheritedEntries.empty())
19241924
continue;
19251925
assert(allInheritedEntries.size() == 1);
1926-
auto inherited = allInheritedEntries.front();
1927-
assert(!inherited.isSuppressed);
1928-
checkType(inherited.getType(), inherited.getTypeRepr(), ownerDecl);
1926+
auto entry = allInheritedEntries.front();
1927+
checkType(entry.getType(), entry.getTypeRepr(), ownerDecl);
19291928
}
19301929
}
19311930

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -90,69 +90,71 @@ static Type containsParameterizedProtocolType(Type inheritedTy) {
9090
// implicit conformances.
9191
static void checkSuppressedEntries(
9292
llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *> declUnion) {
93-
if (declUnion.is<const ExtensionDecl*>())
94-
return; // nothing to check; extensions don't carry suppressed conformances
95-
9693
auto typeDecl = declUnion.dyn_cast<const TypeDecl *>();
97-
assert(typeDecl);
94+
auto extnDecl = declUnion.dyn_cast<const ExtensionDecl*>();
9895

99-
#ifndef NDEBUG
100-
if (isa<AssociatedTypeDecl>(typeDecl)
101-
|| isa<GenericTypeParamDecl>(typeDecl)) {
102-
assert(typeDecl->getSuppressed().empty()
103-
&& "unexpected suppressed conformances");
104-
}
105-
#endif
96+
ASTContext &ctx =
97+
typeDecl ? typeDecl->getASTContext() : extnDecl->getASTContext();
98+
99+
ArrayRef<InheritedEntry> allEntries =
100+
typeDecl ? typeDecl->getAllInheritedEntries()
101+
: extnDecl->getAllInheritedEntries();
106102

107-
ASTContext &ctx = typeDecl->getASTContext();
108103
auto &diags = ctx.Diags;
104+
bool haveMoveOnlyClasses = ctx.LangOpts.hasFeature(Feature::MoveOnlyClasses);
109105

110106
using EntryTracker = std::unordered_map<KnownProtocolKind,
111107
InheritedEntry const *>;
112108
EntryTracker seenEntries;
113109

114-
bool haveMoveOnlyClasses =
115-
typeDecl->getASTContext().LangOpts.hasFeature(Feature::MoveOnlyClasses);
116-
117110
// TODO: this pattern of iterating over the indicies of all entries is
118111
// an all too common pattern throughout the type-checker. It's a symptom of
119112
// an overdue refactoring that allows you to iterate over the entries and
120113
// either automatically perform these type resolution requests, or know
121114
// the requests have already been performed very early on. It's the primary
122115
// thing preventing us from using the InheritedEntryRange accessors.
123-
ArrayRef<InheritedEntry> allEntries = typeDecl->getAllInheritedEntries();
124116
for (auto i : indices(allEntries)) {
125117
auto &suppressed = allEntries[i];
126118

127119
if (!suppressed.isSuppressed)
128120
continue;
129121

130122
// Resolve and validate the type if needed.
131-
InheritedTypeRequest request{typeDecl, i, TypeResolutionStage::Interface};
123+
InheritedTypeRequest request{declUnion, i, TypeResolutionStage::Interface};
132124
Type suppressedTy = evaluateOrDefault(ctx.evaluator, request, Type());
133125

134126
// If we couldn't resolve the type or it contains an error, ignore it.
135127
if (!suppressedTy || suppressedTy->hasError())
136128
continue;
137129

138130
// only structs, enums, and some classes can have suppressed entries
139-
if (!(isa<StructDecl>(typeDecl) || isa<EnumDecl>(typeDecl)
140-
|| (isa<ClassDecl>(typeDecl) && haveMoveOnlyClasses))) {
131+
if (typeDecl) {
132+
if (!(isa<StructDecl>(typeDecl) || isa<EnumDecl>(typeDecl)
133+
|| (isa<ClassDecl>(typeDecl) && haveMoveOnlyClasses))) {
134+
diags.diagnose(suppressed.getLoc(),
135+
diag::suppress_on_wrong_decl,
136+
suppressedTy,
137+
typeDecl->getDescriptiveKind());
138+
// FIXME: a great fix-it would be to remove the entire entry,
139+
// including any trailing comma. See checkInheritedEntry
140+
// for an example of the logic to do that. You'll need to update it
141+
// using MergedArrayRef so it understands source locations for both
142+
// suppressed and inherited entries.
143+
continue;
144+
}
145+
} else {
146+
assert(extnDecl);
141147
diags.diagnose(suppressed.getLoc(),
142148
diag::suppress_on_wrong_decl,
143-
typeDecl->getDescriptiveKind());
144-
// FIXME: a great fix-it would be to remove the entire entry,
145-
// including any trailing comma. See checkInheritedEntry
146-
// for an example of the logic to do that. You'll need to update it
147-
// using MergedArrayRef so it understands source locations for both
148-
// suppressed and inherited entries.
149+
suppressedTy,
150+
extnDecl->getDescriptiveKind());
149151
continue;
150152
}
151153

152154
// Ensure it is a suppressible protocol.
153-
if (auto protocolTy = dyn_cast<ProtocolType>(suppressedTy))
154-
if (auto protocolDecl = protocolTy->getDecl())
155-
if (auto knownProtocol = protocolDecl->getKnownProtocolKind())
155+
if (auto protocolTy = dyn_cast<ProtocolType>(suppressedTy)) {
156+
if (auto protocolDecl = protocolTy->getDecl()) {
157+
if (auto knownProtocol = protocolDecl->getKnownProtocolKind()) {
156158
if (isSuppressibleProtocol(*knownProtocol)) {
157159
// remember that we've seen this suppression for the type
158160
EntryTracker::iterator iter;
@@ -178,20 +180,16 @@ static void checkSuppressedEntries(
178180

179181
continue;
180182
}
181-
182-
// At this point, the suppressed type is wrong. Tailor the error messages.
183-
184-
if (suppressedTy->isConstraintType()) {
185-
diags.diagnose(suppressed.getLoc(),
186-
diag::suppress_cannot_suppress,
187-
suppressedTy);
188-
// FIXME: a great fix-it would be to remove the entire entry.
189-
} else {
190-
diags.diagnose(suppressed.getLoc(),
191-
diag::suppress_not_protocol,
192-
suppressedTy);
193-
// FIXME: a great fix-it would be to remove the entire entry.
183+
}
184+
}
194185
}
186+
187+
// At this point, the suppressed type is wrong.
188+
assert(suppressedTy->isConstraintType());
189+
diags.diagnose(suppressed.getLoc(),
190+
diag::suppress_cannot_suppress,
191+
suppressedTy);
192+
// FIXME: a great fix-it would be to remove the entire entry.
195193
}
196194
}
197195

@@ -2912,7 +2910,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
29122910
return;
29132911

29142912
// go over the all types directly conformed-to by the extension
2915-
for (auto entry : extension->getAllInheritedEntries()) {
2913+
for (auto entry : extension->getInherited()) {
29162914
diagnoseIncompatibleWithMoveOnlyType(extension->getLoc(), nomDecl,
29172915
entry.getType());
29182916
}

lib/Sema/TypeCheckType.cpp

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2085,6 +2085,8 @@ namespace {
20852085
TypeResolutionOptions options);
20862086
NeverNullType resolveCompileTimeConstTypeRepr(CompileTimeConstTypeRepr *repr,
20872087
TypeResolutionOptions options);
2088+
NeverNullType resolveSuppressedTypeRepr(SuppressedTypeRepr *repr,
2089+
TypeResolutionOptions options);
20882090
NeverNullType resolveArrayType(ArrayTypeRepr *repr,
20892091
TypeResolutionOptions options);
20902092
NeverNullType resolveDictionaryType(DictionaryTypeRepr *repr,
@@ -2374,9 +2376,7 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr,
23742376
return resolveCompileTimeConstTypeRepr(cast<CompileTimeConstTypeRepr>(repr),
23752377
options);
23762378
case TypeReprKind::Suppressed:
2377-
// Currently, parsing and other type checking code prevents invalid
2378-
// suppressed types from being formed, so just resolve the base.
2379-
return resolveType(cast<SuppressedTypeRepr>(repr)->getBase(), options);
2379+
return resolveSuppressedTypeRepr(cast<SuppressedTypeRepr>(repr), options);
23802380

23812381
case TypeReprKind::SimpleIdent:
23822382
case TypeReprKind::GenericIdent:
@@ -3391,7 +3391,11 @@ TypeResolver::resolveASTFunctionTypeParams(TupleTypeRepr *inputRepr,
33913391
nestedRepr = specifierRepr->getBase();
33923392
continue;
33933393
case TypeReprKind::Suppressed:
3394-
llvm_unreachable("unexpected suppressed type for function param");
3394+
// Can't have a suppressed type as a function param.
3395+
diagnose(specifierRepr->getLoc(), diag::suppression_illegal_here);
3396+
specifierRepr->setInvalid();
3397+
nestedRepr = specifierRepr->getBase();
3398+
break;
33953399
default:
33963400
break;
33973401
}
@@ -4177,6 +4181,19 @@ TypeResolver::resolveDeclRefTypeRepr(DeclRefTypeRepr *repr,
41774181
return ErrorType::get(getASTContext());
41784182
}
41794183

4184+
// Do not allow unsuppressed uses of Copyable anywhere.
4185+
if (!options.contains(TypeResolutionFlags::IsSuppressed)) {
4186+
if (auto protoTy = result->getAs<ProtocolType>()) {
4187+
if (auto protoDecl = protoTy->getDecl()) {
4188+
if (protoDecl->isSpecificProtocol(KnownProtocolKind::Copyable)) {
4189+
diagnose(repr->getLoc(), diag::copyable_only_suppression);
4190+
repr->setInvalid();
4191+
return ErrorType::get(getASTContext());
4192+
}
4193+
}
4194+
}
4195+
}
4196+
41804197
auto *dc = getDeclContext();
41814198
auto &ctx = getASTContext();
41824199

@@ -4321,6 +4338,39 @@ TypeResolver::resolveCompileTimeConstTypeRepr(CompileTimeConstTypeRepr *repr,
43214338
return resolveType(repr->getBase(), options);
43224339
}
43234340

4341+
NeverNullType
4342+
TypeResolver::resolveSuppressedTypeRepr(SuppressedTypeRepr *repr,
4343+
TypeResolutionOptions options) {
4344+
auto baseType = resolveType(repr->getBase(),
4345+
options | TypeResolutionFlags::IsSuppressed);
4346+
if (baseType->hasError())
4347+
return baseType;
4348+
4349+
// Can't suppress outside of an inheritance clause.
4350+
if (!options.isInheritanceClause()) {
4351+
if (baseType->isConstraintType()) {
4352+
// Give a more detailed message if they tried to suppress a constraint
4353+
// in an illegal spot. This specifically excludes existentials because
4354+
// we don't want to complain about an 'any Equatable' as that's not a
4355+
// constraint.
4356+
diagnose(repr->getLoc(), diag::suppress_cannot_suppress_here, baseType);
4357+
} else {
4358+
diagnose(repr->getLoc(), diag::suppression_illegal_here);
4359+
}
4360+
repr->setInvalid();
4361+
return ErrorType::get(getASTContext());
4362+
}
4363+
4364+
// Can't suppress a non-constraint type in an inheritance clause.
4365+
if (!baseType->isConstraintType()) {
4366+
diagnose(repr->getLoc(), diag::suppress_not_protocol, baseType);
4367+
repr->setInvalid();
4368+
return ErrorType::get(getASTContext());
4369+
}
4370+
4371+
return baseType;
4372+
}
4373+
43244374
NeverNullType TypeResolver::resolveArrayType(ArrayTypeRepr *repr,
43254375
TypeResolutionOptions options) {
43264376
auto baseTy = resolveType(repr->getBase(), options.withoutContext());

0 commit comments

Comments
 (0)