Skip to content

Commit 9805983

Browse files
committed
Sema: implement isFinal using a request evaluator
Add the request evaluator `IsFinalRequest` to lazily determine if a `ValueDecl` is final.
1 parent 2b5a691 commit 9805983

File tree

10 files changed

+184
-70
lines changed

10 files changed

+184
-70
lines changed

include/swift/AST/Decl.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2336,10 +2336,18 @@ class ValueDecl : public Decl {
23362336
/// the declaration will go through an extra level of indirection that
23372337
/// allows the entity to be replaced at runtime.
23382338
unsigned isDynamic : 1;
2339+
2340+
/// Whether the "isFinal" bit has been computed yet.
2341+
unsigned isFinalComputed : 1;
2342+
2343+
/// Whether this declaration is 'final'. A final class can't be subclassed,
2344+
/// a final class member can't be overriden.
2345+
unsigned isFinal : 1;
23392346
} LazySemanticInfo;
23402347

23412348
friend class OverriddenDeclsRequest;
23422349
friend class IsObjCRequest;
2350+
friend class IsFinalRequest;
23432351
friend class IsDynamicRequest;
23442352

23452353
protected:
@@ -2356,6 +2364,8 @@ class ValueDecl : public Decl {
23562364
LazySemanticInfo.hasOverridden = false;
23572365
LazySemanticInfo.isDynamicComputed = false;
23582366
LazySemanticInfo.isDynamic = false;
2367+
LazySemanticInfo.isFinalComputed = false;
2368+
LazySemanticInfo.isFinal = false;
23592369
}
23602370

23612371
// MemberLookupTable borrows a bit from this type
@@ -2596,10 +2606,8 @@ class ValueDecl : public Decl {
25962606
/// Note whether this declaration is known to be exposed to Objective-C.
25972607
void setIsObjC(bool Value);
25982608

2599-
/// Is this declaration marked with 'final'?
2600-
bool isFinal() const {
2601-
return getAttrs().hasAttribute<FinalAttr>();
2602-
}
2609+
/// Is this declaration 'final'?
2610+
bool isFinal() const;
26032611

26042612
/// Is this declaration marked with 'dynamic'?
26052613
bool isDynamic() const;

include/swift/AST/TypeCheckRequests.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,32 @@ class IsObjCRequest :
187187
void cacheResult(bool value) const;
188188
};
189189

190+
/// Determine whether the given declaration is 'final'.
191+
class IsFinalRequest :
192+
public SimpleRequest<IsFinalRequest,
193+
CacheKind::SeparatelyCached,
194+
bool,
195+
ValueDecl *> {
196+
public:
197+
using SimpleRequest::SimpleRequest;
198+
199+
private:
200+
friend SimpleRequest;
201+
202+
// Evaluation.
203+
llvm::Expected<bool> evaluate(Evaluator &evaluator, ValueDecl *decl) const;
204+
205+
public:
206+
// Cycle handling
207+
void diagnoseCycle(DiagnosticEngine &diags) const;
208+
void noteCycleStep(DiagnosticEngine &diags) const;
209+
210+
// Separate caching.
211+
bool isCached() const { return true; }
212+
Optional<bool> getCachedResult() const;
213+
void cacheResult(bool value) const;
214+
};
215+
190216
/// Determine whether the given declaration is 'dynamic''.
191217
class IsDynamicRequest :
192218
public SimpleRequest<IsDynamicRequest,

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ SWIFT_TYPEID(SuperclassTypeRequest)
1919
SWIFT_TYPEID(EnumRawTypeRequest)
2020
SWIFT_TYPEID(OverriddenDeclsRequest)
2121
SWIFT_TYPEID(IsObjCRequest)
22+
SWIFT_TYPEID(IsFinalRequest)
2223
SWIFT_TYPEID(IsDynamicRequest)
2324
SWIFT_TYPEID(RequirementRequest)
2425
SWIFT_TYPEID(USRGenerationRequest)

lib/AST/ASTPrinter.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,19 @@ void PrintAST::printAttributes(const Decl *D) {
970970

971971
D->getAttrs().print(Printer, Options, D);
972972

973+
// Print the implicit 'final' attribute.
974+
if (auto VD = dyn_cast<ValueDecl>(D)) {
975+
auto VarD = dyn_cast<VarDecl>(D);
976+
if (VD->isFinal() &&
977+
!VD->getAttrs().hasAttribute<FinalAttr>() &&
978+
// Don't print a redundant 'final' if printing a 'let' or 'static' decl.
979+
!(VarD && VarD->isLet()) &&
980+
getCorrectStaticSpelling(D) != StaticSpellingKind::KeywordStatic) {
981+
Printer.printAttrName("final");
982+
Printer << " ";
983+
}
984+
}
985+
973986
// Explicitly print 'mutating' and 'nonmutating' before getters and setters
974987
// for which that is true.
975988
if (auto accessor = dyn_cast<AccessorDecl>(D)) {

lib/AST/Decl.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2384,6 +2384,12 @@ void ValueDecl::setIsObjC(bool value) {
23842384
LazySemanticInfo.isObjC = value;
23852385
}
23862386

2387+
bool ValueDecl::isFinal() const {
2388+
return evaluateOrDefault(getASTContext().evaluator,
2389+
IsFinalRequest { const_cast<ValueDecl *>(this) },
2390+
getAttrs().hasAttribute<FinalAttr>());
2391+
}
2392+
23872393
bool ValueDecl::isDynamic() const {
23882394
ASTContext &ctx = getASTContext();
23892395
return evaluateOrDefault(ctx.evaluator,

lib/AST/TypeCheckRequests.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,36 @@ void IsObjCRequest::cacheResult(bool value) const {
220220
decl->setIsObjC(value);
221221
}
222222

223+
//----------------------------------------------------------------------------//
224+
// isFinal computation.
225+
//----------------------------------------------------------------------------//
226+
227+
void IsFinalRequest::diagnoseCycle(DiagnosticEngine &diags) const {
228+
// FIXME: Improve this diagnostic.
229+
auto decl = std::get<0>(getStorage());
230+
diags.diagnose(decl, diag::circular_reference);
231+
}
232+
233+
void IsFinalRequest::noteCycleStep(DiagnosticEngine &diags) const {
234+
auto decl = std::get<0>(getStorage());
235+
// FIXME: Customize this further.
236+
diags.diagnose(decl, diag::circular_reference_through);
237+
}
238+
239+
Optional<bool> IsFinalRequest::getCachedResult() const {
240+
auto decl = std::get<0>(getStorage());
241+
if (decl->LazySemanticInfo.isFinalComputed)
242+
return decl->LazySemanticInfo.isFinal;
243+
244+
return None;
245+
}
246+
247+
void IsFinalRequest::cacheResult(bool value) const {
248+
auto decl = std::get<0>(getStorage());
249+
decl->LazySemanticInfo.isFinalComputed = true;
250+
decl->LazySemanticInfo.isFinal = value;
251+
}
252+
223253
//----------------------------------------------------------------------------//
224254
// isDynamic computation.
225255
//----------------------------------------------------------------------------//

lib/IDE/CodeCompletion.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4167,7 +4167,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
41674167
if (D->shouldHideFromEditor())
41684168
return;
41694169

4170-
if (D->getAttrs().hasAttribute<FinalAttr>() ||
4170+
if (D->isFinal() ||
41714171
// A 'class' member with an initial value cannot be overriden either.
41724172
(D->isStatic() && D->getAttrs().hasAttribute<HasInitialValueAttr>()))
41734173
return;

lib/Sema/CodeSynthesis.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -367,10 +367,6 @@ createCoroutineAccessorPrototype(AbstractStorageDecl *storage,
367367
if (isStatic)
368368
accessor->setStatic();
369369

370-
// The accessor is final if the storage is.
371-
if (storage->isFinal())
372-
makeFinal(ctx, accessor);
373-
374370
// If the storage does not provide this accessor as an opaque accessor,
375371
// we can't add a dynamically-dispatched method entry for the accessor,
376372
// so force it to be statically dispatched. ("final" would be inappropriate

lib/Sema/DerivedConformanceCodable.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1039,7 +1039,7 @@ static ValueDecl *deriveDecodable_init(DerivedConformance &derived) {
10391039
initDecl->setBodySynthesizer(&deriveBodyDecodable_init);
10401040

10411041
// This constructor should be marked as `required` for non-final classes.
1042-
if (classDecl && !classDecl->getAttrs().hasAttribute<FinalAttr>()) {
1042+
if (classDecl && !classDecl->isFinal()) {
10431043
auto *reqAttr = new (C) RequiredAttr(/*IsImplicit=*/true);
10441044
initDecl->getAttrs().add(reqAttr);
10451045
}

0 commit comments

Comments
 (0)