Skip to content

[SE-0335] Enable explicit existential types. #40666

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Jan 15, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
cf44889
[ClangImporter] When importing ObjC pointer types, wrap protocol
hborla Dec 14, 2021
f368b77
[Sema] Account for ExistentialType in lookupExistentialConformance.
hborla Dec 14, 2021
02f5e47
[Sema] Resolve @_implements protocols using the generic requirement
hborla Dec 14, 2021
efeb709
[Deserialization] Fix deserialization of explicit existential types.
hborla Dec 21, 2021
6cee193
[Type System] When explicit existential types are enabled, wrap Error
hborla Dec 21, 2021
a7fa469
[GenericSignature] Don't allow conformance requirements with explicit
hborla Dec 21, 2021
f6f53f6
[Codable] When explicit existential types are enabled, synthesize Cod…
hborla Dec 21, 2021
86730ca
[Interop] Handle existential types in ClangTypeConverter and
hborla Dec 21, 2021
1d56338
[SILOptimizer] Use the constraint type in ExistentialSpecializer when
hborla Dec 21, 2021
f7a82ac
[Type Reconstruction] For now, reconstruct existential types without
hborla Dec 21, 2021
5e4fbc4
[Diagnostics] Stage in the new error message for existential types that
hborla Dec 21, 2021
733648b
[Sema] When explicit existential types are enabled, type witnesses that
hborla Dec 22, 2021
d971d48
[Type System] With explicit existential types, make sure existential
hborla Jan 7, 2022
b7c6348
[Sema] Implement type join for existential types.
hborla Jan 7, 2022
5dced8e
[ConstraintSystem] Fix a few constraint system corner cases with expl…
hborla Jan 7, 2022
f018328
[TypeResolver] Fix a few corner cases in type resolution for explicit
hborla Jan 7, 2022
6e6ca13
[Type System] Use the constraint type of an existential type in
hborla Jan 7, 2022
3021071
[ASTPrinter] Add an option that controls whether existential types
hborla Jan 7, 2022
ee331a8
[Type System] Enable explicit existential types.
hborla Jan 8, 2022
6060de6
[AST] Teach ExistentialType::get to only produce ExistentialType when
hborla Jan 8, 2022
c48b593
[Diagnostics] Remove the warning for 'any Any' and 'any AnyObject'.
hborla Jan 8, 2022
992a871
[Type Reconstruction] Re-construct existential types with Existential…
hborla Jan 11, 2022
6608bf8
[Sema] Fix the source compatibility check for member operators with
hborla Jan 14, 2022
626bea2
[ConstraintSystem] Return an existential type in getTypeOfMemberRefer…
hborla Jan 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ class ASTContext final {
/// Retrieve the declaration of Swift.Error.
ProtocolDecl *getErrorDecl() const;
CanType getExceptionType() const;
CanType getErrorExistentialType() const;

#define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \
/** Retrieve the declaration of Swift.NAME. */ \
Expand Down
2 changes: 1 addition & 1 deletion include/swift/AST/ASTSynthesis.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ inline Type synthesizeType(SynthesisContext &SC,
switch (kind) {
case _any: return SC.Context.TheAnyType;
case _bridgeObject: return SC.Context.TheBridgeObjectType;
case _error: return SC.Context.getExceptionType();
case _error: return SC.Context.getErrorExistentialType();
case _executor: return SC.Context.TheExecutorType;
case _job: return SC.Context.TheJobType;
case _nativeObject: return SC.Context.TheNativeObjectType;
Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ class CanType : public Type {

static bool isReferenceTypeImpl(CanType type, const GenericSignatureImpl *sig,
bool functionsCount);
static bool isConstraintTypeImpl(CanType type);
static bool isExistentialTypeImpl(CanType type);
static bool isAnyExistentialTypeImpl(CanType type);
static bool isObjCExistentialTypeImpl(CanType type);
Expand Down Expand Up @@ -457,6 +458,10 @@ class CanType : public Type {
/*functions count*/ false);
}

bool isConstraintType() const {
return isConstraintTypeImpl(*this);
}

/// Is this type existential?
bool isExistentialType() const {
return isExistentialTypeImpl(*this);
Expand Down
14 changes: 14 additions & 0 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,9 @@ class alignas(1 << TypeAlignInBits) TypeBase
return getRecursiveProperties().hasDependentMember();
}

/// Whether this type represents a generic constraint.
bool isConstraintType() const;

/// isExistentialType - Determines whether this type is an existential type,
/// whose real (runtime) type is unknown but which is known to conform to
/// some set of protocols. Protocol and protocol-conformance types are
Expand Down Expand Up @@ -2731,6 +2734,8 @@ class ExistentialMetatypeType : public AnyMetatypeType {
static bool classof(const TypeBase *T) {
return T->getKind() == TypeKind::ExistentialMetatype;
}

Type getExistentialInstanceType();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the existence of this method, but it messes up TypeWalker if I make AnyMetatypeType::getInstanceType() return ExistentialType for existential metatypes. Alternatively, I could add getRawInstanceType() or something like that for the few places that actually need the stored InstanceType. Really, I'd like to just replace ExistentialMetatypeType with ExistentialType(MetatypeType(...))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think your longer-term goal of removing ExistentialMetatypeType is good and in the meantime, this workaround is reasonable.


private:
ExistentialMetatypeType(Type T, const ASTContext *C,
Expand Down Expand Up @@ -6335,6 +6340,15 @@ inline GenericTypeParamType *TypeBase::getRootGenericParam() {
return t->castTo<GenericTypeParamType>();
}

inline bool TypeBase::isConstraintType() const {
return getCanonicalType().isConstraintType();
}

inline bool CanType::isConstraintTypeImpl(CanType type) {
return (isa<ProtocolType>(type) ||
isa<ProtocolCompositionType>(type));
}

inline bool TypeBase::isExistentialType() {
return getCanonicalType().isExistentialType();
}
Expand Down
2 changes: 1 addition & 1 deletion include/swift/SIL/SILCloner.h
Original file line number Diff line number Diff line change
Expand Up @@ -2220,7 +2220,7 @@ visitOpenExistentialMetatypeInst(OpenExistentialMetatypeInst *Inst) {
auto openedType = Inst->getType().getASTType();
auto exType = Inst->getOperand()->getType().getASTType();
while (auto exMetatype = dyn_cast<ExistentialMetatypeType>(exType)) {
exType = exMetatype.getInstanceType();
exType = exMetatype->getExistentialInstanceType()->getCanonicalType();
openedType = cast<MetatypeType>(openedType).getInstanceType();
}
remapOpenedType(cast<OpenedArchetypeType>(openedType));
Expand Down
2 changes: 1 addition & 1 deletion include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -7072,7 +7072,7 @@ class InitExistentialMetatypeInst final
auto exType = getType().getASTType();
auto concreteType = getOperand()->getType().getASTType();
while (auto exMetatype = dyn_cast<ExistentialMetatypeType>(exType)) {
exType = exMetatype.getInstanceType();
exType = exMetatype->getExistentialInstanceType()->getCanonicalType();
concreteType = cast<MetatypeType>(concreteType).getInstanceType();
}
assert(exType.isExistentialType());
Expand Down
45 changes: 37 additions & 8 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,16 @@ ProtocolDecl *ASTContext::getErrorDecl() const {
return getProtocol(KnownProtocolKind::Error);
}

CanType ASTContext::getErrorExistentialType() const {
Type errorType = getExceptionType();
if (LangOpts.EnableExplicitExistentialTypes &&
errorType->isConstraintType()) {
errorType = ExistentialType::get(errorType);
}

return errorType->getCanonicalType();
}

EnumElementDecl *ASTContext::getOptionalSomeDecl() const {
if (!getImpl().OptionalSomeDecl)
getImpl().OptionalSomeDecl = getOptionalDecl()->getUniqueElement(/*hasVal*/true);
Expand Down Expand Up @@ -3319,6 +3329,11 @@ MetatypeType::MetatypeType(Type T, const ASTContext *C,
ExistentialMetatypeType *
ExistentialMetatypeType::get(Type T, Optional<MetatypeRepresentation> repr,
const ASTContext &ctx) {
// If we're creating an existential metatype from an
// existential type, wrap the constraint type direcly.
if (auto existential = T->getAs<ExistentialType>())
T = existential->getConstraintType();

auto properties = T->getRecursiveProperties();
auto arena = getArena(properties);

Expand Down Expand Up @@ -3351,6 +3366,18 @@ ExistentialMetatypeType::ExistentialMetatypeType(Type T,
}
}

Type ExistentialMetatypeType::getExistentialInstanceType() {
auto instanceType = getInstanceType();
// Note that Any and AnyObject don't yet use ExistentialType.
if (getASTContext().LangOpts.EnableExplicitExistentialTypes &&
!instanceType->is<ExistentialMetatypeType>() &&
!(instanceType->isAny() || instanceType->isAnyObject())) {
instanceType = ExistentialType::get(instanceType);
}

return instanceType;
}

ModuleType *ModuleType::get(ModuleDecl *M) {
ASTContext &C = M->getASTContext();

Expand Down Expand Up @@ -4316,7 +4343,7 @@ GenericEnvironment *OpenedArchetypeType::getGenericEnvironment() const {

CanType OpenedArchetypeType::getAny(Type existential) {
if (auto metatypeTy = existential->getAs<ExistentialMetatypeType>()) {
auto instanceTy = metatypeTy->getInstanceType();
auto instanceTy = metatypeTy->getExistentialInstanceType();
return CanMetatypeType::get(OpenedArchetypeType::getAny(instanceTy));
}
assert(existential->isExistentialType());
Expand Down Expand Up @@ -4865,7 +4892,7 @@ Type ASTContext::getBridgedToObjC(const DeclContext *dc, Type type,
if (auto nsErrorTy = getNSErrorType()) {
// The corresponding value type is Error.
if (bridgedValueType)
*bridgedValueType = getErrorDecl()->getDeclaredInterfaceType();
*bridgedValueType = getErrorExistentialType();

return nsErrorTy;
}
Expand Down Expand Up @@ -4902,7 +4929,7 @@ Type ASTContext::getBridgedToObjC(const DeclContext *dc, Type type,
if (findConformance(KnownProtocolKind::Error)) {
// The corresponding value type is Error.
if (bridgedValueType)
*bridgedValueType = getErrorDecl()->getDeclaredInterfaceType();
*bridgedValueType = getErrorExistentialType();

// Bridge to NSError.
if (auto nsErrorTy = getNSErrorType())
Expand Down Expand Up @@ -4990,24 +5017,26 @@ CanGenericSignature ASTContext::getSingleGenericParameterSignature() const {
// constraints while existential values do.
CanGenericSignature ASTContext::getOpenedArchetypeSignature(Type type) {
assert(type->isExistentialType());
if (auto existential = type->getAs<ExistentialType>())
type = existential->getConstraintType();

const CanType existential = type->getCanonicalType();
const CanType constraint = type->getCanonicalType();

// The opened archetype signature for a protocol type is identical
// to the protocol's own canonical generic signature.
if (const auto protoTy = dyn_cast<ProtocolType>(existential)) {
if (const auto protoTy = dyn_cast<ProtocolType>(constraint)) {
return protoTy->getDecl()->getGenericSignature().getCanonicalSignature();
}

auto found = getImpl().ExistentialSignatures.find(existential);
auto found = getImpl().ExistentialSignatures.find(constraint);
if (found != getImpl().ExistentialSignatures.end())
return found->second;

auto genericParam =
GenericTypeParamType::get(/*type sequence*/ false,
/*depth*/ 0, /*index*/ 0, *this);
Requirement requirement(RequirementKind::Conformance, genericParam,
existential);
constraint);
auto genericSig = buildGenericSignature(*this,
GenericSignature(),
{genericParam},
Expand All @@ -5016,7 +5045,7 @@ CanGenericSignature ASTContext::getOpenedArchetypeSignature(Type type) {
CanGenericSignature canGenericSig(genericSig);

auto result = getImpl().ExistentialSignatures.insert(
std::make_pair(existential, canGenericSig));
std::make_pair(constraint, canGenericSig));
assert(result.second);
(void) result;

Expand Down
12 changes: 8 additions & 4 deletions lib/AST/ASTDemangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -584,11 +584,15 @@ Type ASTBuilder::createProtocolCompositionType(
members.push_back(protocol->getDeclaredInterfaceType());
if (superclass && superclass->getClassOrBoundGenericClass())
members.push_back(superclass);

// FIXME: When explicit existential types are enabled, protocol
// compositions should be wrapped in ExistentialType based on
// context, similar to how protocol compositions are resolved
// during type resolution. For example, protocol compositions
// in parameter types should be wrapped in ExistentialType, but
// protocol compositions on the right side of a conformance
// requirement should not.
Type composition = ProtocolCompositionType::get(Ctx, members, isClassBound);
if (Ctx.LangOpts.EnableExplicitExistentialTypes &&
!(composition->isAny() || composition->isAnyObject())) {
composition = ExistentialType::get(composition);
}
return composition;
}

Expand Down
14 changes: 9 additions & 5 deletions lib/AST/ASTVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2067,7 +2067,10 @@ class Verifier : public ASTWalker {
abort();
}

checkSameType(E->getBase()->getType(), metatype->getInstanceType(),
auto instance = metatype->getInstanceType();
if (auto existential = metatype->getAs<ExistentialMetatypeType>())
instance = existential->getExistentialInstanceType();
checkSameType(E->getBase()->getType(), instance,
"base type of .Type expression");
verifyCheckedBase(E);
}
Expand Down Expand Up @@ -3422,11 +3425,12 @@ class Verifier : public ASTWalker {
}

Type checkExceptionTypeExists(const char *where) {
auto exn = Ctx.getErrorDecl();
if (exn) return exn->getDeclaredInterfaceType();
if (!Ctx.getErrorDecl()) {
Out << "exception type does not exist in " << where << "\n";
abort();
}

Out << "exception type does not exist in " << where << "\n";
abort();
return Ctx.getErrorExistentialType();
}

bool isGoodSourceRange(SourceRange SR) {
Expand Down
8 changes: 8 additions & 0 deletions lib/AST/ClangTypeConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,11 @@ ClangTypeConverter::visitProtocolCompositionType(ProtocolCompositionType *type)
return clangCtx.getObjCObjectPointerType(clangType);
}

clang::QualType
ClangTypeConverter::visitExistentialType(ExistentialType *type) {
return visit(type->getConstraintType());
}

clang::QualType
ClangTypeConverter::visitBuiltinRawPointerType(BuiltinRawPointerType *type) {
return ClangASTContext.VoidPtrTy;
Expand Down Expand Up @@ -828,6 +833,9 @@ clang::QualType ClangTypeConverter::convert(Type type) {
return it->second;

// Try to do this without making cache entries for obvious cases.
if (auto existential = type->getAs<ExistentialType>())
type = existential->getConstraintType();

if (auto nominal = type->getAs<NominalType>()) {
auto decl = nominal->getDecl();
if (auto clangDecl = decl->getClangDecl()) {
Expand Down
1 change: 1 addition & 0 deletions lib/AST/ClangTypeConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ class ClangTypeConverter :
clang::QualType visitEnumType(EnumType *type);
clang::QualType visitFunctionType(FunctionType *type);
clang::QualType visitProtocolCompositionType(ProtocolCompositionType *type);
clang::QualType visitExistentialType(ExistentialType *type);
clang::QualType visitBuiltinRawPointerType(BuiltinRawPointerType *type);
clang::QualType visitBuiltinIntegerType(BuiltinIntegerType *type);
clang::QualType visitBuiltinFloatType(BuiltinFloatType *type);
Expand Down
3 changes: 2 additions & 1 deletion lib/AST/GenericSignatureBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4500,7 +4500,8 @@ ConstraintResult GenericSignatureBuilder::addTypeRequirement(
}

// Check whether we have a reasonable constraint type at all.
if (!constraintType->isExistentialType() &&
if (!constraintType->is<ProtocolType>() &&
!constraintType->is<ProtocolCompositionType>() &&
!constraintType->getClassOrBoundGenericClass()) {
if (source.getLoc().isValid() && !constraintType->hasError()) {
Impl->HadAnyError = true;
Expand Down
8 changes: 6 additions & 2 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -978,13 +978,17 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
// existential to an archetype parameter, so for now we restrict this to
// @objc protocols and marker protocols.
if (!layout.isObjC() && !protocol->isMarkerProtocol()) {
auto constraint = type;
if (auto existential = constraint->getAs<ExistentialType>())
constraint = existential->getConstraintType();

// There's a specific exception for protocols with self-conforming
// witness tables, but the existential has to be *exactly* that type.
// TODO: synthesize witness tables on-demand for protocol compositions
// that can satisfy the requirement.
if (protocol->requiresSelfConformanceWitnessTable() &&
type->is<ProtocolType>() &&
type->castTo<ProtocolType>()->getDecl() == protocol)
constraint->is<ProtocolType>() &&
constraint->castTo<ProtocolType>()->getDecl() == protocol)
return ProtocolConformanceRef(ctx.getSelfConformance(protocol));

return ProtocolConformanceRef::forInvalid();
Expand Down
5 changes: 5 additions & 0 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,8 @@ Type TypeBase::eraseOpenedExistential(OpenedArchetypeType *opened) {
auto instanceType = metatypeType->getInstanceType();
if (instanceType->hasOpenedExistential()) {
instanceType = instanceType->eraseOpenedExistential(opened);
if (auto existential = instanceType->getAs<ExistentialType>())
instanceType = existential->getConstraintType();
return ExistentialMetatypeType::get(instanceType);
}
}
Expand Down Expand Up @@ -1199,6 +1201,9 @@ Type TypeBase::replaceSelfParameterType(Type newSelf) {
/// Look through a metatype, or just return the original type if it is
/// not a metatype.
Type TypeBase::getMetatypeInstanceType() {
if (auto existentialMetaType = getAs<ExistentialMetatypeType>())
return existentialMetaType->getExistentialInstanceType();

if (auto metaTy = getAs<AnyMetatypeType>())
return metaTy->getInstanceType();

Expand Down
24 changes: 24 additions & 0 deletions lib/AST/TypeJoinMeet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ struct TypeJoin : CanTypeVisitor<TypeJoin, CanType> {
CanType visitBoundGenericStructType(CanType second);
CanType visitMetatypeType(CanType second);
CanType visitExistentialMetatypeType(CanType second);
CanType visitExistentialType(CanType second);
CanType visitModuleType(CanType second);
CanType visitDynamicSelfType(CanType second);
CanType visitArchetypeType(CanType second);
Expand Down Expand Up @@ -271,6 +272,29 @@ CanType TypeJoin::visitExistentialMetatypeType(CanType second) {
return ExistentialMetatypeType::get(joinInstance)->getCanonicalType();
}

CanType TypeJoin::visitExistentialType(CanType second) {
assert(First != second);

if (First->getKind() != second->getKind())
return TheAnyType;

auto firstConstraint = First->castTo<ExistentialType>()
->getConstraintType()->getCanonicalType();
auto secondConstraint = second->castTo<ExistentialType>()
->getConstraintType()->getCanonicalType();

auto joinInstance = join(firstConstraint, secondConstraint);
if (!joinInstance)
return CanType();

if (joinInstance->is<ExistentialMetatypeType>() ||
joinInstance->isAny() ||
joinInstance->isAnyObject())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: is<ExistentialMetatypeType>() seems unnecessary here.

Copy link
Member Author

@hborla hborla Jan 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe I removed this code in my follow-up PR but I'm not certain

EDIT: oops, this is my follow up PR, I got it confused with the original 😅

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I did remove this bit of code in a follow-up commit in this PR. You're right that it's unnecessary.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, sorry, I ought to have checked if it's still there on main.

return joinInstance;

return ExistentialType::get(joinInstance)->getCanonicalType();
}

CanType TypeJoin::visitModuleType(CanType second) {
assert(First != second);

Expand Down
3 changes: 3 additions & 0 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6244,6 +6244,9 @@ SwiftDeclConverter::importSwiftNewtype(const clang::TypedefNameDecl *decl,
// Local function to add a known protocol only when the
// underlying type conforms to it.
auto computedNominal = computedPropertyUnderlyingType->getAnyNominal();
if (auto existential =
computedPropertyUnderlyingType->getAs<ExistentialType>())
computedNominal = existential->getConstraintType()->getAnyNominal();
auto transferKnown = [&](KnownProtocolKind kind) {
if (!computedNominal)
return false;
Expand Down
Loading