Skip to content

Commit 6d3017c

Browse files
authored
[Compile Time Constant Extraction] Extract from extensions of types not declared in the same file/module (#70081)
Resolves rdar://118904022
1 parent 9d1a92e commit 6d3017c

File tree

5 files changed

+112
-33
lines changed

5 files changed

+112
-33
lines changed

include/swift/ConstExtract/ConstExtractRequests.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,16 @@
1616
#ifndef SWIFT_CONST_EXTRACT_REQUESTS_H
1717
#define SWIFT_CONST_EXTRACT_REQUESTS_H
1818

19-
#include "swift/AST/SimpleRequest.h"
2019
#include "swift/AST/ASTTypeIDs.h"
2120
#include "swift/AST/ConstTypeInfo.h"
2221
#include "swift/AST/EvaluatorDependencies.h"
2322
#include "swift/AST/FileUnit.h"
2423
#include "swift/AST/Identifier.h"
2524
#include "swift/AST/NameLookup.h"
25+
#include "swift/AST/SimpleRequest.h"
2626
#include "swift/Basic/Statistic.h"
2727
#include "llvm/ADT/Hashing.h"
28+
#include "llvm/ADT/PointerUnion.h"
2829
#include "llvm/ADT/TinyPtrVector.h"
2930

3031
namespace swift {
@@ -35,9 +36,12 @@ class EnumDecl;
3536

3637
/// Retrieve information about compile-time-known values
3738
class ConstantValueInfoRequest
38-
: public SimpleRequest<ConstantValueInfoRequest,
39-
ConstValueTypeInfo(NominalTypeDecl *),
40-
RequestFlags::Cached> {
39+
: public SimpleRequest<
40+
ConstantValueInfoRequest,
41+
ConstValueTypeInfo(
42+
NominalTypeDecl *,
43+
llvm::PointerUnion<const SourceFile *, ModuleDecl *>),
44+
RequestFlags::Cached> {
4145
public:
4246
using SimpleRequest::SimpleRequest;
4347

@@ -46,7 +50,9 @@ class ConstantValueInfoRequest
4650

4751
// Evaluation.
4852
ConstValueTypeInfo
49-
evaluate(Evaluator &eval, NominalTypeDecl *nominal) const;
53+
evaluate(Evaluator &eval, NominalTypeDecl *nominal,
54+
llvm::PointerUnion<const SourceFile *, ModuleDecl *> extractionScope)
55+
const;
5056

5157
public:
5258
// Caching

include/swift/ConstExtract/ConstExtractTypeIDZone.def

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,7 @@
1616
//===----------------------------------------------------------------------===//
1717

1818
SWIFT_REQUEST(ConstExtract, ConstantValueInfoRequest,
19-
ConstValueTypeInfo(NominalTypeDecl *), Cached,
20-
NoLocationInfo)
19+
ConstValueTypeInfo(
20+
NominalTypeDecl *,
21+
llvm::PointerUnion<const SourceFile *, ModuleDecl *>),
22+
Cached, NoLocationInfo)

lib/ConstExtract/ConstExtract.cpp

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,21 @@ class NominalTypeConformanceCollector : public ASTWalker {
5353
}
5454

5555
PreWalkAction walkToDeclPre(Decl *D) override {
56-
if (auto *NTD = llvm::dyn_cast<NominalTypeDecl>(D))
57-
if (!isa<ProtocolDecl>(NTD))
56+
auto *NTD = llvm::dyn_cast<NominalTypeDecl>(D);
57+
if (!NTD)
58+
if (auto *ETD = dyn_cast<ExtensionDecl>(D))
59+
NTD = ETD->getExtendedNominal();
60+
if (NTD)
61+
if (!isa<ProtocolDecl>(NTD) && CheckedDecls.insert(NTD).second)
5862
for (auto &Protocol : NTD->getAllProtocols())
5963
if (Protocol->getAttrs().hasAttribute<ExtractConstantsFromMembersAttr>() ||
6064
Protocols.count(Protocol->getName().str().str()) != 0)
6165
ConformanceTypeDecls.push_back(NTD);
6266
return Action::Continue();
6367
}
68+
69+
private:
70+
std::unordered_set<NominalTypeDecl *> CheckedDecls;
6471
};
6572

6673
std::string toFullyQualifiedTypeNameString(const swift::Type &Type) {
@@ -463,37 +470,52 @@ extractEnumCases(NominalTypeDecl *Decl) {
463470
return llvm::None;
464471
}
465472

466-
ConstValueTypeInfo
467-
ConstantValueInfoRequest::evaluate(Evaluator &Evaluator,
468-
NominalTypeDecl *Decl) const {
469-
// Use 'getStoredProperties' to get lowered lazy and wrapped properties
470-
auto StoredProperties = Decl->getStoredProperties();
471-
std::unordered_set<VarDecl *> StoredPropertiesSet(StoredProperties.begin(),
472-
StoredProperties.end());
473+
ConstValueTypeInfo ConstantValueInfoRequest::evaluate(
474+
Evaluator &Evaluator, NominalTypeDecl *Decl,
475+
llvm::PointerUnion<const SourceFile *, ModuleDecl *> extractionScope)
476+
const {
477+
478+
auto shouldExtract = [&](DeclContext *decl) {
479+
if (auto SF = extractionScope.dyn_cast<const SourceFile *>())
480+
return decl->getOutermostParentSourceFile() == SF;
481+
return decl->getParentModule() == extractionScope.get<ModuleDecl *>();
482+
};
473483

474484
std::vector<ConstValueTypePropertyInfo> Properties;
475-
for (auto Property : StoredProperties) {
476-
Properties.push_back(extractTypePropertyInfo(Property));
477-
}
485+
llvm::Optional<std::vector<EnumElementDeclValue>> EnumCases;
478486

479-
for (auto Member : Decl->getMembers()) {
480-
auto *VD = dyn_cast<VarDecl>(Member);
481-
// Ignore plain stored properties collected above,
482-
// instead gather up remaining static and computed properties.
483-
if (!VD || StoredPropertiesSet.count(VD))
484-
continue;
485-
Properties.push_back(extractTypePropertyInfo(VD));
487+
if (shouldExtract(Decl)) {
488+
// Use 'getStoredProperties' to get lowered lazy and wrapped properties
489+
auto StoredProperties = Decl->getStoredProperties();
490+
std::unordered_set<VarDecl *> StoredPropertiesSet(StoredProperties.begin(),
491+
StoredProperties.end());
492+
for (auto Property : StoredProperties) {
493+
Properties.push_back(extractTypePropertyInfo(Property));
494+
}
495+
496+
for (auto Member : Decl->getMembers()) {
497+
auto *VD = dyn_cast<VarDecl>(Member);
498+
// Ignore plain stored properties collected above,
499+
// instead gather up remaining static and computed properties.
500+
if (!VD || StoredPropertiesSet.count(VD))
501+
continue;
502+
Properties.push_back(extractTypePropertyInfo(VD));
503+
}
504+
505+
EnumCases = extractEnumCases(Decl);
486506
}
487507

488508
for (auto Extension: Decl->getExtensions()) {
489-
for (auto Member : Extension->getMembers()) {
490-
if (auto *VD = dyn_cast<VarDecl>(Member)) {
491-
Properties.push_back(extractTypePropertyInfo(VD));
509+
if (shouldExtract(Extension)) {
510+
for (auto Member : Extension->getMembers()) {
511+
if (auto *VD = dyn_cast<VarDecl>(Member)) {
512+
Properties.push_back(extractTypePropertyInfo(VD));
513+
}
492514
}
493515
}
494516
}
495517

496-
return ConstValueTypeInfo{Decl, Properties, extractEnumCases(Decl)};
518+
return ConstValueTypeInfo{Decl, Properties, EnumCases};
497519
}
498520

499521
std::vector<ConstValueTypeInfo>
@@ -507,7 +529,8 @@ gatherConstValuesForModule(const std::unordered_set<std::string> &Protocols,
507529
Module->walk(ConformanceCollector);
508530
for (auto *CD : ConformanceDecls)
509531
Result.emplace_back(evaluateOrDefault(CD->getASTContext().evaluator,
510-
ConstantValueInfoRequest{CD}, {}));
532+
ConstantValueInfoRequest{CD, Module},
533+
{}));
511534
return Result;
512535
}
513536

@@ -523,8 +546,8 @@ gatherConstValuesForPrimary(const std::unordered_set<std::string> &Protocols,
523546
D->walk(ConformanceCollector);
524547

525548
for (auto *CD : ConformanceDecls)
526-
Result.emplace_back(evaluateOrDefault(CD->getASTContext().evaluator,
527-
ConstantValueInfoRequest{CD}, {}));
549+
Result.emplace_back(evaluateOrDefault(
550+
CD->getASTContext().evaluator, ConstantValueInfoRequest{CD, SF}, {}));
528551
return Result;
529552
}
530553

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: echo "[MyProto]" > %t/protocols.json
3+
4+
// RUN: %target-swift-frontend -typecheck -emit-const-values-path %t/ExtractFromExtension.swiftconstvalues -const-gather-protocols-file %t/protocols.json %S/Inputs/ProtocolConformances.swift -primary-file %s
5+
// RUN: cat %t/ExtractFromExtension.swiftconstvalues 2>&1 | %FileCheck %s
6+
7+
extension MyType {
8+
static let myValue = MyType("it is doable")
9+
}
10+
11+
// CHECK: "typeName": "ProtocolConformances.MyType",
12+
// CHECK: "kind": "struct",
13+
// CHECK: "conformances": [
14+
// CHECK-NEXT: "ProtocolConformances.MyProto"
15+
// CHECK-NEXT: ],
16+
// CHECK: "properties": [
17+
// CHECK-NEXT: {
18+
// CHECK-NEXT: "label": "myValue",
19+
// CHECK: "type": "ProtocolConformances.MyType",
20+
// CHECK: "valueKind": "InitCall",
21+
// CHECK-NEXT: "value": {
22+
// CHECK-NEXT: "type": "ProtocolConformances.MyType",
23+
// CHECK-NEXT: "arguments": [
24+
// CHECK-NEXT: {
25+
// CHECK-NEXT: "label": "",
26+
// CHECK-NEXT: "type": "Swift.String",
27+
// CHECK-NEXT: "valueKind": "RawLiteral",
28+
// CHECK-NEXT: "value": "it is doable"
29+
// CHECK-NEXT: }
30+
// CHECK-NEXT: ]
31+
// CHECK-NEXT: }
32+
// CHECK-NEXT: }
33+
// CHECK-NEXT: ]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
public protocol MyProto { }
2+
3+
public struct MyType {
4+
private let value: String
5+
6+
public init(_ value: String) {
7+
self.value = value
8+
}
9+
10+
private static let defaultValue = MyType("not me")
11+
}
12+
13+
extension MyType: MyProto {
14+
static let anotherValue = MyType("also not me")
15+
}

0 commit comments

Comments
 (0)