@@ -108,7 +108,6 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
108
108
IGNORED_ATTR(StaticInitializeObjCMetadata)
109
109
IGNORED_ATTR(SynthesizedProtocol)
110
110
IGNORED_ATTR(Testable)
111
- IGNORED_ATTR(TypeEraser)
112
111
IGNORED_ATTR(WeakLinked)
113
112
IGNORED_ATTR(PrivateImport)
114
113
IGNORED_ATTR(DisfavoredOverload)
@@ -240,6 +239,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
240
239
241
240
void visitDiscardableResultAttr (DiscardableResultAttr *attr);
242
241
void visitDynamicReplacementAttr (DynamicReplacementAttr *attr);
242
+ void visitTypeEraserAttr (TypeEraserAttr *attr);
243
243
void visitImplementsAttr (ImplementsAttr *attr);
244
244
245
245
void visitFrozenAttr (FrozenAttr *attr);
@@ -2390,6 +2390,179 @@ void AttributeChecker::visitDynamicReplacementAttr(DynamicReplacementAttr *attr)
2390
2390
}
2391
2391
}
2392
2392
2393
+ llvm::Expected<bool >
2394
+ TypeEraserHasViableInitRequest::evaluate (Evaluator &evaluator,
2395
+ TypeEraserAttr *attr,
2396
+ ProtocolDecl *protocol) const {
2397
+ auto &ctx = protocol->getASTContext ();
2398
+ auto &diags = ctx.Diags ;
2399
+ TypeLoc &typeEraserLoc = attr->getTypeEraserLoc ();
2400
+ TypeRepr *typeEraserRepr = typeEraserLoc.getTypeRepr ();
2401
+ DeclContext *dc = protocol->getDeclContext ();
2402
+ Type protocolType = protocol->getDeclaredType ();
2403
+
2404
+ // Get the NominalTypeDecl for the type eraser.
2405
+ Type typeEraser = typeEraserLoc.getType ();
2406
+ if (!typeEraser && typeEraserRepr) {
2407
+ auto resolution = TypeResolution::forContextual (protocol);
2408
+ typeEraser = resolution.resolveType (typeEraserRepr, /* options=*/ None);
2409
+ typeEraserLoc.setType (typeEraser);
2410
+ }
2411
+
2412
+ if (typeEraser->hasError ())
2413
+ return false ;
2414
+
2415
+ // The type eraser must be a concrete nominal type
2416
+ auto nominalTypeDecl = typeEraser->getAnyNominal ();
2417
+ if (auto typeAliasDecl = dyn_cast_or_null<TypeAliasDecl>(nominalTypeDecl))
2418
+ nominalTypeDecl = typeAliasDecl->getUnderlyingType ()->getAnyNominal ();
2419
+
2420
+ if (!nominalTypeDecl || isa<ProtocolDecl>(nominalTypeDecl)) {
2421
+ diags.diagnose (typeEraserLoc.getLoc (), diag::non_nominal_type_eraser);
2422
+ return false ;
2423
+ }
2424
+
2425
+ // The nominal type must be accessible wherever the protocol is accessible
2426
+ if (nominalTypeDecl->getFormalAccess () < protocol->getFormalAccess ()) {
2427
+ diags.diagnose (typeEraserLoc.getLoc (), diag::type_eraser_not_accessible,
2428
+ nominalTypeDecl->getFormalAccess (), nominalTypeDecl->getName (),
2429
+ protocolType, protocol->getFormalAccess ());
2430
+ diags.diagnose (nominalTypeDecl->getLoc (), diag::type_eraser_declared_here);
2431
+ return false ;
2432
+ }
2433
+
2434
+ // The type eraser must conform to the annotated protocol
2435
+ if (!TypeChecker::conformsToProtocol (typeEraser, protocol, dc, None)) {
2436
+ diags.diagnose (typeEraserLoc.getLoc (), diag::type_eraser_does_not_conform,
2437
+ typeEraser, protocolType);
2438
+ diags.diagnose (nominalTypeDecl->getLoc (), diag::type_eraser_declared_here);
2439
+ return false ;
2440
+ }
2441
+
2442
+ // The type eraser must have an init of the form init<T: Protocol>(erasing: T)
2443
+ auto lookupResult = TypeChecker::lookupMember (dc, typeEraser,
2444
+ DeclNameRef::createConstructor ());
2445
+
2446
+ // Keep track of unviable init candidates for diagnostics
2447
+ enum class UnviableReason {
2448
+ Failable,
2449
+ UnsatisfiedRequirements,
2450
+ Inaccessible,
2451
+ };
2452
+ SmallVector<std::tuple<ConstructorDecl *, UnviableReason, Type>, 2 > unviable;
2453
+
2454
+ bool foundMatch = llvm::any_of (lookupResult, [&](const LookupResultEntry &entry) {
2455
+ auto *init = cast<ConstructorDecl>(entry.getValueDecl ());
2456
+ if (!init->isGeneric () || init->getGenericParams ()->size () != 1 )
2457
+ return false ;
2458
+
2459
+ auto genericSignature = init->getGenericSignature ();
2460
+ auto genericParamType = genericSignature->getInnermostGenericParams ().front ();
2461
+
2462
+ // Fow now, only allow one parameter.
2463
+ auto params = init->getParameters ();
2464
+ if (params->size () != 1 )
2465
+ return false ;
2466
+
2467
+ // The parameter must have the form `erasing: T` where T conforms to the protocol.
2468
+ ParamDecl *param = *init->getParameters ()->begin ();
2469
+ if (param->getArgumentName () != ctx.Id_erasing ||
2470
+ !param->getInterfaceType ()->isEqual (genericParamType) ||
2471
+ !genericSignature->conformsToProtocol (genericParamType, protocol))
2472
+ return false ;
2473
+
2474
+ // Allow other constraints as long as the init can be called with any
2475
+ // type conforming to the annotated protocol. We will check this by
2476
+ // substituting the protocol's Self type for the generic arg and check that
2477
+ // the requirements in the generic signature are satisfied.
2478
+ auto baseMap =
2479
+ typeEraser->getContextSubstitutionMap (nominalTypeDecl->getParentModule (),
2480
+ nominalTypeDecl);
2481
+ QuerySubstitutionMap getSubstitution{baseMap};
2482
+ auto subMap = SubstitutionMap::get (
2483
+ genericSignature,
2484
+ [&](SubstitutableType *type) -> Type {
2485
+ if (type->isEqual (genericParamType))
2486
+ return protocol->getSelfTypeInContext ();
2487
+
2488
+ return getSubstitution (type);
2489
+ },
2490
+ TypeChecker::LookUpConformance (dc));
2491
+
2492
+ // Use invalid 'SourceLoc's to suppress diagnostics.
2493
+ auto result = TypeChecker::checkGenericArguments (
2494
+ protocol, SourceLoc (), SourceLoc (), typeEraser,
2495
+ genericSignature->getGenericParams (),
2496
+ genericSignature->getRequirements (),
2497
+ QuerySubstitutionMap{subMap},
2498
+ TypeChecker::LookUpConformance (dc),
2499
+ None);
2500
+
2501
+ if (result != RequirementCheckResult::Success) {
2502
+ unviable.push_back (
2503
+ std::make_tuple (init, UnviableReason::UnsatisfiedRequirements,
2504
+ genericParamType));
2505
+ return false ;
2506
+ }
2507
+
2508
+ if (init->isFailable ()) {
2509
+ unviable.push_back (
2510
+ std::make_tuple (init, UnviableReason::Failable, genericParamType));
2511
+ return false ;
2512
+ }
2513
+
2514
+ if (init->getFormalAccess () < protocol->getFormalAccess ()) {
2515
+ unviable.push_back (
2516
+ std::make_tuple (init, UnviableReason::Inaccessible, genericParamType));
2517
+ return false ;
2518
+ }
2519
+
2520
+ return true ;
2521
+ });
2522
+
2523
+ if (!foundMatch) {
2524
+ if (unviable.empty ()) {
2525
+ diags.diagnose (attr->getLocation (), diag::type_eraser_missing_init,
2526
+ typeEraser, protocol->getName ().str ());
2527
+ diags.diagnose (nominalTypeDecl->getLoc (), diag::type_eraser_declared_here);
2528
+ return false ;
2529
+ }
2530
+
2531
+ diags.diagnose (attr->getLocation (), diag::type_eraser_unviable_init,
2532
+ typeEraser, protocol->getName ().str ());
2533
+ for (auto &candidate: unviable) {
2534
+ auto init = std::get<0 >(candidate);
2535
+ auto reason = std::get<1 >(candidate);
2536
+ auto genericParamType = std::get<2 >(candidate);
2537
+
2538
+ switch (reason) {
2539
+ case UnviableReason::Failable:
2540
+ diags.diagnose (init->getLoc (), diag::type_eraser_failable_init);
2541
+ break ;
2542
+ case UnviableReason::UnsatisfiedRequirements:
2543
+ diags.diagnose (init->getLoc (),
2544
+ diag::type_eraser_init_unsatisfied_requirements,
2545
+ genericParamType, protocol->getName ().str ());
2546
+ break ;
2547
+ case UnviableReason::Inaccessible:
2548
+ diags.diagnose (init->getLoc (), diag::type_eraser_init_not_accessible,
2549
+ init->getFormalAccess (), protocolType,
2550
+ protocol->getFormalAccess ());
2551
+ break ;
2552
+ }
2553
+ }
2554
+ return false ;
2555
+ }
2556
+
2557
+ return true ;
2558
+ }
2559
+
2560
+ void AttributeChecker::visitTypeEraserAttr (TypeEraserAttr *attr) {
2561
+ assert (isa<ProtocolDecl>(D));
2562
+ // Invoke the request.
2563
+ (void )attr->hasViableTypeEraserInit (cast<ProtocolDecl>(D));
2564
+ }
2565
+
2393
2566
void AttributeChecker::visitImplementsAttr (ImplementsAttr *attr) {
2394
2567
TypeLoc &ProtoTypeLoc = attr->getProtocolType ();
2395
2568
0 commit comments