Skip to content

More accurate ArchetypeType::getExistentialType() #61942

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
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
2 changes: 1 addition & 1 deletion include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4721,7 +4721,7 @@ class ProtocolDecl final : public NominalTypeDecl {
AssociatedTypeDecl *getAssociatedType(Identifier name) const;

/// Returns the existential type for this protocol.
Type getExistentialType() const {
Type getDeclaredExistentialType() const {
return ExistentialType::get(getDeclaredInterfaceType());
}

Expand Down
7 changes: 7 additions & 0 deletions include/swift/AST/GenericSignature.h
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,13 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final
/// will return an instance of \c ExistentialType.
Type getNonDependentUpperBounds(Type type) const;

/// Given a type parameter, compute the most specific supertype (upper bound)
/// that is possibly dependent on other type parameters.
///
/// \note If the upper bound is a protocol or protocol composition,
/// will return an instance of \c ExistentialType.
Type getDependentUpperBounds(Type type) const;

static void Profile(llvm::FoldingSetNodeID &ID,
TypeArrayView<GenericTypeParamType> genericParams,
ArrayRef<Requirement> requirements);
Expand Down
11 changes: 0 additions & 11 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -6689,17 +6689,6 @@ inline bool TypeBase::isOpenedExistential() const {
return isa<OpenedArchetypeType>(T);
}

inline bool TypeBase::isOpenedExistentialWithError() {
if (!hasOpenedExistential())
return false;

CanType T = getCanonicalType();
if (auto archetype = dyn_cast<OpenedArchetypeType>(T)) {
return archetype->getExistentialType()->isExistentialWithError();
}
return false;
}

inline bool TypeBase::canDynamicallyBeOptionalType(bool includeExistential) {
CanType T = getCanonicalType();
auto isArchetypeOrExistential = isa<ArchetypeType>(T) ||
Expand Down
6 changes: 3 additions & 3 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -930,8 +930,8 @@ Type ASTContext::get##NAME##Type() const { \
#include "swift/AST/KnownStdlibTypes.def"

CanType ASTContext::getErrorExistentialType() const {
if (auto exn = getErrorDecl()) {
return exn->getExistentialType()->getCanonicalType();
if (auto *errorProto = getErrorDecl()) {
return errorProto->getDeclaredExistentialType()->getCanonicalType();
} else {
// Use Builtin.NativeObject just as a stand-in.
return TheNativeObjectType;
Expand Down Expand Up @@ -2605,7 +2605,7 @@ ASTContext::getSelfConformance(ProtocolDecl *protocol) {
auto &entry = selfConformances[protocol];
if (!entry) {
entry = new (*this, AllocationArena::Permanent)
SelfProtocolConformance(protocol->getExistentialType());
SelfProtocolConformance(protocol->getDeclaredExistentialType());
}
return entry;
}
Expand Down
7 changes: 7 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6473,6 +6473,13 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
if (auto existential = constraint->getAs<ExistentialType>())
constraint = existential->getConstraintType();

// Opaque archetype substitutions are always canonical, so re-sugar the
// constraint type using the owning declaration's generic parameter names.
auto genericSig = T->getDecl()->getNamingDecl()->getInnermostDeclContext()
->getGenericSignatureOfContext();
if (genericSig)
constraint = genericSig->getSugaredType(constraint);

visit(constraint);
return;
}
Expand Down
117 changes: 107 additions & 10 deletions lib/AST/GenericSignature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,8 @@ unsigned GenericSignatureImpl::getGenericParamOrdinal(
Type GenericSignatureImpl::getNonDependentUpperBounds(Type type) const {
assert(type->isTypeParameter());

bool hasExplicitAnyObject = requiresClass(type);

llvm::SmallVector<Type, 2> types;
if (Type superclass = getSuperclassBound(type)) {
// If the class contains a type parameter, try looking for a non-dependent
Expand All @@ -606,24 +608,119 @@ Type GenericSignatureImpl::getNonDependentUpperBounds(Type type) const {
superclass = superclass->getSuperclass();
}

if (superclass)
if (superclass) {
types.push_back(superclass);
hasExplicitAnyObject = false;
}
}
for (const auto &elt : getRequiredProtocols(type)) {
types.push_back(elt->getDeclaredInterfaceType());
for (auto *proto : getRequiredProtocols(type)) {
if (proto->requiresClass())
hasExplicitAnyObject = false;

types.push_back(proto->getDeclaredInterfaceType());
}

const auto layout = getLayoutConstraint(type);
const auto boundsTy = ProtocolCompositionType::get(
auto constraint = ProtocolCompositionType::get(
getASTContext(), types,
/*HasExplicitAnyObject=*/layout &&
layout->getKind() == LayoutConstraintKind::Class);
hasExplicitAnyObject);

if (!constraint->isConstraintType()) {
assert(constraint->getClassOrBoundGenericClass());
return constraint;
}

return ExistentialType::get(constraint);
}

Type GenericSignatureImpl::getDependentUpperBounds(Type type) const {
assert(type->isTypeParameter());

llvm::SmallVector<Type, 2> types;

auto &ctx = type->getASTContext();

bool hasExplicitAnyObject = requiresClass(type);

// FIXME: If the superclass bound is implied by one of our protocols, we
// shouldn't add it to the constraint type.
if (Type superclass = getSuperclassBound(type)) {
types.push_back(superclass);
hasExplicitAnyObject = false;
}

for (auto proto : getRequiredProtocols(type)) {
if (proto->requiresClass())
hasExplicitAnyObject = false;

auto *baseType = proto->getDeclaredInterfaceType()->castTo<ProtocolType>();

auto primaryAssocTypes = proto->getPrimaryAssociatedTypes();
if (!primaryAssocTypes.empty()) {
SmallVector<Type, 2> argTypes;

// Attempt to recover same-type requirements on primary associated types.
for (auto *assocType : primaryAssocTypes) {
// For each primary associated type A of P, compute the reduced type
// of T.[P]A.
auto *memberType = DependentMemberType::get(type, assocType);
auto reducedType = getReducedType(memberType);

// If the reduced type is at a lower depth than the root generic
// parameter of T, then it's constrained.
bool hasOuterGenericParam = false;
bool hasInnerGenericParam = false;
reducedType.visit([&](Type t) {
if (auto *paramTy = t->getAs<GenericTypeParamType>()) {
unsigned rootDepth = type->getRootGenericParam()->getDepth();
if (paramTy->getDepth() == rootDepth)
hasInnerGenericParam = true;
else {
assert(paramTy->getDepth() < rootDepth);
hasOuterGenericParam = true;
}
}
});

if (hasInnerGenericParam && hasOuterGenericParam) {
llvm::errs() << "Weird same-type requirements?\n";
llvm::errs() << "Interface type: " << type << "\n";
llvm::errs() << "Member type: " << memberType << "\n";
llvm::errs() << "Reduced member type: " << reducedType << "\n";
llvm::errs() << GenericSignature(this) << "\n";
abort();
}

if (!hasInnerGenericParam)
argTypes.push_back(reducedType);
}

// We should have either constrained all primary associated types,
// or none of them.
if (!argTypes.empty()) {
if (argTypes.size() != primaryAssocTypes.size()) {
llvm::errs() << "Not all primary associated types constrained?\n";
llvm::errs() << "Interface type: " << type << "\n";
llvm::errs() << GenericSignature(this) << "\n";
abort();
}

types.push_back(ParameterizedProtocolType::get(ctx, baseType, argTypes));
continue;
}
}

types.push_back(baseType);
}

auto constraint = ProtocolCompositionType::get(
ctx, types, hasExplicitAnyObject);

if (boundsTy->isExistentialType()) {
return ExistentialType::get(boundsTy);
if (!constraint->isConstraintType()) {
assert(constraint->getClassOrBoundGenericClass());
return constraint;
}

return boundsTy;
return ExistentialType::get(constraint);
}

void GenericSignature::Profile(llvm::FoldingSetNodeID &id) const {
Expand Down
41 changes: 21 additions & 20 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,19 @@ bool TypeBase::isExistentialWithError() {
return layout.isExistentialWithError(getASTContext());
}

bool TypeBase::isOpenedExistentialWithError() {
if (auto archetype = getAs<OpenedArchetypeType>()) {
auto errorProto = getASTContext().getErrorDecl();
if (!errorProto) return false;

for (auto protoDecl : archetype->getConformsTo()) {
if (protoDecl == errorProto || protoDecl->inheritsFrom(errorProto))
return true;
}
}
return false;
}

bool TypeBase::isStdlibType() {
if (auto *NTD = getAnyNominal()) {
auto *DC = NTD->getDeclContext();
Expand Down Expand Up @@ -3493,34 +3506,22 @@ bool ArchetypeType::isRoot() const {
}

Type ArchetypeType::getExistentialType() const {
auto *genericEnv = getGenericEnvironment();

// Opened types hold this directly.
if (auto *opened = dyn_cast<OpenedArchetypeType>(this)) {
if (opened->isRoot()) {
return getGenericEnvironment()->getOpenedExistentialType();
return genericEnv->getOpenedExistentialType();
}
}

// Otherwise, compute it from scratch.
SmallVector<Type, 4> constraintTypes;

if (auto super = getSuperclass()) {
constraintTypes.push_back(super);
}
for (auto proto : getConformsTo()) {
constraintTypes.push_back(proto->getDeclaredInterfaceType());
}
auto &ctx = const_cast<ArchetypeType*>(this)->getASTContext();
auto constraint = ProtocolCompositionType::get(
ctx, constraintTypes, requiresClass());
// Otherwise we compute it via a generic signature query.
auto interfaceType = getInterfaceType();
auto genericSig = genericEnv->getGenericSignature();

// If the archetype is only constrained to a class type,
// return the class type directly.
if (!constraint->isConstraintType()) {
assert(constraint->getClassOrBoundGenericClass());
return constraint;
}
auto upperBound = genericSig->getDependentUpperBounds(interfaceType);

return ExistentialType::get(constraint);
return genericEnv->mapTypeIntoContext(upperBound);
}

bool ArchetypeType::requiresClass() const {
Expand Down
3 changes: 2 additions & 1 deletion lib/SILGen/SILGenType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,8 @@ static SILFunction *emitSelfConformanceWitness(SILGenModule &SGM,

// Open the protocol type.
auto openedType = OpenedArchetypeType::get(
protocol->getExistentialType()->getCanonicalType(), GenericSignature());
protocol->getDeclaredExistentialType()->getCanonicalType(),
GenericSignature());

// Form the substitutions for calling the witness.
auto witnessSubs = SubstitutionMap::getProtocolSubstitutions(protocol,
Expand Down
3 changes: 2 additions & 1 deletion lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ static bool isOpenedAnyObject(Type type) {
if (!archetype || !archetype->isRoot())
return false;

return archetype->getExistentialType()->isAnyObject();
return (archetype->requiresClass() &&
!archetype->hasRequirements());
}

SubstitutionMap
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8118,7 +8118,7 @@ static bool isCastToExpressibleByNilLiteral(ConstraintSystem &cs, Type fromType,
if (!nilLiteral)
return false;

return toType->isEqual(nilLiteral->getExistentialType()) &&
return toType->isEqual(nilLiteral->getDeclaredExistentialType()) &&
fromType->getOptionalObjectType();
}

Expand Down
24 changes: 13 additions & 11 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -647,8 +647,6 @@ bool TypeChecker::checkContextualRequirements(GenericTypeDecl *decl,
llvm_unreachable("invalid requirement check type");
}

static void diagnoseUnboundGenericType(Type ty, SourceLoc loc);

/// Apply generic arguments to the given type.
///
/// If the type is itself not generic, this does nothing.
Expand Down Expand Up @@ -756,10 +754,11 @@ static Type applyGenericArguments(Type type, TypeResolution resolution,
// Build ParameterizedProtocolType if the protocol has a primary associated
// type and we're in a supported context (for now just generic requirements,
// inheritance clause, extension binding).
if (resolution.getOptions().isConstraintImplicitExistential() && !ctx.LangOpts.hasFeature(Feature::ImplicitSome)) {
if (resolution.getOptions().isConstraintImplicitExistential() &&
!ctx.LangOpts.hasFeature(Feature::ImplicitSome)) {
diags.diagnose(loc, diag::existential_requires_any,
protoDecl->getDeclaredInterfaceType(),
protoDecl->getExistentialType(),
protoDecl->getDeclaredExistentialType(),
/*isAlias=*/isa<TypeAliasType>(type.getPointer()));

return ErrorType::get(ctx);
Expand Down Expand Up @@ -1700,7 +1699,7 @@ static Type resolveNestedIdentTypeComponent(TypeResolution resolution,
auto DC = resolution.getDeclContext();
auto &ctx = DC->getASTContext();
auto &diags = ctx.Diags;
auto isExtenstionBinding = options.is(TypeResolverContext::ExtensionBinding);
auto isExtensionBinding = options.is(TypeResolverContext::ExtensionBinding);

auto maybeDiagnoseBadMemberType = [&](TypeDecl *member, Type memberType,
AssociatedTypeDecl *inferredAssocType) {
Expand All @@ -1715,13 +1714,13 @@ static Type resolveNestedIdentTypeComponent(TypeResolution resolution,

if (options.contains(TypeResolutionFlags::SilenceErrors)) {
if (TypeChecker::isUnsupportedMemberTypeAccess(
parentTy, member, hasUnboundOpener, isExtenstionBinding) !=
parentTy, member, hasUnboundOpener, isExtensionBinding) !=
TypeChecker::UnsupportedMemberTypeAccessKind::None)
return ErrorType::get(ctx);
}

switch (TypeChecker::isUnsupportedMemberTypeAccess(
parentTy, member, hasUnboundOpener, isExtenstionBinding)) {
parentTy, member, hasUnboundOpener, isExtensionBinding)) {
case TypeChecker::UnsupportedMemberTypeAccessKind::None:
break;

Expand Down Expand Up @@ -1910,7 +1909,9 @@ static Type applyNonEscapingIfNecessary(Type ty,

// We lost the sugar to flip the isNoEscape bit.
//
// FIXME(https://github.com/apple/swift/issues/45125): It would be better to add a new AttributedType sugared type, which would wrap the TypeAliasType or ParenType, and apply the isNoEscape bit when de-sugaring.
// FIXME(https://github.com/apple/swift/issues/45125): It would be better
// to add a new AttributedType sugared type, which would wrap the
// TypeAliasType and apply the isNoEscape bit when de-sugaring.
return FunctionType::get(funcTy->getParams(), funcTy->getResult(), extInfo);
}

Expand Down Expand Up @@ -2340,10 +2341,11 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr,
return ty;
};

if(getASTContext().LangOpts.hasFeature(Feature::ImplicitSome)){
if(getASTContext().LangOpts.hasFeature(Feature::ImplicitSome)) {
if (auto opaqueDecl = dyn_cast<OpaqueTypeDecl>(DC)) {
if (auto ordinal = opaqueDecl->getAnonymousOpaqueParamOrdinal(repr))
return diagnoseDisallowedExistential(getIdentityOpaqueTypeArchetypeType(opaqueDecl, *ordinal));
return diagnoseDisallowedExistential(
getIdentityOpaqueTypeArchetypeType(opaqueDecl, *ordinal));
}
}

Expand Down Expand Up @@ -4790,7 +4792,7 @@ class ExistentialTypeVisitor
Ctx.Diags.diagnose(comp->getNameLoc(),
diag::existential_requires_any,
proto->getDeclaredInterfaceType(),
proto->getExistentialType(),
proto->getDeclaredExistentialType(),
/*isAlias=*/false)
.fixItReplace(replaceRepr->getSourceRange(), fix);
}
Expand Down
Loading