@@ -450,15 +450,56 @@ Type ConstraintSystem::inferCaughtErrorType(CatchNode catchNode) {
450
450
}
451
451
}
452
452
453
+ // The type variable that already describes the caught error type for
454
+ // this particular catch node, if it exists. These are lazily created
455
+ // when we are unable to determine the caught error type completely.
456
+ TypeVariableType *caughtTypeVariable = nullptr;
457
+ ConstraintLocator *caughtTypeVariableLocator = nullptr;
458
+
459
+ /// The complete set of type variables referenced by the throw sites.
460
+ SmallPtrSet<TypeVariableType *, 4> referencedTypeVariables;
461
+
462
+ /// The caught error type thus far, which starts at Never because Never
463
+ /// doesn't contribute to throwing anything.
453
464
Type caughtErrorType = ctx.getNeverType();
454
465
for (const auto &throwSite : throwSites) {
455
466
Type type = simplifyType(throwSite.type);
456
-
457
467
Type thrownErrorType;
458
468
switch (throwSite.kind) {
469
+ case PotentialThrowSite::CaughtTypeVariable:
470
+ // The type variable hasn't been bound yet, so record that we
471
+ // know it exists and keep looking.
472
+ if (auto typeVar = type->getAs<TypeVariableType>()) {
473
+ caughtTypeVariable = typeVar;
474
+ caughtTypeVariableLocator = throwSite.locator;
475
+ continue;
476
+ }
477
+
478
+ // The caught type variable has been bound to a fixed type. Return
479
+ // that.
480
+ return type;
481
+
459
482
case PotentialThrowSite::Application: {
460
- auto fnType = type->castTo<AnyFunctionType>();
461
- thrownErrorType = fnType->getEffectiveThrownErrorTypeOrNever();
483
+ auto fnType = type->getAs<AnyFunctionType>();
484
+ if (!fnType) {
485
+ // This applicable function constraint either still involves type
486
+ // variables or wasn't actually a function.
487
+ //
488
+ // If it still involves type variables, track those so we know
489
+ // when to look at the potential throw sites again. Otherwise,
490
+ // it's something like a metatype or a callable-as-function type,
491
+ // in which case there will be another Application throw site that
492
+ // handles the throw site.
493
+ if (type->hasTypeVariable()) {
494
+ type->getTypeVariables(referencedTypeVariables);
495
+ }
496
+
497
+ continue;
498
+ }
499
+
500
+ // Dig out the thrown error type from the function type.
501
+ thrownErrorType =
502
+ simplifyType(fnType->getEffectiveThrownErrorTypeOrNever());
462
503
break;
463
504
}
464
505
@@ -469,6 +510,13 @@ Type ConstraintSystem::inferCaughtErrorType(CatchNode catchNode) {
469
510
break;
470
511
}
471
512
513
+ // If the result has type variables, there's nothing useful we can
514
+ // do. Just record them and continue on.
515
+ if (thrownErrorType->hasTypeVariable()) {
516
+ thrownErrorType->getTypeVariables(referencedTypeVariables);
517
+ continue;
518
+ }
519
+
472
520
// Perform the errorUnion() of the caught error type so far with the
473
521
// thrown error type of this potential throw site.
474
522
caughtErrorType = TypeChecker::errorUnion(
@@ -482,7 +530,46 @@ Type ConstraintSystem::inferCaughtErrorType(CatchNode catchNode) {
482
530
break;
483
531
}
484
532
485
- return caughtErrorType;
533
+ // If the caught error type is 'any Error', or if there are no
534
+ // unresolved type variables, then we now have a fixed type for the
535
+ // caught error type.
536
+ if (caughtErrorType->isErrorExistentialType() ||
537
+ referencedTypeVariables.empty()) {
538
+ return caughtErrorType;
539
+ }
540
+
541
+ // We don't have sufficient information to provide a concrete caught
542
+ // error type, so we will use a type variable instead. If none exists
543
+ // yet, create one now.
544
+ if (!caughtTypeVariable) {
545
+ // Create the type variable.
546
+ caughtTypeVariableLocator = getConstraintLocator(ASTNode(catchNode));
547
+ caughtTypeVariable = createTypeVariable(caughtTypeVariableLocator, 0);
548
+
549
+ // Create a constraint stating that this is the caught error type
550
+ // for the given catch node. This constraint will get reactivated
551
+ // whenever any of the type variables it depends on change, causing
552
+ // us to re-evaluate the potential throw sites.
553
+ SmallVector<TypeVariableType *, 2> allTypeVars(
554
+ referencedTypeVariables.begin(), referencedTypeVariables.end());
555
+ auto constraint = Constraint::createCaughtError(
556
+ *this, Type(caughtTypeVariable), catchNode,
557
+ caughtTypeVariableLocator, allTypeVars);
558
+ addUnsolvedConstraint(constraint);
559
+
560
+ // Record this type variable in the list of potential throw sites,
561
+ // so we can find it again later rather than creating a new one.
562
+ // This is effectively using the potential throw sites as a
563
+ // CatchNode -> TypeVariableType * map, without requiring a separate
564
+ // data structure.
565
+ potentialThrowSites.push_back(
566
+ {catchNode,
567
+ PotentialThrowSite{PotentialThrowSite::CaughtTypeVariable,
568
+ Type(caughtTypeVariable),
569
+ caughtTypeVariableLocator}});
570
+ }
571
+
572
+ return Type(caughtTypeVariable);
486
573
}
487
574
488
575
TypeVariableType *
@@ -3984,6 +4071,8 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
3984
4071
// If accessing this declaration could throw an error, record this as a
3985
4072
// potential throw site.
3986
4073
if (thrownErrorTypeOnAccess) {
4074
+ // FIXME: We likely need to do this before overload resolution,
4075
+ // otherwise we might establish the thrown error type too early.
3987
4076
recordPotentialThrowSite(
3988
4077
PotentialThrowSite::PropertyAccess, thrownErrorTypeOnAccess, locator);
3989
4078
}
0 commit comments