Skip to content

Commit b6d0986

Browse files
authored
[Compile Time Constant Extraction] Add extraction of property wrappers (#62555)
1 parent b9d4d6d commit b6d0986

File tree

4 files changed

+255
-12
lines changed

4 files changed

+255
-12
lines changed

include/swift/AST/ConstTypeInfo.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,25 @@ class RuntimeValue : public CompileTimeValue {
137137
}
138138
};
139139

140+
struct CustomAttrValue {
141+
swift::Type Type;
142+
std::vector<FunctionParameter> Parameters;
143+
};
144+
140145
struct ConstValueTypePropertyInfo {
141146
swift::VarDecl *VarDecl;
142147
std::shared_ptr<CompileTimeValue> Value;
148+
llvm::Optional<std::vector<CustomAttrValue>> PropertyWrappers;
149+
150+
ConstValueTypePropertyInfo(
151+
swift::VarDecl *VarDecl, std::shared_ptr<CompileTimeValue> Value,
152+
llvm::Optional<std::vector<CustomAttrValue>> PropertyWrappers)
153+
: VarDecl(VarDecl), Value(Value), PropertyWrappers(PropertyWrappers) {}
154+
155+
ConstValueTypePropertyInfo(swift::VarDecl *VarDecl,
156+
std::shared_ptr<CompileTimeValue> Value)
157+
: VarDecl(VarDecl), Value(Value),
158+
PropertyWrappers(llvm::Optional<std::vector<CustomAttrValue>>()) {}
143159
};
144160

145161
struct ConstValueTypeInfo {

lib/ConstExtract/ConstExtract.cpp

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -198,24 +198,57 @@ static std::shared_ptr<CompileTimeValue> extractCompileTimeValue(Expr *expr) {
198198
return std::make_shared<RuntimeValue>();
199199
}
200200

201-
static std::shared_ptr<CompileTimeValue>
202-
extractPropertyInitializationValue(VarDecl *propertyDecl) {
203-
if (auto binding = propertyDecl->getParentPatternBinding()) {
204-
if (auto originalInit = binding->getOriginalInit(0)) {
205-
return extractCompileTimeValue(originalInit);
201+
static std::vector<CustomAttrValue>
202+
extractCustomAttrValues(VarDecl *propertyDecl) {
203+
std::vector<CustomAttrValue> customAttrValues;
204+
205+
for (auto *propertyWrapper : propertyDecl->getAttachedPropertyWrappers()) {
206+
std::vector<FunctionParameter> parameters;
207+
208+
if (const auto *args = propertyWrapper->getArgs()) {
209+
for (auto arg : *args) {
210+
const auto label = arg.getLabel().str().str();
211+
auto argExpr = arg.getExpr();
212+
213+
if (auto defaultArgument = dyn_cast<DefaultArgumentExpr>(argExpr)) {
214+
auto *decl = defaultArgument->getParamDecl();
215+
if (decl->hasDefaultExpr()) {
216+
argExpr = decl->getTypeCheckedDefaultExpr();
217+
}
218+
}
219+
parameters.push_back(
220+
{label, argExpr->getType(), extractCompileTimeValue(argExpr)});
221+
}
222+
}
223+
customAttrValues.push_back({propertyWrapper->getType(), parameters});
224+
}
225+
226+
return customAttrValues;
227+
}
228+
229+
static ConstValueTypePropertyInfo
230+
extractTypePropertyInfo(VarDecl *propertyDecl) {
231+
if (const auto binding = propertyDecl->getParentPatternBinding()) {
232+
if (const auto originalInit = binding->getOriginalInit(0)) {
233+
if (propertyDecl->hasAttachedPropertyWrapper()) {
234+
return {propertyDecl, extractCompileTimeValue(originalInit),
235+
extractCustomAttrValues(propertyDecl)};
236+
}
237+
238+
return {propertyDecl, extractCompileTimeValue(originalInit)};
206239
}
207240
}
208241

209242
if (auto accessorDecl = propertyDecl->getAccessor(AccessorKind::Get)) {
210243
auto node = accessorDecl->getTypecheckedBody()->getFirstElement();
211244
if (node.is<Stmt *>()) {
212245
if (auto returnStmt = dyn_cast<ReturnStmt>(node.get<Stmt *>())) {
213-
return extractCompileTimeValue(returnStmt->getResult());
246+
return {propertyDecl, extractCompileTimeValue(returnStmt->getResult())};
214247
}
215248
}
216249
}
217250

218-
return std::make_shared<RuntimeValue>();
251+
return {propertyDecl, std::make_shared<RuntimeValue>()};
219252
}
220253

221254
ConstValueTypeInfo
@@ -228,8 +261,7 @@ ConstantValueInfoRequest::evaluate(Evaluator &Evaluator,
228261

229262
std::vector<ConstValueTypePropertyInfo> Properties;
230263
for (auto Property : StoredProperties) {
231-
Properties.push_back(
232-
{Property, extractPropertyInitializationValue(Property)});
264+
Properties.push_back(extractTypePropertyInfo(Property));
233265
}
234266

235267
for (auto Member : Decl->getMembers()) {
@@ -238,13 +270,13 @@ ConstantValueInfoRequest::evaluate(Evaluator &Evaluator,
238270
// instead gather up remaining static and computed properties.
239271
if (!VD || StoredPropertiesSet.count(VD))
240272
continue;
241-
Properties.push_back({VD, extractPropertyInitializationValue(VD)});
273+
Properties.push_back(extractTypePropertyInfo(VD));
242274
}
243275

244276
for (auto Extension: Decl->getExtensions()) {
245277
for (auto Member : Extension->getMembers()) {
246278
if (auto *VD = dyn_cast<VarDecl>(Member)) {
247-
Properties.push_back({VD, extractPropertyInitializationValue(VD)});
279+
Properties.push_back(extractTypePropertyInfo(VD));
248280
}
249281
}
250282
}
@@ -348,6 +380,31 @@ void writeValue(llvm::json::OStream &JSON,
348380
}
349381
}
350382

383+
void writeAttributes(
384+
llvm::json::OStream &JSON,
385+
llvm::Optional<std::vector<CustomAttrValue>> PropertyWrappers) {
386+
if (!PropertyWrappers.hasValue()) {
387+
return;
388+
}
389+
390+
JSON.attributeArray("attributes", [&] {
391+
for (auto PW : PropertyWrappers.value()) {
392+
JSON.object([&] {
393+
JSON.attribute("type", toFullyQualifiedTypeNameString(PW.Type));
394+
JSON.attributeArray("arguments", [&] {
395+
for (auto FP : PW.Parameters) {
396+
JSON.object([&] {
397+
JSON.attribute("label", FP.Label);
398+
JSON.attribute("type", toFullyQualifiedTypeNameString(FP.Type));
399+
writeValue(JSON, FP.Value);
400+
});
401+
}
402+
});
403+
});
404+
}
405+
});
406+
}
407+
351408
bool writeAsJSONToFile(const std::vector<ConstValueTypeInfo> &ConstValueInfos,
352409
llvm::raw_fd_ostream &OS) {
353410
llvm::json::OStream JSON(OS, 2);
@@ -371,6 +428,7 @@ bool writeAsJSONToFile(const std::vector<ConstValueTypeInfo> &ConstValueInfos,
371428
JSON.attribute("isComputed",
372429
!decl->hasStorage() ? "true" : "false");
373430
writeValue(JSON, PropertyInfo.Value);
431+
writeAttributes(JSON, PropertyInfo.PropertyWrappers);
374432
});
375433
}
376434
});

test/ConstExtraction/ExtractCalls.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ public struct Foo : MyProto {
9898
let func1: Int = adder(2, 3)
9999
}
100100

101-
102101
extension Foo {
103102
struct Boo {}
104103

test/ConstExtraction/ExtractLiterals.swift

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,119 @@
8484
// CHECK-NEXT: "value": "\"Hello, World\""
8585
// CHECK-NEXT: }
8686
// CHECK-NEXT: ]
87+
// CHECK-NEXT: },
88+
// CHECK-NEXT: {
89+
// CHECK-NEXT: "typeName": "ExtractLiterals.PropertyWrappers",
90+
// CHECK-NEXT: "kind": "struct",
91+
// CHECK-NEXT: "properties": [
92+
// CHECK-NEXT: {
93+
// CHECK-NEXT: "label": "_propertyWrapper1",
94+
// CHECK-NEXT: "type": "ExtractLiterals.Buffered<Swift.String>",
95+
// CHECK-NEXT: "isStatic": "false",
96+
// CHECK-NEXT: "isComputed": "false",
97+
// CHECK-NEXT: "valueKind": "Runtime"
98+
// CHECK-NEXT: },
99+
// CHECK-NEXT: {
100+
// CHECK-NEXT: "label": "_propertyWrapper2",
101+
// CHECK-NEXT: "type": "ExtractLiterals.Clamping<Swift.Int>",
102+
// CHECK-NEXT: "isStatic": "false",
103+
// CHECK-NEXT: "isComputed": "false",
104+
// CHECK-NEXT: "valueKind": "Runtime"
105+
// CHECK-NEXT: },
106+
// CHECK-NEXT: {
107+
// CHECK-NEXT: "label": "_propertyWrapper3",
108+
// CHECK-NEXT: "type": "ExtractLiterals.Buffered<ExtractLiterals.Clamping<Swift.Int>>",
109+
// CHECK-NEXT: "isStatic": "false",
110+
// CHECK-NEXT: "isComputed": "false",
111+
// CHECK-NEXT: "valueKind": "Runtime"
112+
// CHECK-NEXT: },
113+
// CHECK-NEXT: {
114+
// CHECK-NEXT: "label": "propertyWrapper1",
115+
// CHECK-NEXT: "type": "Swift.String",
116+
// CHECK-NEXT: "isStatic": "false",
117+
// CHECK-NEXT: "isComputed": "true",
118+
// CHECK-NEXT: "valueKind": "RawLiteral",
119+
// CHECK-NEXT: "value": "\"Hello\"",
120+
// CHECK-NEXT: "attributes": [
121+
// CHECK-NEXT: {
122+
// CHECK-NEXT: "type": "ExtractLiterals.Buffered",
123+
// CHECK-NEXT: "arguments": []
124+
// CHECK-NEXT: }
125+
// CHECK-NEXT: ]
126+
// CHECK-NEXT: },
127+
// CHECK-NEXT: {
128+
// CHECK-NEXT: "label": "$propertyWrapper1",
129+
// CHECK-NEXT: "type": "(Swift.String, Swift.String?)",
130+
// CHECK-NEXT: "isStatic": "false",
131+
// CHECK-NEXT: "isComputed": "true",
132+
// CHECK-NEXT: "valueKind": "Runtime"
133+
// CHECK-NEXT: },
134+
// CHECK-NEXT: {
135+
// CHECK-NEXT: "label": "propertyWrapper2",
136+
// CHECK-NEXT: "type": "Swift.Int",
137+
// CHECK-NEXT: "isStatic": "false",
138+
// CHECK-NEXT: "isComputed": "true",
139+
// CHECK-NEXT: "valueKind": "RawLiteral",
140+
// CHECK-NEXT: "value": "128",
141+
// CHECK-NEXT: "attributes": [
142+
// CHECK-NEXT: {
143+
// CHECK-NEXT: "type": "ExtractLiterals.Clamping",
144+
// CHECK-NEXT: "arguments": [
145+
// CHECK-NEXT: {
146+
// CHECK-NEXT: "label": "min",
147+
// CHECK-NEXT: "type": "Swift.Int",
148+
// CHECK-NEXT: "valueKind": "RawLiteral",
149+
// CHECK-NEXT: "value": "0"
150+
// CHECK-NEXT: },
151+
// CHECK-NEXT: {
152+
// CHECK-NEXT: "label": "max",
153+
// CHECK-NEXT: "type": "Swift.Int",
154+
// CHECK-NEXT: "valueKind": "RawLiteral",
155+
// CHECK-NEXT: "value": "255"
156+
// CHECK-NEXT: }
157+
// CHECK-NEXT: ]
158+
// CHECK-NEXT: }
159+
// CHECK-NEXT: ]
160+
// CHECK-NEXT: },
161+
// CHECK-NEXT: {
162+
// CHECK-NEXT: "label": "propertyWrapper3",
163+
// CHECK-NEXT: "type": "Swift.Int",
164+
// CHECK-NEXT: "isStatic": "false",
165+
// CHECK-NEXT: "isComputed": "true",
166+
// CHECK-NEXT: "valueKind": "RawLiteral",
167+
// CHECK-NEXT: "value": "128",
168+
// CHECK-NEXT: "attributes": [
169+
// CHECK-NEXT: {
170+
// CHECK-NEXT: "type": "ExtractLiterals.Buffered",
171+
// CHECK-NEXT: "arguments": []
172+
// CHECK-NEXT: },
173+
// CHECK-NEXT: {
174+
// CHECK-NEXT: "type": "ExtractLiterals.Clamping",
175+
// CHECK-NEXT: "arguments": [
176+
// CHECK-NEXT: {
177+
// CHECK-NEXT: "label": "min",
178+
// CHECK-NEXT: "type": "Swift.Int",
179+
// CHECK-NEXT: "valueKind": "RawLiteral",
180+
// CHECK-NEXT: "value": "0"
181+
// CHECK-NEXT: },
182+
// CHECK-NEXT: {
183+
// CHECK-NEXT: "label": "max",
184+
// CHECK-NEXT: "type": "Swift.Int",
185+
// CHECK-NEXT: "valueKind": "RawLiteral",
186+
// CHECK-NEXT: "value": "255"
187+
// CHECK-NEXT: }
188+
// CHECK-NEXT: ]
189+
// CHECK-NEXT: }
190+
// CHECK-NEXT: ]
191+
// CHECK-NEXT: },
192+
// CHECK-NEXT: {
193+
// CHECK-NEXT: "label": "$propertyWrapper3",
194+
// CHECK-NEXT: "type": "(ExtractLiterals.Clamping<Swift.Int>, ExtractLiterals.Clamping<Swift.Int>?)",
195+
// CHECK-NEXT: "isStatic": "false",
196+
// CHECK-NEXT: "isComputed": "true",
197+
// CHECK-NEXT: "valueKind": "Runtime"
198+
// CHECK-NEXT: }
199+
// CHECK-NEXT: ]
87200
// CHECK-NEXT: }
88201
// CHECK-NEXT:]
89202

@@ -107,3 +220,60 @@ public struct Floats : MyProto {
107220
public struct Strings : MyProto {
108221
let string1: String = "Hello, World"
109222
}
223+
224+
public struct PropertyWrappers : MyProto {
225+
@Buffered
226+
var propertyWrapper1: String = "Hello"
227+
228+
@Clamping(min: 0, max: 255)
229+
var propertyWrapper2: Int = 128
230+
231+
@Buffered @Clamping(min: 0, max: 255)
232+
var propertyWrapper3: Int = 128
233+
}
234+
235+
@propertyWrapper
236+
struct Clamping<V: Comparable> {
237+
var value: V
238+
let min: V
239+
let max: V
240+
241+
init(wrappedValue: V, min: V, max: V) {
242+
self.value = wrappedValue
243+
self.min = min
244+
self.max = max
245+
}
246+
247+
var wrappedValue: V {
248+
get { return self.value }
249+
set {
250+
if newValue < self.min {
251+
self.value = self.min
252+
} else if newValue > self.max {
253+
self.value = self.max
254+
} else {
255+
self.value = newValue
256+
}
257+
}
258+
}
259+
}
260+
261+
@propertyWrapper
262+
struct Buffered<V> {
263+
var value: V
264+
var lastValue: V?
265+
266+
init(wrappedValue: V) {
267+
self.value = wrappedValue
268+
}
269+
270+
var wrappedValue: V {
271+
get { return value }
272+
set {
273+
self.lastValue = self.value
274+
self.value = newValue
275+
}
276+
}
277+
278+
var projectedValue: (V, V?) { (self.value, self.lastValue) }
279+
}

0 commit comments

Comments
 (0)