Skip to content

Commit e7dd1c1

Browse files
authored
Merge pull request #20384 from jrose-apple/witless
Require @usableFromInline on associated type witnesses
2 parents 92d56cb + f33bf67 commit e7dd1c1

File tree

9 files changed

+201
-35
lines changed

9 files changed

+201
-35
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1691,6 +1691,14 @@ ERROR(type_witness_not_accessible_type,none,
16911691
"%0 %1 must be as accessible as its enclosing type because it "
16921692
"matches a requirement in protocol %3",
16931693
(DescriptiveDeclKind, DeclName, AccessLevel, DeclName))
1694+
ERROR(witness_not_usable_from_inline,none,
1695+
"%0 %1 must be declared '@usableFromInline' because "
1696+
"because it matches a requirement in protocol %2",
1697+
(DescriptiveDeclKind, DeclName, DeclName))
1698+
WARNING(witness_not_usable_from_inline_warn,none,
1699+
"%0 %1 should be declared '@usableFromInline' because "
1700+
"because it matches a requirement in protocol %2",
1701+
(DescriptiveDeclKind, DeclName, DeclName))
16941702
ERROR(type_witness_objc_generic_parameter,none,
16951703
"type %0 involving Objective-C type parameter%select{| %1}2 cannot be "
16961704
"used for associated type %3 of protocol %4",

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 87 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#include "ConstraintSystem.h"
1919
#include "DerivedConformances.h"
2020
#include "MiscDiagnostics.h"
21-
#include "TypeChecker.h"
2221
#include "TypeCheckAvailability.h"
2322
#include "swift/Basic/SourceManager.h"
2423
#include "swift/Basic/StringExtras.h"
@@ -1233,23 +1232,51 @@ bool WitnessChecker::findBestWitness(
12331232
return isReallyBest;
12341233
}
12351234

1236-
bool WitnessChecker::checkWitnessAccess(AccessScope &requiredAccessScope,
1237-
ValueDecl *requirement,
1235+
AccessScope WitnessChecker::getRequiredAccessScope() {
1236+
if (RequiredAccessScopeAndUsableFromInline.hasValue())
1237+
return RequiredAccessScopeAndUsableFromInline.getValue().first;
1238+
1239+
AccessScope result = Proto->getFormalAccessScope(DC);
1240+
1241+
bool witnessesMustBeUsableFromInline = false;
1242+
if (Adoptee) {
1243+
const NominalTypeDecl *adoptingNominal = Adoptee->getAnyNominal();
1244+
1245+
// Compute the intersection of the conforming type's access scope
1246+
// and the protocol's access scope.
1247+
auto scopeIntersection =
1248+
result.intersectWith(adoptingNominal->getFormalAccessScope(DC));
1249+
assert(scopeIntersection.hasValue());
1250+
result = scopeIntersection.getValue();
1251+
1252+
if (!result.isPublic()) {
1253+
witnessesMustBeUsableFromInline =
1254+
Proto->getFormalAccessScope(
1255+
DC, /*usableFromInlineAsPublic*/true).isPublic() &&
1256+
adoptingNominal->getFormalAccessScope(
1257+
DC, /*usableFromInlineAsPublic*/true).isPublic();
1258+
}
1259+
} else {
1260+
if (!result.isPublic()) {
1261+
witnessesMustBeUsableFromInline =
1262+
Proto->getFormalAccessScope(
1263+
DC, /*usableFromInlineAsPublic*/true).isPublic();
1264+
}
1265+
}
1266+
1267+
RequiredAccessScopeAndUsableFromInline =
1268+
std::make_pair(result, witnessesMustBeUsableFromInline);
1269+
return result;
1270+
}
1271+
1272+
bool WitnessChecker::checkWitnessAccess(ValueDecl *requirement,
12381273
ValueDecl *witness,
12391274
bool *isSetter) {
12401275
*isSetter = false;
12411276
if (!TC.getLangOpts().EnableAccessControl)
12421277
return false;
12431278

1244-
// Compute the intersection of the conforming type's access scope
1245-
// and the protocol's access scope.
1246-
auto scopeIntersection =
1247-
requiredAccessScope.intersectWith(Proto->getFormalAccessScope(DC));
1248-
assert(scopeIntersection.hasValue());
1249-
1250-
requiredAccessScope = *scopeIntersection;
1251-
1252-
AccessScope actualScopeToCheck = requiredAccessScope;
1279+
AccessScope actualScopeToCheck = getRequiredAccessScope();
12531280

12541281
// Setting the 'forConformance' flag means that we admit witnesses in
12551282
// protocol extensions that we can see, but are not necessarily as
@@ -1270,7 +1297,7 @@ bool WitnessChecker::checkWitnessAccess(AccessScope &requiredAccessScope,
12701297
}
12711298
}
12721299

1273-
if (actualScopeToCheck.hasEqualDeclContextWith(requiredAccessScope))
1300+
if (actualScopeToCheck.hasEqualDeclContextWith(getRequiredAccessScope()))
12741301
return true;
12751302
}
12761303

@@ -1297,20 +1324,17 @@ checkWitnessAvailability(ValueDecl *requirement,
12971324
DC, *requiredAvailability));
12981325
}
12991326

1300-
RequirementCheck WitnessChecker::
1301-
checkWitness(AccessScope requiredAccessScope,
1302-
ValueDecl *requirement,
1303-
const RequirementMatch &match) {
1327+
RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
1328+
const RequirementMatch &match) {
13041329
if (!match.OptionalAdjustments.empty())
13051330
return CheckKind::OptionalityConflict;
13061331

13071332
bool isSetter = false;
1308-
if (checkWitnessAccess(requiredAccessScope, requirement, match.Witness,
1309-
&isSetter)) {
1333+
if (checkWitnessAccess(requirement, match.Witness, &isSetter)) {
13101334
CheckKind kind = (isSetter
13111335
? CheckKind::AccessOfSetter
13121336
: CheckKind::Access);
1313-
return RequirementCheck(kind, requiredAccessScope);
1337+
return RequirementCheck(kind, getRequiredAccessScope());
13141338
}
13151339

13161340
auto requiredAvailability = AvailabilityContext::alwaysAvailable();
@@ -2340,6 +2364,36 @@ bool ConformanceChecker::checkObjCTypeErasedGenerics(
23402364
return true;
23412365
}
23422366

2367+
namespace {
2368+
/// Helper class for use with ConformanceChecker::diagnoseOrDefer when a witness
2369+
/// needs to be marked as '\@usableFromInline'.
2370+
class DiagnoseUsableFromInline {
2371+
const ValueDecl *witness;
2372+
2373+
public:
2374+
explicit DiagnoseUsableFromInline(const ValueDecl *witness)
2375+
: witness(witness) {
2376+
assert(witness);
2377+
}
2378+
2379+
void operator()(const NormalProtocolConformance *conformance) {
2380+
auto proto = conformance->getProtocol();
2381+
ASTContext &ctx = proto->getASTContext();
2382+
2383+
auto diagID = diag::witness_not_usable_from_inline;
2384+
if (!ctx.isSwiftVersionAtLeast(5))
2385+
diagID = diag::witness_not_usable_from_inline_warn;
2386+
2387+
SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, witness);
2388+
ctx.Diags.diagnose(diagLoc, diagID,
2389+
witness->getDescriptiveKind(),
2390+
witness->getFullName(),
2391+
proto->getName());
2392+
emitDeclaredHereIfNeeded(ctx.Diags, diagLoc, witness);
2393+
}
2394+
};
2395+
}
2396+
23432397
void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType,
23442398
Type type,
23452399
TypeDecl *typeDecl) {
@@ -2357,24 +2411,20 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType,
23572411

23582412
if (typeDecl) {
23592413
// Check access.
2360-
AccessScope requiredAccessScope =
2361-
Adoptee->getAnyNominal()->getFormalAccessScope(DC);
23622414
bool isSetter = false;
2363-
if (checkWitnessAccess(requiredAccessScope, assocType, typeDecl,
2364-
&isSetter)) {
2415+
if (checkWitnessAccess(assocType, typeDecl, &isSetter)) {
23652416
assert(!isSetter);
23662417

23672418
// Avoid relying on the lifetime of 'this'.
23682419
const DeclContext *DC = this->DC;
23692420
diagnoseOrDefer(assocType, false,
2370-
[DC, typeDecl, requiredAccessScope](
2371-
NormalProtocolConformance *conformance) {
2421+
[this, DC, typeDecl](NormalProtocolConformance *conformance) {
23722422
AccessLevel requiredAccess =
2373-
requiredAccessScope.requiredAccessForDiagnostics();
2423+
getRequiredAccessScope().requiredAccessForDiagnostics();
23742424
auto proto = conformance->getProtocol();
23752425
auto protoAccessScope = proto->getFormalAccessScope(DC);
23762426
bool protoForcesAccess =
2377-
requiredAccessScope.hasEqualDeclContextWith(protoAccessScope);
2427+
getRequiredAccessScope().hasEqualDeclContextWith(protoAccessScope);
23782428
auto diagKind = protoForcesAccess
23792429
? diag::type_witness_not_accessible_proto
23802430
: diag::type_witness_not_accessible_type;
@@ -2391,6 +2441,13 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType,
23912441
fixItAccess(fixItDiag, typeDecl, requiredAccess);
23922442
});
23932443
}
2444+
2445+
if (isUsableFromInlineRequired()) {
2446+
bool witnessIsUsableFromInline = typeDecl->getFormalAccessScope(
2447+
DC, /*usableFromInlineAsPublic*/true).isPublic();
2448+
if (!witnessIsUsableFromInline)
2449+
diagnoseOrDefer(assocType, false, DiagnoseUsableFromInline(typeDecl));
2450+
}
23942451
} else {
23952452
// If there was no type declaration, synthesize one.
23962453
auto aliasDecl = new (TC.Context) TypeAliasDecl(SourceLoc(),
@@ -2939,8 +2996,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
29392996
});
29402997
}
29412998

2942-
auto nominalAccessScope = nominal->getFormalAccessScope(DC);
2943-
auto check = checkWitness(nominalAccessScope, requirement, best);
2999+
auto check = checkWitness(requirement, best);
29443000

29453001
switch (check.Kind) {
29463002
case CheckKind::Success:
@@ -5422,7 +5478,7 @@ DefaultWitnessChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
54225478

54235479
// Perform the same checks as conformance witness matching, but silently
54245480
// ignore the candidate instead of diagnosing anything.
5425-
auto check = checkWitness(AccessScope::getPublic(), requirement, best);
5481+
auto check = checkWitness(requirement, best);
54265482
if (check.Kind != CheckKind::Success)
54275483
return ResolveWitnessResult::ExplicitFailed;
54285484

lib/Sema/TypeCheckProtocol.h

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#ifndef SWIFT_SEMA_PROTOCOL_H
1919
#define SWIFT_SEMA_PROTOCOL_H
2020

21+
#include "TypeChecker.h"
22+
#include "swift/AST/AccessScope.h"
2123
#include "swift/AST/RequirementEnvironment.h"
2224
#include "swift/AST/Type.h"
2325
#include "swift/AST/Types.h"
@@ -475,11 +477,24 @@ class WitnessChecker {
475477

476478
RequirementEnvironmentCache ReqEnvironmentCache;
477479

480+
Optional<std::pair<AccessScope, bool>> RequiredAccessScopeAndUsableFromInline;
481+
478482
WitnessChecker(TypeChecker &tc, ProtocolDecl *proto,
479483
Type adoptee, DeclContext *dc);
480484

481485
bool isMemberOperator(FuncDecl *decl, Type type);
482486

487+
AccessScope getRequiredAccessScope();
488+
489+
bool isUsableFromInlineRequired() {
490+
if (!TC.getLangOpts().EnableAccessControl)
491+
return false;
492+
493+
assert(RequiredAccessScopeAndUsableFromInline.hasValue() &&
494+
"must check access first using getRequiredAccessScope");
495+
return RequiredAccessScopeAndUsableFromInline.getValue().second;
496+
}
497+
483498
/// Gather the value witnesses for the given requirement.
484499
///
485500
/// \param ignoringNames If non-null and there are no value
@@ -501,17 +516,15 @@ class WitnessChecker {
501516
unsigned &bestIdx,
502517
bool &doNotDiagnoseMatches);
503518

504-
bool checkWitnessAccess(AccessScope &requiredAccessScope,
505-
ValueDecl *requirement,
519+
bool checkWitnessAccess(ValueDecl *requirement,
506520
ValueDecl *witness,
507521
bool *isSetter);
508522

509523
bool checkWitnessAvailability(ValueDecl *requirement,
510524
ValueDecl *witness,
511525
AvailabilityContext *requirementInfo);
512526

513-
RequirementCheck checkWitness(AccessScope requiredAccessScope,
514-
ValueDecl *requirement,
527+
RequirementCheck checkWitness(ValueDecl *requirement,
515528
const RequirementMatch &match);
516529
};
517530

stdlib/public/core/ArrayBuffer.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ extension _ArrayBuffer {
504504
return count
505505
}
506506

507+
@usableFromInline
507508
internal typealias Indices = Range<Int>
508509

509510
//===--- private --------------------------------------------------------===//

stdlib/public/core/CocoaArray.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ import SwiftShims
2828
@usableFromInline
2929
@_fixed_layout
3030
internal struct _CocoaArrayWrapper : RandomAccessCollection {
31+
@usableFromInline
3132
typealias Indices = Range<Int>
33+
3234
@inlinable
3335
internal var startIndex: Int {
3436
return 0

stdlib/public/core/ContiguousArrayBuffer.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,7 @@ extension _ContiguousArrayBuffer : RandomAccessCollection {
551551
return count
552552
}
553553

554+
@usableFromInline
554555
internal typealias Indices = Range<Int>
555556
}
556557

stdlib/public/core/SliceBuffer.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ internal struct _SliceBuffer<Element>
344344
}
345345
}
346346

347+
@usableFromInline
347348
internal typealias Indices = Range<Int>
348349

349350
//===--- misc -----------------------------------------------------------===//
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 4
2+
// RUN: %target-typecheck-verify-swift -swift-version 4.2
3+
4+
public protocol PublicProtoWithReqs {
5+
associatedtype Assoc
6+
func foo()
7+
}
8+
9+
@usableFromInline struct UFIAdopter<T> : PublicProtoWithReqs {}
10+
// expected-warning@-1 {{type alias 'Assoc' should be declared '@usableFromInline' because because it matches a requirement in protocol 'PublicProtoWithReqs'}} {{none}}
11+
extension UFIAdopter {
12+
typealias Assoc = Int
13+
// expected-note@-1 {{'Assoc' declared here}}
14+
func foo() {}
15+
}
16+
17+
@usableFromInline struct UFIAdopterAllInOne<T> : PublicProtoWithReqs {
18+
typealias Assoc = Int
19+
// expected-warning@-1 {{type alias 'Assoc' should be declared '@usableFromInline' because because it matches a requirement in protocol 'PublicProtoWithReqs'}} {{none}}
20+
func foo() {}
21+
}
22+
23+
internal struct InternalAdopter<T> : PublicProtoWithReqs {}
24+
extension InternalAdopter {
25+
typealias Assoc = Int // okay
26+
func foo() {} // okay
27+
}
28+
29+
30+
@usableFromInline protocol UFIProtoWithReqs {
31+
associatedtype Assoc
32+
func foo()
33+
}
34+
35+
public struct PublicAdopter<T> : UFIProtoWithReqs {}
36+
// expected-warning@-1 {{type alias 'Assoc' should be declared '@usableFromInline' because because it matches a requirement in protocol 'UFIProtoWithReqs'}} {{none}}
37+
extension PublicAdopter {
38+
typealias Assoc = Int
39+
// expected-note@-1 {{'Assoc' declared here}}
40+
func foo() {}
41+
}
42+
extension InternalAdopter: UFIProtoWithReqs {} // okay
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 5
2+
// RUN: %target-swift-frontend -typecheck -disable-access-control %s
3+
4+
public protocol PublicProtoWithReqs {
5+
associatedtype Assoc
6+
func foo()
7+
}
8+
9+
@usableFromInline struct UFIAdopter<T> : PublicProtoWithReqs {}
10+
// expected-error@-1 {{type alias 'Assoc' must be declared '@usableFromInline' because because it matches a requirement in protocol 'PublicProtoWithReqs'}} {{none}}
11+
extension UFIAdopter {
12+
typealias Assoc = Int
13+
// expected-note@-1 {{'Assoc' declared here}}
14+
func foo() {}
15+
}
16+
17+
@usableFromInline struct UFIAdopterAllInOne<T> : PublicProtoWithReqs {
18+
typealias Assoc = Int
19+
// expected-error@-1 {{type alias 'Assoc' must be declared '@usableFromInline' because because it matches a requirement in protocol 'PublicProtoWithReqs'}} {{none}}
20+
func foo() {}
21+
}
22+
23+
internal struct InternalAdopter<T> : PublicProtoWithReqs {}
24+
extension InternalAdopter {
25+
typealias Assoc = Int // okay
26+
func foo() {} // okay
27+
}
28+
29+
30+
@usableFromInline protocol UFIProtoWithReqs {
31+
associatedtype Assoc
32+
func foo()
33+
}
34+
35+
public struct PublicAdopter<T> : UFIProtoWithReqs {}
36+
// expected-error@-1 {{type alias 'Assoc' must be declared '@usableFromInline' because because it matches a requirement in protocol 'UFIProtoWithReqs'}} {{none}}
37+
extension PublicAdopter {
38+
typealias Assoc = Int
39+
// expected-note@-1 {{'Assoc' declared here}}
40+
func foo() {}
41+
}
42+
extension InternalAdopter: UFIProtoWithReqs {} // okay

0 commit comments

Comments
 (0)