Skip to content

[WIP] Use reachable conformances #15254

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

Closed
Closed
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
1 change: 1 addition & 0 deletions include/swift/AST/KnownProtocols.def
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ PROTOCOL(Encodable)
PROTOCOL(Decodable)

PROTOCOL_(ObjectiveCBridgeable)
PROTOCOL_(ObjectiveCBridgeableError)
PROTOCOL_(DestructorSafeContainer)

EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByArrayLiteral)
Expand Down
9 changes: 0 additions & 9 deletions include/swift/AST/LazyResolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,6 @@ class LazyResolver {

/// Resolve an implicitly-generated member with the given name.
virtual void resolveImplicitMember(NominalTypeDecl *nominal, DeclName member) = 0;

/// Mark the given conformance as "used" from the given declaration context.
virtual void markConformanceUsed(ProtocolConformanceRef conformance,
DeclContext *dc) = 0;
};

/// An implementation of LazyResolver that delegates to another.
Expand Down Expand Up @@ -159,11 +155,6 @@ class DelegatingLazyResolver : public LazyResolver {
void resolveImplicitMember(NominalTypeDecl *nominal, DeclName member) override {
Principal.resolveImplicitMember(nominal, member);
}

void markConformanceUsed(ProtocolConformanceRef conformance,
DeclContext *dc) override {
return Principal.markConformanceUsed(conformance, dc);
}
};

class LazyMemberLoader;
Expand Down
7 changes: 5 additions & 2 deletions include/swift/AST/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -940,8 +940,11 @@ class SourceFile final : public FileUnit {
virtual bool walk(ASTWalker &walker) override;

/// Note that the given conformance was used by this source file.
void addUsedConformance(NormalProtocolConformance *conformance) {
UsedConformances.insert(conformance);
///
/// \returns \c true if the conformance was newly-discovered, \c false if it
/// was already known.
bool addUsedConformance(NormalProtocolConformance *conformance) {
return UsedConformances.insert(conformance);
}

/// Retrieve the set of conformances that were used in this source
Expand Down
1 change: 1 addition & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4086,6 +4086,7 @@ DependentMemberType *DependentMemberType::get(Type base, Identifier name) {

DependentMemberType *DependentMemberType::get(Type base,
AssociatedTypeDecl *assocType) {
assert(assocType && "Missing associated type");
auto properties = base->getRecursiveProperties();
properties |= RecursiveTypeProperties::HasDependentMember;
auto arena = getArena(properties);
Expand Down
8 changes: 8 additions & 0 deletions lib/AST/ConformanceLookupTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,9 +529,17 @@ void ConformanceLookupTable::expandImpliedConformances(NominalTypeDecl *nominal,
addProtocol(bridgedNSError, SourceLoc(),
ConformanceSource::forImplied(conformanceEntry));
}
if (auto bridgeableError =
ctx.getProtocol(KnownProtocolKind::ObjectiveCBridgeableError)) {
addProtocol(bridgeableError, SourceLoc(),
ConformanceSource::forImplied(conformanceEntry));
}
}

// Add inherited protocols.
if (resolver)
resolver->resolveInheritedProtocols(conformingProtocol);

addProtocols(conformingProtocol->getInherited(),
ConformanceSource::forImplied(conformanceEntry),
resolver);
Expand Down
73 changes: 28 additions & 45 deletions lib/AST/GenericSignatureBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -912,18 +912,15 @@ bool RequirementSource::isSelfDerivedSource(GenericSignatureBuilder &builder,
}

/// Replace 'Self' in the given dependent type (\c depTy) with the given
/// potential archetype, producing a new potential archetype that refers to
/// dependent type, producing a type that refers to
/// the nested type. This limited operation makes sure that it does not
/// create any new potential archetypes along the way, so it should only be
/// used in cases where we're reconstructing something that we know exists.
static Type replaceSelfWithType(Type selfType, Type depTy) {
if (auto depMemTy = depTy->getAs<DependentMemberType>()) {
Type baseType = replaceSelfWithType(selfType, depMemTy->getBase());

if (auto assocType = depMemTy->getAssocType())
return DependentMemberType::get(baseType, assocType);

return DependentMemberType::get(baseType, depMemTy->getName());
assert(depMemTy->getAssocType() && "Missing associated type");
return DependentMemberType::get(baseType, depMemTy->getAssocType());
}

assert(depTy->is<GenericTypeParamType>() && "missing Self?");
Expand Down Expand Up @@ -1098,14 +1095,15 @@ const RequirementSource *RequirementSource::getMinimalConformanceSource(
// If we haven't seen a protocol requirement, we're done.
if (!sawProtocolRequirement) return this;

// The root archetype might be a nested type, which implies constraints
// The root might be a nested type, which implies constraints
// for each of the protocols of the associated types referenced (if any).
for (auto depMemTy = rootType->getAs<DependentMemberType>(); depMemTy;
depMemTy = depMemTy->getBase()->getAs<DependentMemberType>()) {
if (auto assocType = depMemTy->getAssocType()) {
if (addTypeConstraint(depMemTy->getBase(), assocType->getProtocol(), nullptr))
return nullptr;
}
auto assocType = depMemTy->getAssocType();
assert(assocType);
if (addTypeConstraint(depMemTy->getBase(), assocType->getProtocol(),
nullptr))
return nullptr;
}

return this;
Expand Down Expand Up @@ -1634,10 +1632,8 @@ static Type formProtocolRelativeType(ProtocolDecl *proto,
auto depMemTy = type->castTo<DependentMemberType>();
Type newBaseType = formProtocolRelativeType(proto, baseType,
depMemTy->getBase());
if (auto assocType = depMemTy->getAssocType())
return DependentMemberType::get(newBaseType, assocType);

return DependentMemberType::get(newBaseType, depMemTy->getName());
auto assocType = depMemTy->getAssocType();
return DependentMemberType::get(newBaseType, assocType);
}

const RequirementSource *FloatingRequirementSource::getSource(
Expand Down Expand Up @@ -2310,15 +2306,7 @@ Type EquivalenceClass::getAnchor(
#endif
}

// FIXME: Once we are no longer constructing potential archetypes with
// concrete nested types, we can turn this into assert(updatedAnchor);
if (!updatedAnchor) {
++NumArchetypeAnchorCacheMisses;
archetypeAnchorCache.anchor =
builder.getCanonicalTypeParameter(members.front()->getDependentType({ }));
archetypeAnchorCache.lastGeneration = builder.Impl->Generation;
}

assert(updatedAnchor && "Couldn't update anchor?");
return substAnchor();
}

Expand Down Expand Up @@ -2726,8 +2714,8 @@ static void maybeAddSameTypeRequirementForNestedType(
if (auto depMemTy =
nested.getDependentType(builder)->getAs<DependentMemberType>())
assocType = depMemTy->getAssocType();

if (!assocType) return;
else
return;

// Dig out the type witness.
auto superConformance = superSource->getProtocolConformance().getConcrete();
Expand Down Expand Up @@ -2820,19 +2808,10 @@ int swift::compareDependentTypes(Type type1, Type type2) {
depMemTy2->getName().str()))
return compareNames;

if (auto *assocType1 = depMemTy1->getAssocType()) {
if (auto *assocType2 = depMemTy2->getAssocType()) {
if (int result = compareAssociatedTypes(assocType1, assocType2))
return result;
} else {
// A resolved archetype is always ordered before an unresolved one.
return -1;
}
} else {
// A resolved archetype is always ordered before an unresolved one.
if (depMemTy2->getAssocType())
return +1;
}
auto *assocType1 = depMemTy1->getAssocType();
auto *assocType2 = depMemTy2->getAssocType();
if (int result = compareAssociatedTypes(assocType1, assocType2))
return result;

return 0;
}
Expand Down Expand Up @@ -3842,12 +3821,8 @@ GenericSignatureBuilder::lookupConformance(CanType dependentType,
// FIXME: When lookupConformance() starts respecting modules, we'll need
// to do some filtering here.
ModuleDecl *searchModule = conformedProtocol->getDecl()->getParentModule();
auto result = searchModule->lookupConformance(conformingReplacementType,
conformedProtocol->getDecl());
if (result && getLazyResolver())
getLazyResolver()->markConformanceUsed(*result, searchModule);

return result;
return searchModule->lookupConformance(conformingReplacementType,
conformedProtocol->getDecl());
}

LazyResolver *GenericSignatureBuilder::getLazyResolver() const {
Expand Down Expand Up @@ -4994,8 +4969,12 @@ GenericSignatureBuilder::addSameTypeRequirementBetweenTypeParameters(

// Recursively merge the associated types of T2 into T1.
auto dependentT1 = T1->getDependentType(getGenericParams());
SmallPtrSet<Identifier, 4> visited;
for (auto equivT2 : equivClass2Members) {
for (auto T2Nested : equivT2->NestedTypes) {
// Only visit each name once.
if (!visited.insert(T2Nested.first).second) continue;

// If T1 is concrete but T2 is not, concretize the nested types of T2.
if (t1IsConcrete && !t2IsConcrete) {
concretizeNestedTypeFromConcreteParent(T1, T2Nested.second.front(),
Expand Down Expand Up @@ -5024,9 +5003,13 @@ GenericSignatureBuilder::addSameTypeRequirementBetweenTypeParameters(
}

// If T2 is concrete but T1 was not, concretize the nested types of T1.
visited.clear();
if (t2IsConcrete && !t1IsConcrete) {
for (auto equivT1 : equivClass1Members) {
for (auto T1Nested : equivT1->NestedTypes) {
// Only visit each name once.
if (!visited.insert(T1Nested.first).second) continue;

concretizeNestedTypeFromConcreteParent(T2, T1Nested.second.front(),
*this);
}
Expand Down
3 changes: 2 additions & 1 deletion lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2707,7 +2707,8 @@ namespace {

// Add inheritance clause.
addSynthesizedProtocolAttrs(Impl, errorWrapper,
{KnownProtocolKind::BridgedStoredNSError});
{KnownProtocolKind::BridgedStoredNSError,
KnownProtocolKind::ObjectiveCBridgeableError});

// Create the _nsError member.
// public let _nsError: NSError
Expand Down
1 change: 1 addition & 0 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5878,6 +5878,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) {
case KnownProtocolKind::CaseIterable:
case KnownProtocolKind::Comparable:
case KnownProtocolKind::ObjectiveCBridgeable:
case KnownProtocolKind::ObjectiveCBridgeableError:
case KnownProtocolKind::DestructorSafeContainer:
case KnownProtocolKind::SwiftNewtypeWrapper:
case KnownProtocolKind::ExpressibleByArrayLiteral:
Expand Down
51 changes: 7 additions & 44 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@ void Solution::computeSubstitutions(
return tc.conformsToProtocol(replacement,
protoType->getDecl(),
getConstraintSystem().DC,
(ConformanceCheckFlags::InExpression|
ConformanceCheckFlags::Used));
ConformanceCheckFlags::InExpression);
};

auto subMap = sig->getSubstitutionMap(
Expand Down Expand Up @@ -435,18 +434,6 @@ namespace {
const Solution &solution;
bool SuppressDiagnostics;

/// Recognize used conformances from an imported type when we must emit
/// the witness table.
///
/// This arises in _BridgedStoredNSError, where we wouldn't
/// otherwise pull in the witness table, causing dynamic casts to
/// perform incorrectly, and _ErrorCodeProtocol, where we need to
/// check for _BridgedStoredNSError conformances on the
/// corresponding ErrorType.
void checkForImportedUsedConformances(Type toType) {
cs.getTypeChecker().useBridgedNSErrorConformances(dc, toType);
}

/// \brief Coerce the given tuple to another tuple type.
///
/// \param expr The expression we're converting.
Expand Down Expand Up @@ -574,8 +561,7 @@ namespace {
auto conformance =
tc.conformsToProtocol(
baseTy, proto, cs.DC,
(ConformanceCheckFlags::InExpression|
ConformanceCheckFlags::Used));
ConformanceCheckFlags::InExpression);
if (conformance && conformance->isConcrete()) {
if (auto witness =
conformance->getConcrete()->getWitnessDecl(decl, &tc)) {
Expand Down Expand Up @@ -1770,8 +1756,7 @@ namespace {
= tc.conformsToProtocol(valueType,
bridgedProto,
cs.DC,
(ConformanceCheckFlags::InExpression|
ConformanceCheckFlags::Used));
ConformanceCheckFlags::InExpression);

FuncDecl *fn = nullptr;

Expand Down Expand Up @@ -3214,7 +3199,6 @@ namespace {
auto toType = simplifyType(cs.getType(expr->getCastTypeLoc()));
auto sub = cs.coerceToRValue(expr->getSubExpr());

checkForImportedUsedConformances(toType);
expr->setSubExpr(sub);

// Set the type we checked against.
Expand Down Expand Up @@ -3563,7 +3547,6 @@ namespace {
// Simplify the type we're casting to.
auto toType = simplifyType(cs.getType(expr->getCastTypeLoc()));
expr->getCastTypeLoc().setType(toType, /*validated=*/true);
checkForImportedUsedConformances(toType);

auto &tc = cs.getTypeChecker();

Expand Down Expand Up @@ -3649,7 +3632,6 @@ namespace {
// Simplify the type we're casting to.
auto toType = simplifyType(cs.getType(expr->getCastTypeLoc()));
expr->getCastTypeLoc().setType(toType, /*validated=*/true);
checkForImportedUsedConformances(toType);

// The subexpression is always an rvalue.
auto &tc = cs.getTypeChecker();
Expand Down Expand Up @@ -3729,7 +3711,6 @@ namespace {
bool isInsideIsExpr = false) {
// Simplify the type we're casting to.
auto toType = simplifyType(cs.getType(expr->getCastTypeLoc()));
checkForImportedUsedConformances(toType);
expr->getCastTypeLoc().setType(toType, /*validated=*/true);

// The subexpression is always an rvalue.
Expand Down Expand Up @@ -4548,12 +4529,6 @@ namespace {
Expr *walkToExprPost(Expr *expr) {
Expr *result = visit(expr);

// Mark any _ObjectiveCBridgeable conformances as 'used'.
if (result) {
auto &tc = cs.getTypeChecker();
tc.useObjectiveCBridgeableConformances(cs.DC, cs.getType(result));
}

assert(expr == ExprStack.back());
ExprStack.pop_back();

Expand Down Expand Up @@ -4615,9 +4590,7 @@ namespace {
for (auto indexType : indexTypes) {
auto conformance =
cs.TC.conformsToProtocol(indexType.getType(), hashable,
cs.DC,
(ConformanceCheckFlags::Used|
ConformanceCheckFlags::InExpression));
cs.DC, None);
if (!conformance) {
cs.TC.diagnose(component.getIndexExpr()->getLoc(),
diag::expr_keypath_subscript_index_not_hashable,
Expand Down Expand Up @@ -5409,8 +5382,7 @@ collectExistentialConformances(TypeChecker &tc, Type fromType, Type toType,
for (auto proto : layout.getProtocols()) {
conformances.push_back(
*tc.containsProtocol(fromType, proto->getDecl(), DC,
(ConformanceCheckFlags::InExpression|
ConformanceCheckFlags::Used)));
ConformanceCheckFlags::InExpression));
}

return tc.Context.AllocateCopy(conformances);
Expand Down Expand Up @@ -6423,8 +6395,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
auto conformance =
tc.conformsToProtocol(
cs.getType(expr), hashable, cs.DC,
(ConformanceCheckFlags::InExpression |
ConformanceCheckFlags::Used));
ConformanceCheckFlags::InExpression);
assert(conformance && "must conform to Hashable");

return cs.cacheType(
Expand Down Expand Up @@ -7707,11 +7678,7 @@ namespace {
}

Expr *walkToExprPost(Expr *expr) override {
Expr *result = Rewriter.walkToExprPost(expr);
auto &cs = Rewriter.getConstraintSystem();
if (result && cs.hasType(result))
Rewriter.checkForImportedUsedConformances(cs.getType(result));
return result;
return Rewriter.walkToExprPost(expr);
}

/// \brief Ignore statements.
Expand Down Expand Up @@ -7959,10 +7926,6 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr,
return nullptr;
}

// Mark any normal conformances used in this solution as "used".
for (auto &e : solution.Conformances)
TC.markConformanceUsed(e.second, DC);

ExprRewriter rewriter(*this, solution, suppressDiagnostics);
ExprWalker walker(rewriter);

Expand Down
Loading