Skip to content

Commit 3447bd1

Browse files
committed
Change implementation to infer @inlinable semantics from @inline(always) on public declarations
1 parent 25a071e commit 3447bd1

File tree

9 files changed

+85
-30
lines changed

9 files changed

+85
-30
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3094,6 +3094,10 @@ class ValueDecl : public Decl {
30943094
/// \c \@usableFromInline, \c \@inlinalbe, and \c \@_alwaysEmitIntoClient
30953095
bool isUsableFromInline() const;
30963096

3097+
// Returns \c true if this value decl is marked with an attribute that implies
3098+
// \c \@inlinable semantics: either \c \@inlinable or \c \@inline(always)
3099+
bool hasAttributeWithInlinableSemantics() const;
3100+
30973101
/// Returns \c true if this declaration is *not* intended to be used directly
30983102
/// by application developers despite the visibility.
30993103
bool shouldHideFromEditor() const;

lib/AST/Attr.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,9 +1143,13 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
11431143
cast<InlineAttr>(this)->getKind() == InlineKind::Always &&
11441144
Options.SuppressInlineAlways) {
11451145
attrName = "inline(__always)";
1146+
Printer.printSimpleAttr(attrName, /*needAt=*/true);
1147+
Printer << ' ';
1148+
// Add @inlinable
1149+
Printer.printSimpleAttr("inlinable", /*needAt=*/true);
1150+
} else {
1151+
Printer.printSimpleAttr(attrName, /*needAt=*/true);
11461152
}
1147-
1148-
Printer.printSimpleAttr(attrName, /*needAt=*/true);
11491153
}
11501154
return true;
11511155

lib/AST/Decl.cpp

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4930,6 +4930,25 @@ SourceLoc Decl::getAttributeInsertionLoc(bool forModifier) const {
49304930
return resultLoc;
49314931
}
49324932

4933+
bool ValueDecl::hasAttributeWithInlinableSemantics() const {
4934+
if (getAttrs().hasAttribute<InlinableAttr>())
4935+
return true;
4936+
4937+
// @inline(always) implies @inlinable on "public" (open, public, package)
4938+
// declarations.
4939+
AccessScope access =
4940+
getFormalAccessScope(nullptr, /*treatUsableFromInlineAsPublic*/false);
4941+
if (!access.isPublicOrPackage())
4942+
return false;
4943+
4944+
if (auto *inlineAttr = getAttrs().getAttribute<InlineAttr>()) {
4945+
if (inlineAttr && inlineAttr->getKind() == InlineKind::Always)
4946+
return true;
4947+
}
4948+
4949+
return false;
4950+
}
4951+
49334952
/// Returns true if \p VD needs to be treated as publicly-accessible
49344953
/// at the SIL, LLVM, and machine levels due to being @usableFromInline.
49354954
bool ValueDecl::isUsableFromInline() const {
@@ -4942,22 +4961,22 @@ bool ValueDecl::isUsableFromInline() const {
49424961

49434962
if (getAttrs().hasAttribute<UsableFromInlineAttr>() ||
49444963
getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>() ||
4945-
getAttrs().hasAttribute<InlinableAttr>())
4964+
hasAttributeWithInlinableSemantics())
49464965
return true;
49474966

49484967
if (auto *accessor = dyn_cast<AccessorDecl>(this)) {
49494968
auto *storage = accessor->getStorage();
49504969
if (storage->getAttrs().hasAttribute<UsableFromInlineAttr>() ||
49514970
storage->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>() ||
4952-
storage->getAttrs().hasAttribute<InlinableAttr>())
4971+
storage->hasAttributeWithInlinableSemantics())
49534972
return true;
49544973
}
49554974

49564975
if (auto *opaqueType = dyn_cast<OpaqueTypeDecl>(this)) {
49574976
if (auto *namingDecl = opaqueType->getNamingDecl()) {
49584977
if (namingDecl->getAttrs().hasAttribute<UsableFromInlineAttr>() ||
49594978
namingDecl->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>() ||
4960-
namingDecl->getAttrs().hasAttribute<InlinableAttr>())
4979+
namingDecl->hasAttributeWithInlinableSemantics())
49614980
return true;
49624981
}
49634982
}
@@ -5576,7 +5595,7 @@ void ValueDecl::copyFormalAccessFrom(const ValueDecl *source,
55765595
// Inherit the @usableFromInline attribute.
55775596
if (source->getAttrs().hasAttribute<UsableFromInlineAttr>() &&
55785597
!getAttrs().hasAttribute<UsableFromInlineAttr>() &&
5579-
!getAttrs().hasAttribute<InlinableAttr>() &&
5598+
!hasAttributeWithInlinableSemantics() &&
55805599
DeclAttribute::canAttributeAppearOnDecl(DeclAttrKind::UsableFromInline,
55815600
this)) {
55825601
auto &ctx = getASTContext();
@@ -10712,7 +10731,7 @@ bool AbstractFunctionDecl::isValidKeyPathComponent() const {
1071210731

1071310732
bool AbstractFunctionDecl::isResilient() const {
1071410733
// Check for attributes that makes functions non-resilient.
10715-
if (getAttrs().hasAttribute<InlinableAttr>() &&
10734+
if (hasAttributeWithInlinableSemantics() &&
1071610735
getAttrs().hasAttribute<UsableFromInlineAttr>())
1071710736
return false;
1071810737

lib/AST/DeclContext.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator,
556556
return {FragileFunctionKind::Transparent};
557557
}
558558

559-
if (AFD->getAttrs().hasAttribute<InlinableAttr>()) {
559+
if (AFD->hasAttributeWithInlinableSemantics()) {
560560
return {FragileFunctionKind::Inlinable};
561561
}
562562

@@ -572,7 +572,7 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator,
572572
// @backDeployed, and @inlinable from their storage declarations.
573573
if (auto accessor = dyn_cast<AccessorDecl>(AFD)) {
574574
auto *storage = accessor->getStorage();
575-
if (storage->getAttrs().getAttribute<InlinableAttr>()) {
575+
if (storage->hasAttributeWithInlinableSemantics()) {
576576
return {FragileFunctionKind::Inlinable};
577577
}
578578
if (storage->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>()) {

lib/Sema/CodeSynthesis.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,14 @@ configureInheritedDesignatedInitAttributes(ClassDecl *classDecl,
590590
// Inherit the @inlinable attribute.
591591
auto *clonedAttr = new (ctx) InlinableAttr(/*implicit=*/true);
592592
ctor->getAttrs().add(clonedAttr);
593-
593+
} else if (superclassCtor->getAttrs().hasAttribute<InlineAttr>() &&
594+
superclassCtor->getAttrs().getAttribute<InlineAttr>()->getKind()
595+
== InlineKind::Always) {
596+
// Inherit the @inline(always) attribute.
597+
auto *clonedAttr = new (ctx) InlineAttr(SourceLoc(), SourceRange(),
598+
InlineKind::Always,
599+
/*implicit=*/true);
600+
ctor->getAttrs().add(clonedAttr);
594601
} else if (access == AccessLevel::Internal && !superclassCtor->isDynamic()){
595602
// Inherit the @usableFromInline attribute.
596603
auto *clonedAttr = new (ctx) UsableFromInlineAttr(/*implicit=*/true);

lib/Sema/TypeCheckAttr.cpp

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3707,19 +3707,8 @@ void AttributeChecker::visitInlineAttr(InlineAttr *attr) {
37073707
if (attr->getKind() != InlineKind::Always)
37083708
return;
37093709

3710-
// Check '@inline(always)' properties.
3711-
auto *VD = cast<ValueDecl>(D);
3712-
auto access = VD->getFormalAccess();
3713-
3714-
// @inline(always) requires @inlinable for package and public declarations.
3715-
if (access >= AccessLevel::Package &&
3716-
!(VD->getAttrs().hasAttribute<InlinableAttr>() ||
3717-
VD->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())) {
3718-
diagnoseAndRemoveAttr(attr, diag::attr_inline_always_requires_inlinable);
3719-
return;
3720-
}
3721-
37223710
// Can't have @inline(always) on @usableFromInline.
3711+
auto *VD = cast<ValueDecl>(D);
37233712
if (VD->getAttrs().hasAttribute<UsableFromInlineAttr>()) {
37243713
diagnoseAndRemoveAttr(attr, diag::attr_inline_always_no_usable_from_inline);
37253714
return;

test/ModuleInterface/attr-inline-always.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@
1313
// REQUIRES: swift_feature_InlineAlways
1414

1515
// CHECK: #if compiler(>=5.3) && $InlineAlways
16-
// CHECK: @inline(always) @inlinable public func inlineAlwaysFunc() {}
16+
// CHECK: @inline(always) public func inlineAlwaysFunc() {}
1717
// CHECK: #else
1818
// CHECK: @inline(__always) @inlinable public func inlineAlwaysFunc() {}
1919
// CHECK: #endif
2020
@inline(always)
21-
@inlinable
2221
public func inlineAlwaysFunc() {}

test/Sema/inline_always.swift

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -package-name pkg -disable-availability-checking -enable-experimental-feature InlineAlways
1+
// RUN: %target-typecheck-verify-swift -package-name pkg -enable-experimental-feature InlineAlways
22

33
// REQUIRES: swift_feature_InlineAlways
44

@@ -17,6 +17,9 @@ package func packageInlinableFunc() {}
1717
@inline(always) // okay
1818
internal func internalFunc() {}
1919

20+
@usableFromInline
21+
internal func internalFuncUFI() {}
22+
2023
@inline(always) // okay
2124
@inlinable
2225
func internalInlinable() {}
@@ -27,12 +30,43 @@ private func privateFunc() {}
2730
@inline(always) // okay
2831
fileprivate func filePrivateFunc() {}
2932

30-
@inline(always) // // expected-error{{'@inline(always)' on public or package declarations must be used together with '@inlinable'}}
31-
public func publicFunc() {}
33+
@inline(always) // okay, implies @inlinable
34+
public func publicFunc() {
35+
internalFuncUFI() // okay because useableFromInline
36+
}
37+
38+
func internalFunc2() {} //expected-note{{global function 'internalFunc2()' is not '@usableFromInline' or public}}
39+
40+
@inline(always) // implies @inlinable
41+
public func publicFuncUsingInternal() {
42+
internalFunc2() //expected-error{{global function 'internalFunc2()' is internal and cannot be referenced from an '@inlinable' function}}
43+
}
44+
45+
func internalFunc3() {} //expected-note{{global function 'internalFunc3()' is not '@usableFromInline' or public}}
3246

33-
@inline(always) // // expected-error{{'@inline(always)' on public or package declarations must be used together with '@inlinable'}}
47+
@inline(always) // implies @inlinable
48+
package func packageFuncUsingInternal() {
49+
internalFunc3() //expected-error{{global function 'internalFunc3()' is internal and cannot be referenced from an '@inlinable' function}}
50+
}
51+
52+
@inline(always)
53+
func internalUsingInternal() {
54+
internalFunc2()
55+
}
56+
57+
@inline(always) // okay
3458
package func packageFunc() {}
3559

3660
@inline(always) // expected-error{{cannot use '@inline(always)' together with '@usableFromInline'}}
3761
@usableFromInline
3862
func internalUFI() {}
63+
64+
func internalFunc4() {} //expected-note{{global function 'internalFunc4()' is not '@usableFromInline' or public}}
65+
66+
@inline(always)
67+
public var x: Int {
68+
get {
69+
internalFunc4() // expected-error{{global function 'internalFunc4()' is internal and cannot be referenced from an '@inlinable' function}}
70+
return 1
71+
}
72+
}

test/Serialization/attr-inline-always.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77

88
// BC-CHECK: <Inline_DECL_ATTR
99

10-
// MODULE-CHECK: @inline(always) @inlinable func inlineAlwaysFunc()
10+
// MODULE-CHECK: @inline(always) func inlineAlwaysFunc()
1111

1212
@inline(always)
13-
@inlinable
1413
public func inlineAlwaysFunc() {}

0 commit comments

Comments
 (0)