@@ -188,6 +188,83 @@ static VarDecl *deriveRawRepresentable_raw(DerivedConformance &derived) {
188
188
return propDecl;
189
189
}
190
190
191
+ // / Contains information needed to synthesize a runtime version check.
192
+ struct RuntimeVersionCheck {
193
+ PlatformKind Platform;
194
+ llvm::VersionTuple Version;
195
+
196
+ RuntimeVersionCheck (PlatformKind Platform, llvm::VersionTuple Version)
197
+ : Platform(Platform), Version(Version)
198
+ { }
199
+
200
+ VersionRange getVersionRange () const {
201
+ return VersionRange::allGTE (Version);
202
+ }
203
+
204
+ // / Synthesizes a statement which returns nil if the runtime version check
205
+ // / fails, e.g. "guard #available(iOS 10, *) else { return nil }".
206
+ Stmt *createEarlyReturnStmt (ASTContext &C) const {
207
+ // platformSpec = "\(attr.platform) \(attr.introduced)"
208
+ auto platformSpec = new (C) PlatformVersionConstraintAvailabilitySpec (
209
+ Platform, SourceLoc (),
210
+ Version, SourceLoc ()
211
+ );
212
+
213
+ // otherSpec = "*"
214
+ auto otherSpec = new (C) OtherPlatformAvailabilitySpec (SourceLoc ());
215
+
216
+ // availableInfo = "#available(\(platformSpec), \(otherSpec))"
217
+ auto availableInfo = PoundAvailableInfo::create (C, SourceLoc (),
218
+ { platformSpec, otherSpec },
219
+ SourceLoc ());
220
+
221
+ // This won't be filled in by TypeCheckAvailability because we have
222
+ // invalid SourceLocs in this area of the AST.
223
+ availableInfo->setAvailableRange (getVersionRange ());
224
+
225
+ // earlyReturnBody = "{ return nil }"
226
+ auto earlyReturn = new (C) FailStmt (SourceLoc (), SourceLoc ());
227
+ auto earlyReturnBody = BraceStmt::create (C, SourceLoc (),
228
+ ASTNode (earlyReturn),
229
+ SourceLoc (), /* implicit=*/ true );
230
+
231
+ // guardStmt = "guard \(availableInfo) else \(earlyReturnBody)"
232
+ StmtConditionElement conds[1 ] = { availableInfo };
233
+ auto guardStmt = new (C) GuardStmt (SourceLoc (), C.AllocateCopy (conds),
234
+ earlyReturnBody, /* implicit=*/ true );
235
+
236
+ return guardStmt;
237
+ }
238
+ };
239
+
240
+ // / Checks if the case will be available at runtime given the current target
241
+ // / platform. If it will never be available, returns false. If it will always
242
+ // / be available, returns true. If it will sometimes be available, adds
243
+ // / information about the runtime check needed to ensure it is available to
244
+ // / \c versionCheck and returns true.
245
+ static bool checkAvailability (EnumElementDecl* elt, ASTContext &C,
246
+ Optional<RuntimeVersionCheck> &versionCheck) {
247
+ auto *attr = elt->getAttrs ().getPotentiallyUnavailable (C);
248
+
249
+ // Is it always available?
250
+ if (!attr)
251
+ return true ;
252
+
253
+ AvailableVersionComparison availability = attr->getVersionAvailability (C);
254
+
255
+ assert (availability != AvailableVersionComparison::Available &&
256
+ " DeclAttributes::getPotentiallyUnavailable() shouldn't "
257
+ " return an available attribute" );
258
+
259
+ // Is it never available?
260
+ if (availability != AvailableVersionComparison::PotentiallyUnavailable)
261
+ return false ;
262
+
263
+ // It's conditionally available; create a version constraint and return true.
264
+ versionCheck.emplace (attr->Platform , *attr->Introduced );
265
+ return true ;
266
+ }
267
+
191
268
static void
192
269
deriveBodyRawRepresentable_init (AbstractFunctionDecl *initDecl, void *) {
193
270
// enum SomeEnum : SomeType {
@@ -238,7 +315,15 @@ deriveBodyRawRepresentable_init(AbstractFunctionDecl *initDecl, void *) {
238
315
SmallVector<ASTNode, 4 > cases;
239
316
unsigned Idx = 0 ;
240
317
for (auto elt : enumDecl->getAllElements ()) {
241
- // litPat = "\(elt.rawValueExpr)", e.g. "42" as a pattern
318
+ // First, check case availability. If the case will definitely be
319
+ // unavailable, skip it. If it might be unavailable at runtime, save
320
+ // information about that check in versionCheck and keep processing this
321
+ // element.
322
+ Optional<RuntimeVersionCheck> versionCheck (None);
323
+ if (!checkAvailability (elt, C, versionCheck))
324
+ continue ;
325
+
326
+ // litPat = elt.rawValueExpr as a pattern
242
327
LiteralExpr *litExpr = cloneRawLiteralExpr (C, elt->getRawValueExpr ());
243
328
if (isStringEnum) {
244
329
// In case of a string enum we are calling the _findStringSwitchCase
@@ -250,73 +335,31 @@ deriveBodyRawRepresentable_init(AbstractFunctionDecl *initDecl, void *) {
250
335
nullptr , nullptr );
251
336
litPat->setImplicit ();
252
337
253
- // Will collect any preliminary guards, plus the final assignment .
338
+ // / Statements in the body of this case .
254
339
SmallVector<ASTNode, 2 > stmts;
255
340
256
- // If there's an @available attribute specifying when the case was
257
- // introduced, generate an early return on unavailability, e.g.
258
- // "guard #available(iOS 9, *) else { return nil }"
259
- for (auto *attr : elt->getAttrs ().getAttributes <AvailableAttr>()) {
260
- // We only care about attributes which have an "introduced" version for
261
- // the platform we're building for. We don't care about language version
262
- // because we may be compiling in an older language mode, but writing APIs
263
- // for a newer one.
264
- if (attr->Introduced .hasValue () && attr->hasPlatform () &&
265
- attr->isActivePlatform (C) &&
266
- *attr->Introduced > C.LangOpts .getMinPlatformVersion ()) {
267
- // platformSpec = "\(attr.platform) \(attr.introduced)"
268
- auto platformSpec =
269
- new (C) PlatformVersionConstraintAvailabilitySpec (
270
- attr->Platform , SourceLoc (),
271
- *attr->Introduced , SourceLoc ()
272
- );
273
-
274
- // otherSpec = "*"
275
- auto otherSpec = new (C) OtherPlatformAvailabilitySpec (SourceLoc ());
276
-
277
- // availableInfo = "#available(\(platformSpec), \(otherSpec))"
278
- auto availableInfo = PoundAvailableInfo::create (C, SourceLoc (),
279
- { platformSpec, otherSpec },
280
- SourceLoc ());
281
-
282
- // This won't be filled in by TypeCheckAvailability because we have
283
- // invalid SourceLocs in this area of the AST.
284
- auto versionRange = VersionRange::allGTE (*attr->Introduced );
285
- availableInfo->setAvailableRange (versionRange);
286
-
287
- // earlyReturnBody = "{ return nil }"
288
- auto earlyReturn = new (C) FailStmt (SourceLoc (), SourceLoc ());
289
- auto earlyReturnBody = BraceStmt::create (C, SourceLoc (),
290
- ASTNode (earlyReturn),
291
- SourceLoc (), /* implicit=*/ true );
292
-
293
- // guardStmt = "guard \(vailableInfo) else \(earlyReturnBody)"
294
- SmallVector<StmtConditionElement, 1 >
295
- conds{StmtConditionElement (availableInfo)};
296
- auto guardStmt = new (C) GuardStmt (SourceLoc (), C.AllocateCopy (conds),
297
- earlyReturnBody, /* implicit=*/ true );
298
-
299
- stmts.push_back (guardStmt);
300
- }
301
- }
341
+ // If checkAvailability() discovered we need a runtime version check,
342
+ // add it now.
343
+ if (versionCheck.hasValue ())
344
+ stmts.push_back (ASTNode (versionCheck->createEarlyReturnStmt (C)));
345
+
346
+ // Create a statement which assigns the case to self.
302
347
303
348
// valueExpr = "\(enumType).\(elt)"
304
349
auto eltRef = new (C) DeclRefExpr (elt, DeclNameLoc (), /* implicit*/ true );
305
350
auto metaTyRef = TypeExpr::createImplicit (enumType, C);
306
351
auto valueExpr = new (C) DotSyntaxCallExpr (eltRef, SourceLoc (), metaTyRef);
307
352
308
- // selfRef = "self"
353
+ // assignment = "self = \(valueExpr) "
309
354
auto selfRef = new (C) DeclRefExpr (selfDecl, DeclNameLoc (),
310
355
/* implicit*/ true ,
311
356
AccessSemantics::DirectToStorage);
312
-
313
- // assignment = "\(selfRef) = \(valueExpr)"
314
357
auto assignment = new (C) AssignExpr (selfRef, SourceLoc (), valueExpr,
315
358
/* implicit*/ true );
316
359
317
360
stmts.push_back (ASTNode (assignment));
318
361
319
- // body = stmts.joined(separator: "\n" )
362
+ // body = "{ \(stmts) }" (the braces are silent )
320
363
auto body = BraceStmt::create (C, SourceLoc (),
321
364
stmts, SourceLoc ());
322
365
0 commit comments