Skip to content

Commit d65d1d2

Browse files
committed
Sema: Diagnostics for @_inlineable, @_versioned and Swift 4 default arguments model
Piggybacks some resilience diagnostics onto the availability checking code. Public and versioned functions with inlineable bodies can only reference other public and internal entities, since the SIL code for the function body is serialized and stored as part of the module. This includes @_transparent functions, @_inlineable functions, accessors for @_inlineable storage, @inline(__always) functions, and in Swift 4 mode, default argument expressions. The new checks are a source-breaking change, however we don't guarantee source compatibility for underscored attributes. The new ABI and tests for the default argument model will come in subsequent commits.
1 parent 5eb16e6 commit d65d1d2

30 files changed

+437
-122
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3295,6 +3295,35 @@ NOTE(availability_protocol_requirement_here, none,
32953295
NOTE(availability_conformance_introduced_here, none,
32963296
"conformance introduced here", ())
32973297

3298+
//------------------------------------------------------------------------------
3299+
// Resilience diagnostics
3300+
//------------------------------------------------------------------------------
3301+
3302+
ERROR(versioned_attr_with_explicit_accessibility,
3303+
none, "'@_versioned' attribute can only be applied to internal "
3304+
"declarations, but %0 is %select{private|fileprivate|%error|public}1",
3305+
(Identifier, Accessibility))
3306+
3307+
#define FRAGILE_FUNC_KIND \
3308+
"%select{a '@_transparent' function|" \
3309+
"an '@inline(__always)' function|" \
3310+
"an '@_inlineable' function|" \
3311+
"a default argument value}"
3312+
3313+
ERROR(local_type_in_inlineable_function,
3314+
none, "type %0 cannot be nested inside " FRAGILE_FUNC_KIND "1",
3315+
(DeclName, unsigned))
3316+
3317+
ERROR(resilience_decl_unavailable,
3318+
none, "%0 %1 is %select{private|fileprivate|internal|%error}2 and "
3319+
"cannot be referenced from " FRAGILE_FUNC_KIND "3",
3320+
(DescriptiveDeclKind, DeclName, Accessibility, unsigned))
3321+
3322+
NOTE(resilience_decl_declared_here,
3323+
none, "%0 %1 is not '@_versioned' or public", (DescriptiveDeclKind, DeclName))
3324+
3325+
#undef FRAGILE_FUNC_KIND
3326+
32983327
//------------------------------------------------------------------------------
32993328
// Variable usage diagnostics
33003329
//------------------------------------------------------------------------------
@@ -3337,7 +3366,7 @@ ERROR(circular_reference, none,
33373366
"circular reference", ())
33383367

33393368
ERROR(redundant_type_alias_define, none,
3340-
"redundant type alias declaration", ())
3369+
"redundant type alias declaration", ())
33413370

33423371
NOTE(circular_reference_through, none,
33433372
"through reference here", ())

include/swift/AST/Initializer.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,16 +148,26 @@ class DefaultArgumentInitializer : public Initializer {
148148
public:
149149
explicit DefaultArgumentInitializer(DeclContext *parent, unsigned index)
150150
: Initializer(InitializerKind::DefaultArgument, parent) {
151-
SpareBits = index;
151+
SpareBits = (unsigned(ResilienceExpansion::Maximal) | index << 1);
152152
}
153153

154-
unsigned getIndex() const { return SpareBits; }
154+
unsigned getIndex() const { return SpareBits >> 1; }
155+
156+
ResilienceExpansion getResilienceExpansion() const {
157+
return ResilienceExpansion(SpareBits & 1);
158+
}
155159

156160
/// Change the parent of this context. This is necessary because
157161
/// the function signature is parsed before the function
158162
/// declaration/expression itself is built.
159163
void changeFunction(AbstractFunctionDecl *parent);
160-
164+
165+
/// Change the resilience expansion of this context, necessary
166+
/// for the same reason as above.
167+
void changeResilienceExpansion(ResilienceExpansion expansion) {
168+
SpareBits = (SpareBits & ~1) | unsigned(expansion);
169+
}
170+
161171
static bool classof(const DeclContext *DC) {
162172
if (auto init = dyn_cast<Initializer>(DC))
163173
return classof(init);

lib/AST/DeclContext.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -469,13 +469,20 @@ bool DeclContext::isGenericContext() const {
469469
/// are used.
470470
ResilienceExpansion DeclContext::getResilienceExpansion() const {
471471
for (const auto *dc = this; dc->isLocalContext(); dc = dc->getParent()) {
472+
// Default argument initializer contexts have their resilience expansion
473+
// set when they're type checked.
474+
if (auto *DAI = dyn_cast<DefaultArgumentInitializer>(dc))
475+
return DAI->getResilienceExpansion();
476+
472477
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(dc)) {
473478
// If the function is a nested function, we will serialize its body if
474479
// we serialize the parent's body.
475480
if (AFD->getDeclContext()->isLocalContext())
476481
continue;
477482

478-
if (AFD->isInvalid())
483+
// FIXME: Make sure this method is never called on decls that have not
484+
// been fully validated.
485+
if (!AFD->hasAccessibility())
479486
break;
480487

481488
// If the function is not externally visible, we will not be serializing
@@ -495,8 +502,8 @@ ResilienceExpansion DeclContext::getResilienceExpansion() const {
495502
if (attr->getKind() == InlineKind::Always)
496503
return ResilienceExpansion::Minimal;
497504

498-
// If a property or subscript is @_fragile, the accessors are
499-
// @_fragile also.
505+
// If a property or subscript is @_inlineable, the accessors are
506+
// @_inlineable also.
500507
if (auto FD = dyn_cast<FuncDecl>(AFD))
501508
if (auto *ASD = FD->getAccessorStorageDecl())
502509
if (ASD->getAttrs().getAttribute<InlineableAttr>())
@@ -505,7 +512,6 @@ ResilienceExpansion DeclContext::getResilienceExpansion() const {
505512
}
506513

507514
return ResilienceExpansion::Maximal;
508-
509515
}
510516

511517
/// Determine whether the innermost context is generic.

lib/Sema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ add_swift_library(swiftSema STATIC
2323
NameBinding.cpp
2424
PCMacro.cpp
2525
PlaygroundTransform.cpp
26+
ResilienceDiagnostics.cpp
2627
SourceLoader.cpp
2728
TypeCheckAttr.cpp
2829
TypeCheckAvailability.cpp

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//===--- ResilienceDiagnostics.cpp - Resilience Inlineability Diagnostics -===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 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+
// This file implements diagnostics for @inlineable.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#include "TypeChecker.h"
18+
#include "swift/AST/Attr.h"
19+
#include "swift/AST/Decl.h"
20+
#include "swift/AST/Initializer.h"
21+
#include "swift/AST/DeclContext.h"
22+
using namespace swift;
23+
24+
enum FragileFunctionKind : unsigned {
25+
Transparent,
26+
InlineAlways,
27+
Inlineable,
28+
DefaultArgument
29+
};
30+
31+
FragileFunctionKind getFragileFunctionKind(const DeclContext *DC) {
32+
for (; DC->isLocalContext(); DC = DC->getParent()) {
33+
if (auto *DAI = dyn_cast<DefaultArgumentInitializer>(DC))
34+
if (DAI->getResilienceExpansion() == ResilienceExpansion::Minimal)
35+
return FragileFunctionKind::DefaultArgument;
36+
37+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(DC)) {
38+
// If the function is a nested function, we will serialize its body if
39+
// we serialize the parent's body.
40+
if (AFD->getDeclContext()->isLocalContext())
41+
continue;
42+
43+
// Bodies of public transparent and always-inline functions are
44+
// serialized, so use conservative access patterns.
45+
if (AFD->isTransparent())
46+
return FragileFunctionKind::Transparent;
47+
48+
if (AFD->getAttrs().hasAttribute<InlineableAttr>())
49+
return FragileFunctionKind::Inlineable;
50+
51+
if (auto attr = AFD->getAttrs().getAttribute<InlineAttr>())
52+
if (attr->getKind() == InlineKind::Always)
53+
return FragileFunctionKind::InlineAlways;
54+
55+
// If a property or subscript is @_inlineable, the accessors are
56+
// @_inlineable also.
57+
if (auto FD = dyn_cast<FuncDecl>(AFD))
58+
if (auto *ASD = FD->getAccessorStorageDecl())
59+
if (ASD->getAttrs().getAttribute<InlineableAttr>())
60+
return FragileFunctionKind::Inlineable;
61+
}
62+
}
63+
64+
llvm_unreachable("Context is not nested inside a fragile function");
65+
}
66+
67+
void TypeChecker::diagnoseInlineableLocalType(const NominalTypeDecl *NTD) {
68+
auto *DC = NTD->getDeclContext();
69+
auto expansion = DC->getResilienceExpansion();
70+
if (expansion == ResilienceExpansion::Minimal) {
71+
diagnose(NTD, diag::local_type_in_inlineable_function,
72+
NTD->getFullName(), getFragileFunctionKind(DC));
73+
}
74+
}
75+
76+
bool TypeChecker::diagnoseInlineableDeclRef(SourceLoc loc,
77+
const ValueDecl *D,
78+
const DeclContext *DC) {
79+
auto expansion = DC->getResilienceExpansion();
80+
if (expansion == ResilienceExpansion::Minimal) {
81+
if (!isa<GenericTypeParamDecl>(D) &&
82+
// FIXME: Figure out what to do with typealiases
83+
!isa<TypeAliasDecl>(D) &&
84+
!D->getDeclContext()->isLocalContext() &&
85+
D->hasAccessibility()) {
86+
if (D->getEffectiveAccess() < Accessibility::Public) {
87+
diagnose(loc, diag::resilience_decl_unavailable,
88+
D->getDescriptiveKind(), D->getFullName(),
89+
D->getFormalAccess(), getFragileFunctionKind(DC));
90+
diagnose(D, diag::resilience_decl_declared_here,
91+
D->getDescriptiveKind(), D->getFullName());
92+
return true;
93+
}
94+
}
95+
}
96+
97+
return false;
98+
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -727,9 +727,6 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
727727
IGNORED_ATTR(WarnUnqualifiedAccess)
728728
IGNORED_ATTR(ShowInInterface)
729729
IGNORED_ATTR(DiscardableResult)
730-
731-
// FIXME: We actually do have things to enforce for versioned API.
732-
IGNORED_ATTR(Versioned)
733730
#undef IGNORED_ATTR
734731

735732
void visitAvailableAttr(AvailableAttr *attr);
@@ -764,6 +761,8 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
764761
void visitPrefixAttr(PrefixAttr *attr) { checkOperatorAttribute(attr); }
765762

766763
void visitSpecializeAttr(SpecializeAttr *attr);
764+
765+
void visitVersionedAttr(VersionedAttr *attr);
767766
};
768767
} // end anonymous namespace
769768

@@ -1522,6 +1521,19 @@ void AttributeChecker::visitSpecializeAttr(SpecializeAttr *attr) {
15221521
ConcreteDeclRef(DC->getASTContext(), FD, substitutions));
15231522
}
15241523

1524+
void AttributeChecker::visitVersionedAttr(VersionedAttr *attr) {
1525+
auto *VD = cast<ValueDecl>(D);
1526+
1527+
if (VD->getFormalAccess() != Accessibility::Internal) {
1528+
TC.diagnose(attr->getLocation(),
1529+
diag::versioned_attr_with_explicit_accessibility,
1530+
VD->getName(),
1531+
VD->getFormalAccess())
1532+
.fixItRemove(attr->getRangeWithAt());
1533+
attr->setInvalid();
1534+
}
1535+
}
1536+
15251537
void TypeChecker::checkDeclAttributes(Decl *D) {
15261538
AttributeChecker Checker(*this, D);
15271539

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2259,6 +2259,10 @@ bool AvailabilityWalker::diagAvailability(const ValueDecl *D, SourceRange R,
22592259
return true;
22602260
}
22612261

2262+
if (R.isValid())
2263+
if (TC.diagnoseInlineableDeclRef(R.Start, D, DC))
2264+
return true;
2265+
22622266
if (TC.diagnoseExplicitUnavailability(D, R, DC, call))
22632267
return true;
22642268

lib/Sema/TypeCheckDecl.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3940,7 +3940,9 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
39403940
TC.validateDecl(assocType);
39413941
}
39423942

3943-
void checkUnsupportedNestedGeneric(NominalTypeDecl *NTD) {
3943+
void checkUnsupportedNestedType(NominalTypeDecl *NTD) {
3944+
TC.diagnoseInlineableLocalType(NTD);
3945+
39443946
// We don't support protocols outside the top level of a file.
39453947
if (isa<ProtocolDecl>(NTD) &&
39463948
!NTD->getParent()->isModuleScopeContext()) {
@@ -3988,7 +3990,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
39883990
TC.computeAccessibility(ED);
39893991

39903992
if (!IsSecondPass) {
3991-
checkUnsupportedNestedGeneric(ED);
3993+
checkUnsupportedNestedType(ED);
39923994

39933995
TC.validateDecl(ED);
39943996

@@ -4045,7 +4047,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
40454047
TC.computeAccessibility(SD);
40464048

40474049
if (!IsSecondPass) {
4048-
checkUnsupportedNestedGeneric(SD);
4050+
checkUnsupportedNestedType(SD);
40494051

40504052
TC.validateDecl(SD);
40514053
TC.ValidatedTypes.remove(SD);
@@ -4174,7 +4176,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
41744176
TC.computeAccessibility(CD);
41754177

41764178
if (!IsSecondPass) {
4177-
checkUnsupportedNestedGeneric(CD);
4179+
checkUnsupportedNestedType(CD);
41784180

41794181
TC.validateDecl(CD);
41804182
if (!CD->hasValidSignature())
@@ -4287,7 +4289,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
42874289
TC.computeAccessibility(PD);
42884290

42894291
if (!IsSecondPass)
4290-
checkUnsupportedNestedGeneric(PD);
4292+
checkUnsupportedNestedType(PD);
42914293

42924294
if (IsSecondPass) {
42934295
checkAccessibility(TC, PD);

lib/Sema/TypeCheckStmt.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,9 +1206,16 @@ Stmt *StmtChecker::visitBraceStmt(BraceStmt *BS) {
12061206
}
12071207

12081208
/// Check the default arguments that occur within this pattern.
1209-
static void checkDefaultArguments(TypeChecker &tc, ParameterList *params,
1210-
unsigned &nextArgIndex, DeclContext *dc) {
1211-
assert(dc->isLocalContext());
1209+
static void checkDefaultArguments(TypeChecker &tc,
1210+
ParameterList *params,
1211+
unsigned &nextArgIndex,
1212+
AbstractFunctionDecl *func) {
1213+
// In Swift 4 mode, default argument bodies are inlined into the
1214+
// caller.
1215+
auto expansion = func->getResilienceExpansion();
1216+
if (!tc.Context.isSwiftVersion3() &&
1217+
func->getEffectiveAccess() == Accessibility::Public)
1218+
expansion = ResilienceExpansion::Minimal;
12121219

12131220
for (auto &param : *params) {
12141221
++nextArgIndex;
@@ -1219,6 +1226,9 @@ static void checkDefaultArguments(TypeChecker &tc, ParameterList *params,
12191226
Expr *e = param->getDefaultValue();
12201227
auto initContext = param->getDefaultArgumentInitContext();
12211228

1229+
cast<DefaultArgumentInitializer>(initContext)
1230+
->changeResilienceExpansion(expansion);
1231+
12221232
// Type-check the initializer, then flag that we did so.
12231233
if (!tc.typeCheckExpression(e, initContext,
12241234
TypeLoc::withoutLoc(param->getType()),

lib/Sema/TypeChecker.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,6 +1888,13 @@ class TypeChecker final : public LazyResolver {
18881888

18891889
ArchetypeBuilder createArchetypeBuilder(ModuleDecl *mod);
18901890

1891+
/// \name Resilience diagnostics
1892+
1893+
void diagnoseInlineableLocalType(const NominalTypeDecl *NTD);
1894+
1895+
bool diagnoseInlineableDeclRef(SourceLoc loc, const ValueDecl *D,
1896+
const DeclContext *DC);
1897+
18911898
/// \name Availability checking
18921899
///
18931900
/// Routines that perform API availability checking and type checking of

stdlib/public/core/ArrayBuffer.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ internal struct _ArrayBuffer<Element> : _ArrayBufferProtocol {
8080
_storage = storage
8181
}
8282

83+
@_versioned
8384
internal var _storage: _ArrayBridgeStorage
8485
}
8586

stdlib/public/core/Bool.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
/// have a consistent type interface.
6262
@_fixed_layout
6363
public struct Bool {
64+
@_versioned
6465
internal var _value: Builtin.Int1
6566

6667
/// Creates an instance initialized to `false`.

stdlib/public/core/BridgeStorage.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,5 +150,6 @@ struct _BridgeStorage<
150150

151151
// rawValue is passed inout to _isUnique. Although its value
152152
// is unchanged, it must appear mutable to the optimizer.
153+
@_versioned
153154
internal var rawValue: Builtin.BridgeObject
154155
}

stdlib/public/core/CTypes.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public typealias CBool = Bool
7878
/// cannot be represented in Swift, such as incomplete struct types.
7979
@_fixed_layout
8080
public struct OpaquePointer : Hashable {
81+
@_versioned
8182
internal var _rawValue: Builtin.RawPointer
8283

8384
@_versioned

0 commit comments

Comments
 (0)