Skip to content

Commit 972cd5c

Browse files
committed
Sema: Check associated conformance availability
This replaces the old exportability check with a call into the new general conformance availability check entry point. Part of <rdar://problem/35158274>.
1 parent 6934857 commit 972cd5c

File tree

5 files changed

+137
-85
lines changed

5 files changed

+137
-85
lines changed

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,17 @@ ExportContext ExportContext::forFunctionBody(DeclContext *DC, SourceLoc loc) {
222222
unavailablePlatformKind);
223223
}
224224

225+
ExportContext ExportContext::forConformance(DeclContext *DC,
226+
ProtocolDecl *proto) {
227+
assert(isa<ExtensionDecl>(DC) || isa<NominalTypeDecl>(DC));
228+
auto where = forDeclSignature(DC->getInnermostDeclarationDeclContext());
229+
230+
where.Exported &= proto->getFormalAccessScope(
231+
DC, /*usableFromInlineAsPublic*/true).isPublic();
232+
233+
return where;
234+
}
235+
225236
ExportContext ExportContext::withReason(ExportabilityReason reason) const {
226237
auto copy = *this;
227238
copy.Reason = unsigned(reason);
@@ -2172,20 +2183,19 @@ void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange,
21722183
}
21732184
}
21742185

2175-
void TypeChecker::diagnoseIfDeprecated(
2176-
SourceLoc loc,
2177-
const RootProtocolConformance *rootConf,
2178-
const ExtensionDecl *ext,
2179-
const ExportContext &where) {
2186+
bool TypeChecker::diagnoseIfDeprecated(SourceLoc loc,
2187+
const RootProtocolConformance *rootConf,
2188+
const ExtensionDecl *ext,
2189+
const ExportContext &where) {
21802190
const AvailableAttr *attr = TypeChecker::getDeprecated(ext);
21812191
if (!attr)
2182-
return;
2192+
return false;
21832193

21842194
// We match the behavior of clang to not report deprecation warnings
21852195
// inside declarations that are themselves deprecated on all deployment
21862196
// targets.
21872197
if (where.isDeprecated()) {
2188-
return;
2198+
return false;
21892199
}
21902200

21912201
auto *dc = where.getDeclContext();
@@ -2196,7 +2206,7 @@ void TypeChecker::diagnoseIfDeprecated(
21962206
// Suppress a deprecation warning if the availability checking machinery
21972207
// thinks the reference program location will not execute on any
21982208
// deployment target for the current platform.
2199-
return;
2209+
return false;
22002210
}
22012211
}
22022212

@@ -2215,7 +2225,7 @@ void TypeChecker::diagnoseIfDeprecated(
22152225
attr->Deprecated.hasValue(), deprecatedVersion,
22162226
/*message*/ StringRef())
22172227
.highlight(attr->getRange());
2218-
return;
2228+
return true;
22192229
}
22202230

22212231
EncodedDiagnosticMessage encodedMessage(attr->Message);
@@ -2225,6 +2235,7 @@ void TypeChecker::diagnoseIfDeprecated(
22252235
attr->Deprecated.hasValue(), deprecatedVersion,
22262236
encodedMessage.Message)
22272237
.highlight(attr->getRange());
2238+
return true;
22282239
}
22292240

22302241
void swift::diagnoseUnavailableOverride(ValueDecl *override,
@@ -3374,7 +3385,8 @@ void swift::diagnoseTypeAvailability(const TypeRepr *TR, Type T, SourceLoc loc,
33743385
bool
33753386
swift::diagnoseConformanceAvailability(SourceLoc loc,
33763387
ProtocolConformanceRef conformance,
3377-
const ExportContext &where) {
3388+
const ExportContext &where,
3389+
Type depTy, Type replacementTy) {
33783390
assert(!where.isImplicit());
33793391

33803392
if (!conformance.isConcrete())
@@ -3385,29 +3397,55 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
33853397

33863398
auto *DC = where.getDeclContext();
33873399

3400+
auto maybeEmitAssociatedTypeNote = [&]() {
3401+
if (!depTy && !replacementTy)
3402+
return;
3403+
3404+
Type selfTy = rootConf->getProtocol()->getProtocolSelfType();
3405+
if (!depTy->isEqual(selfTy)) {
3406+
auto &ctx = DC->getASTContext();
3407+
ctx.Diags.diagnose(
3408+
loc,
3409+
diag::assoc_conformance_from_implementation_only_module,
3410+
depTy, replacementTy->getCanonicalType());
3411+
}
3412+
};
3413+
33883414
if (auto *ext = dyn_cast<ExtensionDecl>(rootConf->getDeclContext())) {
3389-
if (TypeChecker::diagnoseConformanceExportability(loc, rootConf, ext, where))
3415+
if (TypeChecker::diagnoseConformanceExportability(loc, rootConf, ext, where)) {
3416+
maybeEmitAssociatedTypeNote();
33903417
return true;
3418+
}
33913419

3392-
if (diagnoseExplicitUnavailability(loc, rootConf, ext, where))
3420+
if (diagnoseExplicitUnavailability(loc, rootConf, ext, where)) {
3421+
maybeEmitAssociatedTypeNote();
33933422
return true;
3394-
3395-
// Diagnose for deprecation
3396-
TypeChecker::diagnoseIfDeprecated(loc, rootConf, ext, where);
3423+
}
33973424

33983425
// Diagnose (and possibly signal) for potential unavailability
33993426
auto maybeUnavail = TypeChecker::checkConformanceAvailability(
34003427
rootConf, ext, where);
34013428
if (maybeUnavail.hasValue()) {
34023429
TypeChecker::diagnosePotentialUnavailability(rootConf, ext, loc, DC,
34033430
maybeUnavail.getValue());
3431+
maybeEmitAssociatedTypeNote();
3432+
return true;
3433+
}
3434+
3435+
// Diagnose for deprecation
3436+
if (TypeChecker::diagnoseIfDeprecated(loc, rootConf, ext, where)) {
3437+
maybeEmitAssociatedTypeNote();
3438+
3439+
// Deprecation is just a warning, so keep going with checking the
3440+
// substitution map below.
34043441
}
34053442
}
34063443

34073444
// Now, check associated conformances.
34083445
SubstitutionMap subConformanceSubs =
34093446
concreteConf->getSubstitutions(DC->getParentModule());
3410-
if (diagnoseSubstitutionMapAvailability(loc, subConformanceSubs, where))
3447+
if (diagnoseSubstitutionMapAvailability(loc, subConformanceSubs, where,
3448+
depTy, replacementTy))
34113449
return true;
34123450

34133451
return false;
@@ -3416,10 +3454,12 @@ swift::diagnoseConformanceAvailability(SourceLoc loc,
34163454
bool
34173455
swift::diagnoseSubstitutionMapAvailability(SourceLoc loc,
34183456
SubstitutionMap subs,
3419-
const ExportContext &where) {
3457+
const ExportContext &where,
3458+
Type depTy, Type replacementTy) {
34203459
bool hadAnyIssues = false;
34213460
for (ProtocolConformanceRef conformance : subs.getConformances()) {
3422-
if (diagnoseConformanceAvailability(loc, conformance, where))
3461+
if (diagnoseConformanceAvailability(loc, conformance, where,
3462+
depTy, replacementTy))
34233463
hadAnyIssues = true;
34243464
}
34253465
return hadAnyIssues;

lib/Sema/TypeCheckAvailability.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ class ExportContext {
129129
/// it can reference anything.
130130
static ExportContext forFunctionBody(DeclContext *DC, SourceLoc loc);
131131

132+
/// Create an instance describing associated conformances that can be
133+
/// referenced from the the conformance defined by the given DeclContext,
134+
/// which must be a NominalTypeDecl or ExtensionDecl.
135+
static ExportContext forConformance(DeclContext *DC, ProtocolDecl *proto);
136+
132137
/// Produce a new context with the same properties as this one, except
133138
/// changing the ExportabilityReason. This only affects diagnostics.
134139
ExportContext withReason(ExportabilityReason reason) const;
@@ -212,12 +217,16 @@ void diagnoseTypeAvailability(const TypeRepr *TR, Type T, SourceLoc loc,
212217
bool
213218
diagnoseConformanceAvailability(SourceLoc loc,
214219
ProtocolConformanceRef conformance,
215-
const ExportContext &context);
220+
const ExportContext &context,
221+
Type depTy=Type(),
222+
Type replacementTy=Type());
216223

217224
bool
218225
diagnoseSubstitutionMapAvailability(SourceLoc loc,
219226
SubstitutionMap subs,
220-
const ExportContext &context);
227+
const ExportContext &context,
228+
Type depTy=Type(),
229+
Type replacementTy=Type());
221230

222231
/// Diagnose uses of unavailable declarations. Returns true if a diagnostic
223232
/// was emitted.

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 15 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4308,54 +4308,6 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup(
43084308
return ResolveWitnessResult::ExplicitFailed;
43094309
}
43104310

4311-
static void checkExportability(Type depTy, Type replacementTy,
4312-
const ProtocolConformance *conformance,
4313-
NormalProtocolConformance *conformanceBeingChecked,
4314-
DeclContext *DC) {
4315-
SourceFile *SF = DC->getParentSourceFile();
4316-
if (!SF)
4317-
return;
4318-
4319-
SubstitutionMap subs =
4320-
conformance->getSubstitutions(SF->getParentModule());
4321-
for (auto &subConformance : subs.getConformances()) {
4322-
if (!subConformance.isConcrete())
4323-
continue;
4324-
checkExportability(depTy, replacementTy, subConformance.getConcrete(),
4325-
conformanceBeingChecked, DC);
4326-
}
4327-
4328-
const RootProtocolConformance *rootConformance =
4329-
conformance->getRootConformance();
4330-
ModuleDecl *M = rootConformance->getDeclContext()->getParentModule();
4331-
4332-
auto where = ExportContext::forDeclSignature(
4333-
DC->getInnermostDeclarationDeclContext());
4334-
auto originKind = getDisallowedOriginKind(
4335-
rootConformance->getDeclContext()->getAsDecl(),
4336-
where);
4337-
if (originKind == DisallowedOriginKind::None)
4338-
return;
4339-
4340-
ASTContext &ctx = SF->getASTContext();
4341-
4342-
Type selfTy = rootConformance->getProtocol()->getProtocolSelfType();
4343-
4344-
ctx.Diags.diagnose(
4345-
conformanceBeingChecked->getLoc(),
4346-
diag::conformance_from_implementation_only_module,
4347-
rootConformance->getType(),
4348-
rootConformance->getProtocol()->getName(), 0, M->getName(),
4349-
static_cast<unsigned>(originKind));
4350-
4351-
if (!depTy->isEqual(selfTy)) {
4352-
ctx.Diags.diagnose(
4353-
conformanceBeingChecked->getLoc(),
4354-
diag::assoc_conformance_from_implementation_only_module,
4355-
depTy, replacementTy->getCanonicalType());
4356-
}
4357-
}
4358-
43594311
void ConformanceChecker::ensureRequirementsAreSatisfied() {
43604312
Conformance->finishSignatureConformances();
43614313
auto proto = Conformance->getProtocol();
@@ -4401,18 +4353,21 @@ void ConformanceChecker::ensureRequirementsAreSatisfied() {
44014353

44024354
// Now check that our associated conformances are at least as visible as
44034355
// the conformance itself.
4404-
if (getRequiredAccessScope().isPublic() || isUsableFromInlineRequired()) {
4405-
for (auto req : proto->getRequirementSignature()) {
4406-
if (req.getKind() == RequirementKind::Conformance) {
4407-
auto depTy = req.getFirstType();
4408-
auto *proto = req.getSecondType()->castTo<ProtocolType>()->getDecl();
4409-
auto conformance = Conformance->getAssociatedConformance(depTy, proto);
4410-
if (conformance.isConcrete()) {
4411-
auto *concrete = conformance.getConcrete();
4412-
auto replacementTy = DC->mapTypeIntoContext(concrete->getType());
4413-
checkExportability(depTy, replacementTy, concrete,
4414-
Conformance, DC);
4415-
}
4356+
auto where = ExportContext::forConformance(DC, proto);
4357+
if (where.isImplicit())
4358+
return;
4359+
4360+
for (auto req : proto->getRequirementSignature()) {
4361+
if (req.getKind() == RequirementKind::Conformance) {
4362+
auto depTy = req.getFirstType();
4363+
auto *proto = req.getSecondType()->castTo<ProtocolType>()->getDecl();
4364+
auto conformance = Conformance->getAssociatedConformance(depTy, proto);
4365+
if (conformance.isConcrete()) {
4366+
auto *concrete = conformance.getConcrete();
4367+
auto replacementTy = DC->mapTypeIntoContext(concrete->getType());
4368+
diagnoseConformanceAvailability(Conformance->getLoc(),
4369+
conformance, where,
4370+
depTy, replacementTy);
44164371
}
44174372
}
44184373
}

lib/Sema/TypeChecker.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,10 +1066,10 @@ void diagnoseIfDeprecated(SourceRange SourceRange,
10661066
const ApplyExpr *Call);
10671067

10681068
/// Emits a diagnostic for a reference to a conformnace that is deprecated.
1069-
void diagnoseIfDeprecated(SourceLoc Loc,
1070-
const RootProtocolConformance *DeprecatedConf,
1071-
const ExtensionDecl *Ext,
1072-
const ExportContext &Where);
1069+
bool diagnoseIfDeprecated(SourceLoc loc,
1070+
const RootProtocolConformance *rootConf,
1071+
const ExtensionDecl *ext,
1072+
const ExportContext &where);
10731073
/// @}
10741074

10751075
/// If LangOptions::DebugForbidTypecheckPrefix is set and the given decl

test/Sema/conformance_availability.swift

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public struct HasUnavailableConformance1 {}
1616

1717
@available(*, unavailable)
1818
extension HasUnavailableConformance1 : Horse {}
19-
// expected-note@-1 6{{conformance of 'HasUnavailableConformance1' to 'Horse' has been explicitly marked unavailable here}}
19+
// expected-note@-1 7{{conformance of 'HasUnavailableConformance1' to 'Horse' has been explicitly marked unavailable here}}
2020

2121
func passUnavailableConformance1(x: HasUnavailableConformance1) {
2222
takesHorse(x) // expected-error {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
@@ -212,4 +212,52 @@ func passAvailableConformance1a(x: HasAvailableConformance1) {
212212
takesHorse(x)
213213
x.giddyUp()
214214
_ = UsesHorse<HasAvailableConformance1>.self
215+
}
216+
217+
// Associated conformance with unavailability
218+
protocol Rider {
219+
associatedtype H : Horse
220+
}
221+
222+
struct AssocConformanceUnavailable : Rider {
223+
// expected-error@-1 {{conformance of 'HasUnavailableConformance1' to 'Horse' is unavailable}}
224+
// expected-note@-2 {{in associated type 'Self.H' (inferred as 'HasUnavailableConformance1')}}
225+
typealias H = HasUnavailableConformance1
226+
}
227+
228+
// Associated conformance with deprecation
229+
struct AssocConformanceDeprecated : Rider {
230+
// expected-warning@-1 {{conformance of 'HasDeprecatedConformance1' to 'Horse' is deprecated}}
231+
// expected-note@-2 {{in associated type 'Self.H' (inferred as 'HasDeprecatedConformance1')}}
232+
typealias H = HasDeprecatedConformance1
233+
}
234+
235+
// Associated conformance with availability
236+
struct AssocConformanceAvailable1 : Rider {
237+
// expected-error@-1 {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
238+
// expected-note@-2 {{in associated type 'Self.H' (inferred as 'HasAvailableConformance1')}}
239+
// FIXME expected-note@-3 {{add @available attribute to enclosing initializer}}
240+
// expected-note@-4 {{add @available attribute to enclosing struct}}
241+
typealias H = HasAvailableConformance1
242+
}
243+
244+
@available(macOS 100, *)
245+
struct AssocConformanceAvailable2 : Rider {
246+
typealias H = HasAvailableConformance1
247+
}
248+
249+
struct AssocConformanceAvailable3 {}
250+
251+
extension AssocConformanceAvailable3 : Rider {
252+
// expected-error@-1 {{conformance of 'HasAvailableConformance1' to 'Horse' is only available in macOS 100 or newer}}
253+
// expected-note@-2 {{in associated type 'Self.H' (inferred as 'HasAvailableConformance1')}}
254+
// expected-note@-3 {{add @available attribute to enclosing extension}}
255+
typealias H = HasAvailableConformance1
256+
}
257+
258+
struct AssocConformanceAvailable4 {}
259+
260+
@available(macOS 100, *)
261+
extension AssocConformanceAvailable4 : Rider {
262+
typealias H = HasAvailableConformance1
215263
}

0 commit comments

Comments
 (0)