Skip to content

[5.7][Sema] Use ExistentialType for Any and AnyObject. #59657

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 4 commits into from
Jun 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 8 additions & 2 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ class ASTContext final {
/// Retrieve the declaration of Swift.Error.
ProtocolDecl *getErrorDecl() const;
CanType getErrorExistentialType() const;

#define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \
/** Retrieve the declaration of Swift.NAME. */ \
DECL_CLASS *get##NAME##Decl() const; \
Expand All @@ -561,7 +561,13 @@ class ASTContext final {
/// Retrieve the declaration of the "pointee" property of a pointer type.
VarDecl *getPointerPointeePropertyDecl(PointerTypeKind ptrKind) const;

/// Retrieve the type Swift.AnyObject.
/// Retrieve the type Swift.Any as an existential type.
CanType getAnyExistentialType() const;

/// Retrieve the type Swift.AnyObject as a constraint.
CanType getAnyObjectConstraint() const;

/// Retrieve the type Swift.AnyObject as an existential type.
CanType getAnyObjectType() const;

#define KNOWN_SDK_TYPE_DECL(MODULE, NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \
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 @@ -54,7 +54,7 @@ enum SingletonTypeSynthesizer {
inline Type synthesizeType(SynthesisContext &SC,
SingletonTypeSynthesizer kind) {
switch (kind) {
case _any: return SC.Context.TheAnyType;
case _any: return SC.Context.getAnyExistentialType();
case _bridgeObject: return SC.Context.TheBridgeObjectType;
case _error: return SC.Context.getErrorExistentialType();
case _executor: return SC.Context.TheExecutorType;
Expand Down
22 changes: 20 additions & 2 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -5398,17 +5398,35 @@ struct ExistentialTypeGeneralization {
class ExistentialType final : public TypeBase {
Type ConstraintType;

/// Whether to print this existential type with the 'any' keyword,
/// e.g. in diagnostics.
///
/// Any and AnyObject need not use 'any', and they are printed
/// in diagnostics without 'any' unless wrapped in MetatypeType.
/// This field should only be used by TypePrinter.
bool PrintWithAny;

ExistentialType(Type constraintType,
bool printWithAny,
const ASTContext *canonicalContext,
RecursiveTypeProperties properties)
: TypeBase(TypeKind::Existential, canonicalContext, properties),
ConstraintType(constraintType) {}
ConstraintType(constraintType), PrintWithAny(printWithAny) {}

public:
static Type get(Type constraint, bool forceExistential = false);
static Type get(Type constraint);

Type getConstraintType() const { return ConstraintType; }

bool shouldPrintWithAny() const { return PrintWithAny; }

void forcePrintWithAny(llvm::function_ref<void(Type)> print) {
bool oldValue = PrintWithAny;
PrintWithAny = true;
print(this);
PrintWithAny = oldValue;
}

bool requiresClass() const {
if (auto protocol = ConstraintType->getAs<ProtocolType>())
return protocol->requiresClass();
Expand Down
31 changes: 19 additions & 12 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -956,7 +956,11 @@ ASTContext::getPointerPointeePropertyDecl(PointerTypeKind ptrKind) const {
llvm_unreachable("bad pointer kind");
}

CanType ASTContext::getAnyObjectType() const {
CanType ASTContext::getAnyExistentialType() const {
return ExistentialType::get(TheAnyType)->getCanonicalType();
}

CanType ASTContext::getAnyObjectConstraint() const {
if (getImpl().AnyObjectType) {
return getImpl().AnyObjectType;
}
Expand All @@ -967,6 +971,11 @@ CanType ASTContext::getAnyObjectType() const {
return getImpl().AnyObjectType;
}

CanType ASTContext::getAnyObjectType() const {
return ExistentialType::get(getAnyObjectConstraint())
->getCanonicalType();
}

#define KNOWN_SDK_TYPE_DECL(MODULE, NAME, DECLTYPE, GENERIC_ARGS) \
DECLTYPE *ASTContext::get##NAME##Decl() const { \
if (!getImpl().NAME##Decl) { \
Expand Down Expand Up @@ -4242,20 +4251,18 @@ ProtocolType::ProtocolType(ProtocolDecl *TheDecl, Type Parent,
RecursiveTypeProperties properties)
: NominalType(TypeKind::Protocol, &Ctx, TheDecl, Parent, properties) { }

Type ExistentialType::get(Type constraint, bool forceExistential) {
Type ExistentialType::get(Type constraint) {
auto &C = constraint->getASTContext();
if (!forceExistential) {
// FIXME: Any and AnyObject don't yet use ExistentialType.
if (constraint->isAny() || constraint->isAnyObject())
return constraint;

// ExistentialMetatypeType is already an existential type.
if (constraint->is<ExistentialMetatypeType>())
return constraint;
}
// ExistentialMetatypeType is already an existential type.
if (constraint->is<ExistentialMetatypeType>())
return constraint;

assert(constraint->isConstraintType());

bool printWithAny = true;
if (constraint->isEqual(C.TheAnyType) || constraint->isAnyObject())
printWithAny = false;

auto properties = constraint->getRecursiveProperties();
if (constraint->is<ParameterizedProtocolType>())
properties |= RecursiveTypeProperties::HasParameterizedExistential;
Expand All @@ -4266,7 +4273,7 @@ Type ExistentialType::get(Type constraint, bool forceExistential) {
return entry;

const ASTContext *canonicalContext = constraint->isCanonical() ? &C : nullptr;
return entry = new (C, arena) ExistentialType(constraint,
return entry = new (C, arena) ExistentialType(constraint, printWithAny,
canonicalContext,
properties);
}
Expand Down
53 changes: 26 additions & 27 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5145,7 +5145,7 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
llvm_unreachable("bad opaque-return-type printing mode");
}
} else if (auto existential = dyn_cast<ExistentialType>(T.getPointer())) {
if (!Options.PrintExplicitAny)
if (!Options.PrintExplicitAny || !existential->shouldPrintWithAny())
return isSimpleUnderPrintOptions(existential->getConstraintType());
} else if (auto existential = dyn_cast<ExistentialMetatypeType>(T.getPointer())) {
if (!Options.PrintExplicitAny)
Expand Down Expand Up @@ -5581,31 +5581,30 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
}

Type instanceType = T->getInstanceType();
if (Options.PrintExplicitAny) {
if (T->is<ExistentialMetatypeType>()) {
Printer << "any ";

// FIXME: We need to replace nested existential metatypes so that
// we don't print duplicate 'any'. This will be unnecessary once
// ExistentialMetatypeType is split into ExistentialType(MetatypeType).
instanceType = Type(instanceType).transform([](Type type) -> Type {
if (auto existential = type->getAs<ExistentialMetatypeType>())
return MetatypeType::get(existential->getInstanceType());

return type;
});
} else if (instanceType->isAny() || instanceType->isAnyObject()) {
// FIXME: 'any' is needed to distinguish between '(any Any).Type'
// and 'any Any.Type'. However, this combined with the above hack
// to replace nested existential metatypes with metatypes causes
// a bug in printing nested existential metatypes for Any and AnyObject,
// e.g. 'any (any Any).Type.Type'. This will be fixed by using
// ExistentialType for Any and AnyObject.
instanceType = ExistentialType::get(instanceType, /*forceExistential=*/true);
}
}
if (Options.PrintExplicitAny && T->is<ExistentialMetatypeType>()) {
Printer << "any ";

// FIXME: We need to replace nested existential metatypes so that
// we don't print duplicate 'any'. This will be unnecessary once
// ExistentialMetatypeType is split into ExistentialType(MetatypeType).
printWithParensIfNotSimple(instanceType.transform([](Type type) -> Type {
if (auto existential = type->getAs<ExistentialMetatypeType>())
return MetatypeType::get(existential->getInstanceType());

printWithParensIfNotSimple(instanceType);
return type;
}));
} else if (T->is<MetatypeType>() && instanceType->is<ExistentialType>()) {
// The 'any' keyword is needed to distinguish between existential
// metatypes and singleton metatypes. However, 'any' usually isn't
// printed for Any and AnyObject, because it's unnecessary to write
// 'any' with these specific constraints. Force printing with 'any'
// for the existential instance type in this case.
instanceType->getAs<ExistentialType>()->forcePrintWithAny([&](Type ty) {
printWithParensIfNotSimple(ty);
});
} else {
printWithParensIfNotSimple(instanceType);
}

// We spell normal metatypes of existential types as .Protocol.
if (isa<MetatypeType>(T) &&
Expand Down Expand Up @@ -6217,15 +6216,15 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
}

void visitExistentialType(ExistentialType *T) {
if (Options.PrintExplicitAny)
if (Options.PrintExplicitAny && T->shouldPrintWithAny())
Printer << "any ";

// FIXME: The desugared type is used here only to support
// existential types with protocol typealiases in Swift
// interfaces. Verifying that the underlying type of a
// protocol typealias is a constriant type is fundamentally
// circular, so the desugared type should be written in source.
if (Options.DesugarExistentialConstraint) {
if (Options.DesugarExistentialConstraint && !T->isAnyObject()) {
visit(T->getConstraintType()->getDesugaredType());
} else {
visit(T->getConstraintType());
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/Builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ namespace {
if (wantsAdditionalAnyObjectRequirement) {
Requirement req(RequirementKind::Conformance,
TheGenericParamList->getParams()[0]->getInterfaceType(),
ctx.getAnyObjectType());
ctx.getAnyObjectConstraint());
addedRequirements.push_back(req);
}
for (auto gp : TheGenericParamList->getParams()) {
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2847,7 +2847,7 @@ static Type mapSignatureFunctionType(ASTContext &ctx, Type type,
// Functions and subscripts cannot overload differing only in opaque return
// types. Replace the opaque type with `Any`.
if (type->is<OpaqueTypeArchetypeType>()) {
type = ProtocolCompositionType::get(ctx, {}, /*hasAnyObject*/ false);
type = ctx.getAnyExistentialType();
}

return mapSignatureParamType(ctx, type);
Expand Down
5 changes: 4 additions & 1 deletion lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,10 @@ bool TypeBase::isStructurallyUninhabited() {
}

bool TypeBase::isAny() {
return isEqual(getASTContext().TheAnyType);
Type constraint = this;
if (auto existential = constraint->getAs<ExistentialType>())
constraint = existential->getConstraintType();
return constraint->isEqual(getASTContext().TheAnyType);
}

bool TypeBase::isPlaceholder() {
Expand Down
28 changes: 16 additions & 12 deletions lib/AST/TypeJoinMeet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ struct TypeJoin : CanTypeVisitor<TypeJoin, CanType> {
// For convenience, TheAnyType from ASTContext;
CanType TheAnyType;

CanType getAnyExistentialType() {
return ExistentialType::get(TheAnyType)->getCanonicalType();
}

TypeJoin(CanType First) : First(First), Unimplemented(CanType()) {
assert(First && "Unexpected null type!");
TheAnyType = First->getASTContext().TheAnyType;
Expand Down Expand Up @@ -112,10 +116,10 @@ struct TypeJoin : CanTypeVisitor<TypeJoin, CanType> {

// Likewise, rather than making every visitor deal with Any,
// always dispatch to the protocol composition side of the join.
if (first->is<ProtocolCompositionType>())
if (first->is<ProtocolCompositionType>() || first->is<ExistentialType>())
return TypeJoin(second).visit(first);

if (second->is<ProtocolCompositionType>())
if (second->is<ProtocolCompositionType>() || second->is<ExistentialType>())
return TypeJoin(first).visit(second);

// Otherwise the first type might be an optional (or not), so
Expand Down Expand Up @@ -166,7 +170,7 @@ CanType TypeJoin::visitErrorType(CanType second) {
CanType TypeJoin::visitTupleType(CanType second) {
assert(First != second);

return TheAnyType;
return getAnyExistentialType();
}

CanType TypeJoin::visitEnumType(CanType second) {
Expand All @@ -184,7 +188,7 @@ CanType TypeJoin::visitStructType(CanType second) {

// FIXME: When possible we should return a protocol or protocol
// composition.
return TheAnyType;
return getAnyExistentialType();
}

CanType TypeJoin::visitClassType(CanType second) {
Expand Down Expand Up @@ -240,7 +244,7 @@ CanType TypeJoin::visitMetatypeType(CanType second) {
assert(First != second);

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

auto firstInstance =
First->castTo<AnyMetatypeType>()->getInstanceType()->getCanonicalType();
Expand All @@ -258,7 +262,7 @@ CanType TypeJoin::visitExistentialMetatypeType(CanType second) {
assert(First != second);

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

auto firstInstance =
First->castTo<AnyMetatypeType>()->getInstanceType()->getCanonicalType();
Expand All @@ -276,7 +280,7 @@ CanType TypeJoin::visitExistentialType(CanType second) {
assert(First != second);

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

auto firstConstraint = First->castTo<ExistentialType>()
->getConstraintType()->getCanonicalType();
Expand All @@ -293,7 +297,7 @@ CanType TypeJoin::visitExistentialType(CanType second) {
CanType TypeJoin::visitModuleType(CanType second) {
assert(First != second);

return TheAnyType;
return getAnyExistentialType();
}

CanType TypeJoin::visitDynamicSelfType(CanType second) {
Expand All @@ -312,7 +316,7 @@ CanType TypeJoin::visitDependentMemberType(CanType second) {
assert(First != second);

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

return Unimplemented;
}
Expand All @@ -326,7 +330,7 @@ CanType TypeJoin::visitFunctionType(CanType second) {
if (secondFnTy->getExtInfo().isNoEscape()) {
return Nonexistent;
} else {
return TheAnyType;
return getAnyExistentialType();
}
}

Expand Down Expand Up @@ -362,7 +366,7 @@ CanType TypeJoin::visitGenericFunctionType(CanType second) {
assert(First != second);

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

return Unimplemented;
}
Expand Down Expand Up @@ -482,7 +486,7 @@ CanType TypeJoin::visitBuiltinType(CanType second) {
assert(First != second);

// BuiltinType with any non-equal type results in Any.
return TheAnyType;
return getAnyExistentialType();
}

} // namespace
Expand Down
8 changes: 4 additions & 4 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5414,7 +5414,7 @@ static ValueDecl *addThunkForDependentTypes(FuncDecl *oldDecl,
// If the un-specialized function had a parameter with type "Any" preserve
// that parameter. Otherwise, use the new function parameter.
auto oldParamType = oldDecl->getParameters()->get(parameterIndex)->getType();
if (oldParamType->isEqual(newDecl->getASTContext().TheAnyType)) {
if (oldParamType->isEqual(newDecl->getASTContext().getAnyExistentialType())) {
updatedAnyParams = true;
auto newParam =
ParamDecl::cloneWithoutType(newDecl->getASTContext(), newFnParam);
Expand All @@ -5429,16 +5429,16 @@ static ValueDecl *addThunkForDependentTypes(FuncDecl *oldDecl,
// If we don't need this thunk, bail out.
if (!updatedAnyParams &&
!oldDecl->getResultInterfaceType()->isEqual(
oldDecl->getASTContext().TheAnyType))
oldDecl->getASTContext().getAnyExistentialType()))
return newDecl;

auto fixedParams =
ParameterList::create(newDecl->getASTContext(), fixedParameters);

Type fixedResultType;
if (oldDecl->getResultInterfaceType()->isEqual(
oldDecl->getASTContext().TheAnyType))
fixedResultType = oldDecl->getASTContext().TheAnyType;
oldDecl->getASTContext().getAnyExistentialType()))
fixedResultType = oldDecl->getASTContext().getAnyExistentialType();
else
fixedResultType = newDecl->getResultInterfaceType();

Expand Down
2 changes: 1 addition & 1 deletion lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8161,7 +8161,7 @@ Optional<GenericParamList *> SwiftDeclConverter::importObjCGenericParams(
}
if (inherited.empty()) {
inherited.push_back(
TypeLoc::withoutLoc(Impl.SwiftContext.getAnyObjectType()));
TypeLoc::withoutLoc(Impl.SwiftContext.getAnyObjectConstraint()));
}
genericParamDecl->setInherited(Impl.SwiftContext.AllocateCopy(inherited));

Expand Down
Loading