Skip to content

Commit b080b5f

Browse files
committed
[Typed throws] An ErrorUnion type to the type system
The errorUnion type operation specifies how thrown error types are combined when multiple errors are thrown in the same context. When thrown error types can have type variables in them, we sometimes cannot resolve the errorUnion until the type variables have substitutions. In such cases, we need to persist the result of errorUnion in the constraint solver. Introduce the ErrorUnionType to do exactly that, and update the core errorUnion operation to produce an ErrorUnionType when needed. At present, this code is inert, because any errorUnion operation today involves only concrete types. However, inference of thrown errors in closures will introduce type variables, and depend on this.
1 parent b0d4ba0 commit b080b5f

File tree

15 files changed

+275
-16
lines changed

15 files changed

+275
-16
lines changed

include/swift/AST/Type.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,9 @@ class CanType : public Type {
521521
return isAnyExistentialTypeImpl(*this);
522522
}
523523

524+
/// Is this type the error existential, 'any Error'?
525+
bool isErrorExistentialType() const;
526+
524527
/// Break an existential down into a set of constraints.
525528
ExistentialLayout getExistentialLayout();
526529

include/swift/AST/TypeNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ TYPE(Pack, Type)
188188
TYPE(PackExpansion, Type)
189189
TYPE(PackElement, Type)
190190
UNCHECKED_TYPE(TypeVariable, Type)
191+
UNCHECKED_TYPE(ErrorUnion, Type)
191192
ABSTRACT_SUGARED_TYPE(Sugar, Type)
192193
SUGARED_TYPE(Paren, SugarType)
193194
SUGARED_TYPE(TypeAlias, SugarType)

include/swift/AST/Types.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,11 @@ class alignas(1 << TypeAlignInBits) TypeBase
439439
ID : 29
440440
);
441441

442+
SWIFT_INLINE_BITFIELD_FULL(ErrorUnionType, TypeBase, 32,
443+
// Number of terms in the union.
444+
NumTerms : 32
445+
);
446+
442447
SWIFT_INLINE_BITFIELD(SILFunctionType, TypeBase, NumSILExtInfoBits+1+4+1+2+1+1,
443448
ExtInfoBits : NumSILExtInfoBits,
444449
HasClangTypeInfo : 1,
@@ -873,6 +878,9 @@ class alignas(1 << TypeAlignInBits) TypeBase
873878
/// an existential metatype.
874879
bool isAnyExistentialType();
875880

881+
/// isErrorExistentialType - Determines whether this type is 'any Error'.
882+
bool isErrorExistentialType();
883+
876884
/// isObjCExistentialType - Determines whether this type is an
877885
/// class-bounded existential type whose required conformances are
878886
/// all @objc. Such types are compatible with ObjC.
@@ -6922,6 +6930,43 @@ TypeVariableType : public TypeBase {
69226930
};
69236931
DEFINE_EMPTY_CAN_TYPE_WRAPPER(TypeVariableType, Type)
69246932

6933+
/// Represents the union of two or more
6934+
class ErrorUnionType final
6935+
: public TypeBase,
6936+
public llvm::FoldingSetNode,
6937+
private llvm::TrailingObjects<ErrorUnionType, Type> {
6938+
friend TrailingObjects;
6939+
6940+
ErrorUnionType(const ASTContext *ctx, ArrayRef<Type> terms,
6941+
RecursiveTypeProperties properties)
6942+
: TypeBase(TypeKind::ErrorUnion, /*Context=*/ctx, properties) {
6943+
Bits.ErrorUnionType.NumTerms = terms.size();
6944+
std::uninitialized_copy(terms.begin(), terms.end(),
6945+
getTrailingObjects<Type>());
6946+
}
6947+
6948+
public:
6949+
/// Form a new error union type from a set of terms.
6950+
static Type get(const ASTContext &ctx, ArrayRef<Type> terms);
6951+
6952+
ArrayRef<Type> getTerms() const {
6953+
return { getTrailingObjects<Type>(), Bits.ErrorUnionType.NumTerms };
6954+
};
6955+
6956+
// Support for FoldingSet.
6957+
void Profile(llvm::FoldingSetNodeID &id) const {
6958+
Profile(id, getTerms());
6959+
}
6960+
6961+
static void Profile(llvm::FoldingSetNodeID &id, ArrayRef<Type> terms);
6962+
6963+
// Implement isa/cast/dyncast/etc.
6964+
static bool classof(const TypeBase *T) {
6965+
return T->getKind() == TypeKind::ErrorUnion;
6966+
}
6967+
};
6968+
DEFINE_EMPTY_CAN_TYPE_WRAPPER(ErrorUnionType, Type)
6969+
69256970
/// PlaceholderType - This represents a placeholder type for a type variable
69266971
/// or dependent member type that cannot be resolved to a concrete type
69276972
/// because the expression is ambiguous. This type is only used by the
@@ -7279,6 +7324,10 @@ inline bool CanType::isAnyExistentialTypeImpl(CanType type) {
72797324
return isExistentialTypeImpl(type) || isa<ExistentialMetatypeType>(type);
72807325
}
72817326

7327+
inline bool TypeBase::isErrorExistentialType() {
7328+
return getCanonicalType().isErrorExistentialType();
7329+
}
7330+
72827331
inline bool TypeBase::isClassExistentialType() {
72837332
CanType T = getCanonicalType();
72847333
if (auto pt = dyn_cast<ProtocolType>(T))

lib/AST/ASTContext.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,7 @@ struct ASTContext::Implementation {
453453
llvm::DenseMap<Type, InOutType*> InOutTypes;
454454
llvm::DenseMap<std::pair<Type, void*>, DependentMemberType *>
455455
DependentMemberTypes;
456+
llvm::FoldingSet<ErrorUnionType> ErrorUnionTypes;
456457
llvm::DenseMap<void *, PlaceholderType *> PlaceholderTypes;
457458
llvm::DenseMap<Type, DynamicSelfType *> DynamicSelfTypes;
458459
llvm::DenseMap<std::pair<EnumDecl*, Type>, EnumType*> EnumTypes;
@@ -3009,6 +3010,50 @@ Type ErrorType::get(Type originalType) {
30093010
return entry = new (mem) ErrorType(ctx, originalType, properties);
30103011
}
30113012

3013+
void ErrorUnionType::Profile(llvm::FoldingSetNodeID &id, ArrayRef<Type> terms) {
3014+
id.AddInteger(terms.size());
3015+
for (auto term : terms) {
3016+
id.AddPointer(term.getPointer());
3017+
}
3018+
}
3019+
3020+
Type ErrorUnionType::get(const ASTContext &ctx, ArrayRef<Type> terms) {
3021+
// Peep-hole the simple cases. Error union types are always synthesized by
3022+
// the type checker and never written explicitly, so we have no use for
3023+
// extra type sugar around them.
3024+
switch (terms.size()) {
3025+
case 0: return ctx.getNeverType();
3026+
case 1: return terms[0];
3027+
default: break;
3028+
}
3029+
3030+
// Determine canonicality and recursive type properties.
3031+
bool isCanonical = true;
3032+
RecursiveTypeProperties properties;
3033+
for (Type term : terms) {
3034+
if (!term->isCanonical())
3035+
isCanonical = false;
3036+
properties |= term->getRecursiveProperties();
3037+
}
3038+
3039+
// Check whether we've seen this type before.
3040+
auto arena = getArena(properties);
3041+
void *insertPos = nullptr;
3042+
llvm::FoldingSetNodeID id;
3043+
ErrorUnionType::Profile(id, terms);
3044+
if (auto knownTy = ctx.getImpl().getArena(arena).ErrorUnionTypes
3045+
.FindNodeOrInsertPos(id, insertPos))
3046+
return knownTy;
3047+
3048+
// Use trailing objects for term storage.
3049+
auto size = totalSizeToAlloc<Type>(terms.size());
3050+
auto mem = ctx.Allocate(size, alignof(ErrorUnionType), arena);
3051+
auto unionTy = new (mem) ErrorUnionType(isCanonical ? &ctx : nullptr,
3052+
terms, properties);
3053+
ctx.getImpl().getArena(arena).ErrorUnionTypes.InsertNode(unionTy, insertPos);
3054+
return unionTy;
3055+
}
3056+
30123057
Type PlaceholderType::get(ASTContext &ctx, Originator originator) {
30133058
assert(originator);
30143059

lib/AST/ASTDumper.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4432,6 +4432,13 @@ namespace {
44324432
printFoot();
44334433
}
44344434

4435+
void visitErrorUnionType(ErrorUnionType *T, StringRef label) {
4436+
printCommon("error_union_type", label);
4437+
for (auto term : T->getTerms())
4438+
printRec(term);
4439+
printFoot();
4440+
}
4441+
44354442
#undef TRIVIAL_TYPE_PRINTER
44364443
};
44374444

lib/AST/ASTMangler.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,9 @@ void ASTMangler::appendType(Type type, GenericSignature sig,
11791179
case TypeKind::TypeVariable:
11801180
llvm_unreachable("mangling type variable");
11811181

1182+
case TypeKind::ErrorUnion:
1183+
llvm_unreachable("Error unions should not persist to mangling");
1184+
11821185
case TypeKind::Module:
11831186
llvm_unreachable("Cannot mangle module type yet");
11841187

lib/AST/ASTPrinter.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6317,6 +6317,17 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
63176317
Printer << "_";
63186318
}
63196319

6320+
void visitErrorUnionType(ErrorUnionType *T) {
6321+
Printer << "error_union(";
6322+
interleave(T->getTerms(),
6323+
[&](Type type) {
6324+
visit(type);
6325+
}, [&]{
6326+
Printer << ", ";
6327+
});
6328+
Printer << ")";
6329+
}
6330+
63206331
void visitPlaceholderType(PlaceholderType *T) {
63216332
if (Options.PrintTypesForDebugging) {
63226333
Printer << "<<placeholder for ";

lib/AST/Type.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ bool CanType::isReferenceTypeImpl(CanType type, const GenericSignatureImpl *sig,
296296
case TypeKind::PackElement:
297297
case TypeKind::SILPack:
298298
case TypeKind::BuiltinTuple:
299+
case TypeKind::ErrorUnion:
299300
#define REF_STORAGE(Name, ...) \
300301
case TypeKind::Name##Storage:
301302
#include "swift/AST/ReferenceStorage.def"
@@ -1857,6 +1858,15 @@ CanType TypeBase::computeCanonicalType() {
18571858
Result = BoundGenericType::get(BGT->getDecl(), parentTy, CanGenericArgs);
18581859
break;
18591860
}
1861+
case TypeKind::ErrorUnion: {
1862+
SmallVector<Type, 2> newTerms;
1863+
for (auto term : cast<ErrorUnionType>(this)->getTerms()) {
1864+
newTerms.push_back(term->getCanonicalType());
1865+
}
1866+
ASTContext &ctx = newTerms[0]->getASTContext();
1867+
Result = ErrorUnionType::get(ctx, newTerms).getPointer();
1868+
break;
1869+
}
18601870
}
18611871

18621872
// Cache the canonical type for future queries.
@@ -4691,6 +4701,48 @@ case TypeKind::Id:
46914701
return ParenType::get(Ptr->getASTContext(), underlying);
46924702
}
46934703

4704+
case TypeKind::ErrorUnion: {
4705+
auto errorUnion = cast<ErrorUnionType>(base);
4706+
bool anyChanged = false;
4707+
SmallVector<Type, 4> terms;
4708+
unsigned Index = 0;
4709+
for (Type term : errorUnion->getTerms()) {
4710+
Type transformedTerm =
4711+
term.transformWithPosition(TypePosition::Invariant, fn);
4712+
if (!transformedTerm)
4713+
return Type();
4714+
4715+
// If nothing has changed, just keep going.
4716+
if (!anyChanged &&
4717+
transformedTerm.getPointer() == term.getPointer()) {
4718+
++Index;
4719+
continue;
4720+
}
4721+
4722+
// If this is the first change we've seen, copy all of the previous
4723+
// elements.
4724+
if (!anyChanged) {
4725+
// Copy all of the previous elements.
4726+
terms.append(errorUnion->getTerms().begin(),
4727+
errorUnion->getTerms().begin() + Index);
4728+
anyChanged = true;
4729+
}
4730+
4731+
// If the transformed type is a pack, immediately expand it.
4732+
if (auto termPack = getTransformedPack(transformedTerm)) {
4733+
auto termElements = termPack->getElementTypes();
4734+
terms.append(termElements.begin(), termElements.end());
4735+
} else {
4736+
terms.push_back(transformedTerm);
4737+
}
4738+
}
4739+
4740+
if (!anyChanged)
4741+
return *this;
4742+
4743+
return ErrorUnionType::get(Ptr->getASTContext(), terms);
4744+
}
4745+
46944746
case TypeKind::Pack: {
46954747
auto pack = cast<PackType>(base);
46964748
bool anyChanged = false;
@@ -5389,6 +5441,7 @@ ReferenceCounting TypeBase::getReferenceCounting() {
53895441
case TypeKind::PackElement:
53905442
case TypeKind::SILPack:
53915443
case TypeKind::BuiltinTuple:
5444+
case TypeKind::ErrorUnion:
53925445
#define REF_STORAGE(Name, ...) \
53935446
case TypeKind::Name##Storage:
53945447
#include "swift/AST/ReferenceStorage.def"
@@ -5622,6 +5675,13 @@ bool TypeBase::hasSimpleTypeRepr() const {
56225675
}
56235676
}
56245677

5678+
bool CanType::isErrorExistentialType() const {
5679+
if (!isExistentialTypeImpl(*this))
5680+
return false;
5681+
5682+
return const_cast<CanType *>(this)->getExistentialLayout().isErrorExistential();
5683+
}
5684+
56255685
bool CanType::isForeignReferenceType() {
56265686
if (auto *classDecl = getPointer()->lookThroughAllOptionalTypes()->getClassOrBoundGenericClass())
56275687
return classDecl->isForeignReferenceType();

lib/AST/TypeWalker.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,13 @@ class Traversal : public TypeVisitor<Traversal, bool>
250250

251251
bool visitTypeVariableType(TypeVariableType *ty) { return false; }
252252

253+
bool visitErrorUnionType(ErrorUnionType *ty) {
254+
for (auto term : ty->getTerms())
255+
if (doIt(term))
256+
return true;
257+
return false;
258+
}
259+
253260
bool visitSILBlockStorageType(SILBlockStorageType *ty) {
254261
return doIt(ty->getCaptureType());
255262
}

lib/ClangImporter/ImportType.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1974,6 +1974,7 @@ class GetSendableType :
19741974
NEVER_VISIT(PackExpansionType)
19751975
NEVER_VISIT(PackElementType)
19761976
NEVER_VISIT(TypeVariableType)
1977+
NEVER_VISIT(ErrorUnionType)
19771978

19781979
VISIT(SugarType, recurse)
19791980

0 commit comments

Comments
 (0)