Skip to content

Commit cfd0d6a

Browse files
Merge pull request #84624 from aschwaighofer/inline_always_error_non_final_class_methods
Error on usage of `@inline(always)` on non-final class methods
2 parents bf30e7b + 7ceebbe commit cfd0d6a

File tree

3 files changed

+97
-0
lines changed

3 files changed

+97
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8928,6 +8928,12 @@ ERROR(attr_inline_always_requires_inlinable,none,
89288928
"together with '@inlinable'", ())
89298929
ERROR(attr_inline_always_no_usable_from_inline,none,
89308930
"cannot use '@inline(always)' together with '@usableFromInline'", ())
8931+
ERROR(attr_inline_always_requires_final_method,none,
8932+
"'@inline(always)' on class %select{methods|vars}1 requires %0 to be "
8933+
"marked 'final'", (DeclName, bool))
8934+
ERROR(attr_inline_always_on_accessor,none,
8935+
"'@inline(always)' on class variable accessors whose variable declaration"
8936+
" is not final are not allowed", ())
89318937

89328938
#define UNDEFINE_DIAGNOSTIC_MACROS
89338939
#include "DefineDiagnosticMacros.h"

lib/Sema/TypeCheckAttr.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3713,6 +3713,37 @@ void AttributeChecker::visitInlineAttr(InlineAttr *attr) {
37133713
diagnoseAndRemoveAttr(attr, diag::attr_inline_always_no_usable_from_inline);
37143714
return;
37153715
}
3716+
// Check that @inline(always) is only used on statically dispatched methods
3717+
// (final, static, and methods in extensions mostly).
3718+
bool isAccessorDecl = false;
3719+
if (auto *accessorDecl = dyn_cast<AccessorDecl>(D)) {
3720+
VD = accessorDecl->getStorage();
3721+
isAccessorDecl = true;
3722+
}
3723+
3724+
auto dc = dyn_cast<ClassDecl>(VD->getDeclContext());
3725+
if (!dc)
3726+
return;
3727+
// In a `class` context.
3728+
3729+
if (dc->isActor())
3730+
return;
3731+
3732+
if (auto *vd = dyn_cast<VarDecl>(VD)) {
3733+
if (vd->isFinal())
3734+
return;
3735+
if (isAccessorDecl) {
3736+
diagnoseAndRemoveAttr(attr, diag::attr_inline_always_on_accessor);
3737+
} else {
3738+
diagnoseAndRemoveAttr(attr, diag::attr_inline_always_requires_final_method,
3739+
vd->getName(), true/*isVar*/);
3740+
}
3741+
} else if (auto *afd = dyn_cast<AbstractFunctionDecl>(VD)) {
3742+
if (afd->isFinal())
3743+
return;
3744+
diagnoseAndRemoveAttr(attr, diag::attr_inline_always_requires_final_method,
3745+
afd->getName(), false/*isVar*/);
3746+
}
37163747
}
37173748

37183749
void AttributeChecker::visitInlinableAttr(InlinableAttr *attr) {

test/Sema/inline_always.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,63 @@ public var x: Int {
7070
return 1
7171
}
7272
}
73+
74+
class C {
75+
static func staticMethodInA() {} // okay
76+
77+
@inline(always)
78+
final func myMethod() {} // okay
79+
80+
@inline(always) // expected-error {{'@inline(always)' on class methods requires 'myMethod2()' to be marked 'final'}}
81+
func myMethod2() {}
82+
83+
@inline(always)
84+
final var myVarFinal : Int { // okay
85+
return 0
86+
}
87+
88+
@inline(always) // expected-error {{'@inline(always)' on class vars requires 'myVar' to be marked 'final'}}
89+
var myVar : Int {
90+
return 0
91+
}
92+
93+
@inline(always)
94+
final var myVar2Final: Int { // okay
95+
get {
96+
return 1
97+
}
98+
}
99+
100+
@inline(always) // expected-error {{'@inline(always)' on class vars requires 'myVar2' to be marked 'final'}}
101+
var myVar2: Int {
102+
get {
103+
return 1
104+
}
105+
}
106+
107+
final var myVar3Final : Int { // okay
108+
@inline(always)
109+
get {
110+
return 1
111+
}
112+
}
113+
114+
115+
var myVar3: Int {
116+
@inline(always) // expected-error {{'@inline(always)' on class variable accessors whose variable declaration is not final are not allowed}}
117+
get {
118+
return 1
119+
}
120+
}
121+
122+
@inline(always)
123+
static func myMethod3() {} // okay
124+
}
125+
126+
extension C {
127+
@inline(always)
128+
final func myMethod10() {} // okay
129+
130+
@inline(always)
131+
func myMethod11() {} // okay
132+
}

0 commit comments

Comments
 (0)