Skip to content

Protocol conformance: store conformances needed for the requirement signature #7853

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
15 changes: 15 additions & 0 deletions include/swift/AST/ProtocolConformance.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,10 @@ class NormalProtocolConformance : public ProtocolConformance,
/// the requirements of those protocols.
InheritedConformanceMap InheritedMapping;

/// Conformances that satisfy each of conformance requirements of the
/// requirement signature of the protocol.
ArrayRef<ProtocolConformanceRef> SignatureConformances;

LazyMemberLoader *Resolver = nullptr;
uint64_t ResolverContextData;

Expand Down Expand Up @@ -481,6 +485,17 @@ class NormalProtocolConformance : public ProtocolConformance,
InheritedMapping[proto] = conformance;
}

/// Retrieve the protocol conformances that satisfy the requirements of the
/// protocol, which line up with the conformance constraints in the
/// protocol's requirement signature.
ArrayRef<ProtocolConformanceRef> getSignatureConformances() const {
return SignatureConformances;
}

/// Copy the given protocol conformances for the requirement signature into
/// the normal conformance.
void setSignatureConformances(ArrayRef<ProtocolConformanceRef> conformances);

/// Determine whether the witness for the given type requirement
/// is the default definition.
bool usesDefaultDefinition(AssociatedTypeDecl *requirement) const {
Expand Down
3 changes: 2 additions & 1 deletion include/swift/AST/ProtocolConformanceRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@
#include "llvm/ADT/PointerUnion.h"
#include "swift/AST/TypeAlignments.h"
#include "swift/AST/Type.h"
#include "swift/AST/ProtocolConformance.h"

namespace llvm {
class raw_ostream;
}

namespace swift {

class ProtocolConformance;

/// A ProtocolConformanceRef is a handle to a protocol conformance which
/// may be either concrete or abstract.
///
Expand Down
2 changes: 1 addition & 1 deletion include/swift/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const uint16_t VERSION_MAJOR = 0;
/// in source control, you should also update the comment to briefly
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
const uint16_t VERSION_MINOR = 322; // Last change: end_lifetime
const uint16_t VERSION_MINOR = 323; // Last change: requirement conformances

using DeclID = PointerEmbeddedInt<unsigned, 31>;
using DeclIDField = BCFixed<31>;
Expand Down
31 changes: 31 additions & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "swift/AST/LazyResolver.h"
#include "swift/AST/ModuleLoader.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/RawComment.h"
#include "swift/AST/SILLayout.h"
#include "swift/AST/TypeCheckerDebugConsumer.h"
Expand Down Expand Up @@ -3606,6 +3607,36 @@ static NominalTypeDecl *findUnderlyingTypeInModule(ASTContext &ctx,
return nullptr;
}

bool ForeignRepresentationInfo::isRepresentableAsOptional() const {
switch (getKind()) {
case ForeignRepresentableKind::None:
llvm_unreachable("this type is not representable");

case ForeignRepresentableKind::Trivial:
return Storage.getPointer() != 0;

case ForeignRepresentableKind::Bridged: {
auto KPK_ObjectiveCBridgeable = KnownProtocolKind::ObjectiveCBridgeable;
ProtocolDecl *proto = getConformance()->getProtocol();
assert(proto->isSpecificProtocol(KPK_ObjectiveCBridgeable) &&
"unknown protocol; does it support optional?");
(void)proto;
(void)KPK_ObjectiveCBridgeable;

return true;
}

case ForeignRepresentableKind::BridgedError:
return true;

case ForeignRepresentableKind::Object:
case ForeignRepresentableKind::StaticBridged:
llvm_unreachable("unexpected kind in ForeignRepresentableCacheEntry");
}

llvm_unreachable("Unhandled ForeignRepresentableKind in switch.");
}

ForeignRepresentationInfo
ASTContext::getForeignRepresentationInfo(NominalTypeDecl *nominal,
ForeignLanguage language,
Expand Down
10 changes: 9 additions & 1 deletion lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2615,10 +2615,18 @@ void ProtocolConformance::dump(llvm::raw_ostream &out, unsigned indent) const {
};

switch (getKind()) {
case ProtocolConformanceKind::Normal:
case ProtocolConformanceKind::Normal: {
auto normal = cast<NormalProtocolConformance>(this);

printCommon("normal");
// Maybe print information about the conforming context?

for (auto conformance : normal->getSignatureConformances()) {
out << '\n';
conformance.dump(out, indent + 2);
}
break;
}

case ProtocolConformanceKind::Inherited: {
auto conf = cast<InheritedProtocolConformance>(this);
Expand Down
34 changes: 34 additions & 0 deletions lib/AST/ASTVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2025,6 +2025,40 @@ struct ASTNodeBase {};
continue;
}
}

// Make sure we have the right signature conformances.
if (!normal->isInvalid()){
auto conformances = normal->getSignatureConformances();
unsigned idx = 0;
for (auto req : proto->getRequirementSignature()->getRequirements()) {
if (req.getKind() != RequirementKind::Conformance)
continue;

if (idx >= conformances.size()) {
Out << "error: not enough conformances for requirement signature\n";
normal->dump(Out);
abort();
}

auto reqProto =
req.getSecondType()->castTo<ProtocolType>()->getDecl();
if (reqProto != conformances[idx].getRequirement()) {
Out << "error: wrong protocol in signature conformances: have "
<< conformances[idx].getRequirement()->getName().str()
<< ", expected " << reqProto->getName().str()<< "\n";
normal->dump(Out);
abort();
}

++idx;
}

if (idx != conformances.size()) {
Out << "error: too many conformances for requirement signature\n";
normal->dump(Out);
abort();
}
}
}

void verifyGenericEnvironment(Decl *D,
Expand Down
33 changes: 3 additions & 30 deletions lib/AST/ForeignRepresentationInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@
#ifndef SWIFT_FOREIGNREPRESENTATIONINFO_H
#define SWIFT_FOREIGNREPRESENTATIONINFO_H

#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/Type.h"
#include "swift/Basic/LLVM.h"
#include "llvm/ADT/PointerEmbeddedInt.h"
#include "llvm/ADT/PointerIntPair.h"

namespace swift {

class ProtocolConformance;

class ForeignRepresentationInfo {
using PayloadTy =
llvm::PointerEmbeddedInt<uintptr_t, sizeof(uintptr_t) * CHAR_BIT - 3>;
Expand Down Expand Up @@ -112,35 +113,7 @@ class ForeignRepresentationInfo {
}

/// Returns true if the optional version of this type is also representable.
bool isRepresentableAsOptional() const {
switch (getKind()) {
case ForeignRepresentableKind::None:
llvm_unreachable("this type is not representable");

case ForeignRepresentableKind::Trivial:
return Storage.getPointer() != 0;

case ForeignRepresentableKind::Bridged: {
auto KPK_ObjectiveCBridgeable = KnownProtocolKind::ObjectiveCBridgeable;
ProtocolDecl *proto = getConformance()->getProtocol();
assert(proto->isSpecificProtocol(KPK_ObjectiveCBridgeable) &&
"unknown protocol; does it support optional?");
(void)proto;
(void)KPK_ObjectiveCBridgeable;

return true;
}

case ForeignRepresentableKind::BridgedError:
return true;

case ForeignRepresentableKind::Object:
case ForeignRepresentableKind::StaticBridged:
llvm_unreachable("unexpected kind in ForeignRepresentableCacheEntry");
}

llvm_unreachable("Unhandled ForeignRepresentableKind in switch.");
}
bool isRepresentableAsOptional() const;
};

} // end namespace swift
Expand Down
19 changes: 19 additions & 0 deletions lib/AST/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,25 @@ AbstractStorageDecl *ProtocolConformance::getBehaviorDecl() const {
return getRootNormalConformance()->getBehaviorDecl();
}

void NormalProtocolConformance::setSignatureConformances(
ArrayRef<ProtocolConformanceRef> conformances) {
auto &ctx = getProtocol()->getASTContext();
SignatureConformances = ctx.AllocateCopy(conformances);

#if !NDEBUG
unsigned idx = 0;
for (auto req : getProtocol()->getRequirementSignature()->getRequirements()) {
if (req.getKind() == RequirementKind::Conformance) {
assert(idx < conformances.size());
assert(conformances[idx].getRequirement() ==
req.getSecondType()->castTo<ProtocolType>()->getDecl());
++idx;
}
}
assert(idx == conformances.size() && "Too many conformances");
#endif
}

void NormalProtocolConformance::resolveLazyInfo() const {
assert(Resolver);
assert(isComplete());
Expand Down
1 change: 1 addition & 0 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/LazyResolver.h"
#include "swift/AST/Module.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/SubstitutionMap.h"
#include "swift/AST/TypeLoc.h"
#include "llvm/ADT/APFloat.h"
Expand Down
19 changes: 19 additions & 0 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6952,6 +6952,7 @@ void ClangImporter::Implementation::finishProtocolConformance(
};
return getDeclName(*left).compare(getDeclName(*right));
});

// Schedule any that aren't complete.
for (auto *inherited : inheritedProtos) {
ModuleDecl *M = conformance->getDeclContext()->getParentModule();
Expand All @@ -6964,6 +6965,24 @@ void ClangImporter::Implementation::finishProtocolConformance(
inheritedConformance->getConcrete());
}

// Collect conformances for the requirement signature.
SmallVector<ProtocolConformanceRef, 4> reqConformances;
for (auto req : proto->getRequirementSignature()->getRequirements()) {
if (req.getKind() != RequirementKind::Conformance)
continue;

assert(req.getFirstType()->isEqual(proto->getSelfInterfaceType()));
auto reqProto = req.getSecondType()->castTo<ProtocolType>()->getDecl();

ModuleDecl *M = conformance->getDeclContext()->getParentModule();
auto reqConformance = M->lookupConformance(conformance->getType(),
reqProto, /*resolver=*/nullptr);
assert(reqConformance && reqConformance->isConcrete() &&
"required conformance not found");
reqConformances.push_back(*reqConformance);
}
conformance->setSignatureConformances(reqConformances);

conformance->setState(ProtocolConformanceState::Complete);
}

Expand Down
1 change: 1 addition & 0 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "ConstraintSystem.h"
#include "swift/AST/ASTVisitor.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/SubstitutionMap.h"
#include "swift/Basic/StringExtras.h"
#include "llvm/ADT/APFloat.h"
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/CSRanking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
//
//===----------------------------------------------------------------------===//
#include "ConstraintSystem.h"
#include "swift/AST/ProtocolConformance.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Compiler.h"

Expand Down
1 change: 1 addition & 0 deletions lib/Sema/CodeSynthesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "swift/AST/Expr.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/Basic/Defer.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
Expand Down
1 change: 0 additions & 1 deletion lib/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
#include "swift/AST/ASTVisitor.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/Types.h"
#include "swift/AST/TypeCheckerDebugConsumer.h"
#include "llvm/ADT/ilist.h"
Expand Down
5 changes: 5 additions & 0 deletions lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1388,6 +1388,11 @@ bool GenericRequirementsCheckListener::shouldCheck(RequirementKind kind,
return true;
}

void GenericRequirementsCheckListener::satisfiedConformance(
Type depTy, Type replacementTy,
ProtocolConformanceRef conformance) {
}

bool TypeChecker::
solveForExpression(Expr *&expr, DeclContext *dc, Type convertType,
FreeTypeVariableBinding allowFreeTypeVariables,
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "swift/AST/GenericSignatureBuilder.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/PrettyStackTrace.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/ReferencedNameTracker.h"
#include "swift/AST/TypeWalker.h"
#include "swift/Basic/Statistic.h"
Expand Down
15 changes: 13 additions & 2 deletions lib/Sema/TypeCheckGeneric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1124,10 +1124,13 @@ std::pair<bool, bool> TypeChecker::checkGenericArguments(
UnsatisfiedDependency *unsatisfiedDependency,
ConformanceCheckOptions conformanceOptions,
GenericRequirementsCheckListener *listener) {
bool valid = true;

for (const auto &rawReq : genericSig->getRequirements()) {
auto req = rawReq.subst(substitutions, conformances);
if (!req) {
// Another requirement will fail later; just continue.
valid = false;
continue;
}

Expand All @@ -1138,8 +1141,10 @@ std::pair<bool, bool> TypeChecker::checkGenericArguments(
if (kind != RequirementKind::Layout) {
rawSecondType = rawReq.getSecondType();
secondType = req->getSecondType().subst(substitutions, conformances);
if (!secondType)
if (!secondType) {
valid = false;
continue;
}
}

if (listener && !listener->shouldCheck(kind, firstType, secondType))
Expand All @@ -1165,6 +1170,12 @@ std::pair<bool, bool> TypeChecker::checkGenericArguments(
if (!result.second)
return std::make_pair(false, false);

// Report the conformance.
if (listener) {
listener->satisfiedConformance(rawReq.getFirstType(), firstType,
*result.second);
}

continue;
}

Expand Down Expand Up @@ -1207,5 +1218,5 @@ std::pair<bool, bool> TypeChecker::checkGenericArguments(
}
}

return std::make_pair(false, true);
return std::make_pair(false, valid);
}
Loading