Skip to content

Commit a1fbf9d

Browse files
authored
Merge pull request #12354 from slavapestov/try-again-this-time-with-more-self
Re-apply "Fix issue with 'Self' metadata when class conforms to protocol with default implementations"
2 parents fa5852c + a3c4882 commit a1fbf9d

File tree

13 files changed

+797
-302
lines changed

13 files changed

+797
-302
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,6 +1452,11 @@ ERROR(witness_requires_dynamic_self,none,
14521452
"method %0 in non-final class %1 must return `Self` to conform to "
14531453
"protocol %2",
14541454
(DeclName, Type, Type))
1455+
ERROR(witness_requires_class_implementation,none,
1456+
"method %0 in non-final class %1 cannot be implemented in a "
1457+
"protocol extension because it returns `Self` and has associated type "
1458+
"requirements",
1459+
(DeclName, Type))
14551460
ERROR(witness_not_accessible_proto,none,
14561461
"%select{initializer %1|method %1|%select{|setter for }2property %1"
14571462
"|subscript%select{| setter}2}0 must be declared "

include/swift/AST/Types.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3520,10 +3520,16 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode,
35203520

35213521
CanType getSelfInstanceType() const;
35223522

3523-
/// If this a @convention(witness_method) function with an abstract
3524-
/// self parameter, return the protocol constraint for the Self type.
3523+
/// If this is a @convention(witness_method) function with a protocol
3524+
/// constrained self parameter, return the protocol constraint for
3525+
/// the Self type.
35253526
ProtocolDecl *getDefaultWitnessMethodProtocol(ModuleDecl &M) const;
35263527

3528+
/// If this is a @convention(witness_method) function with a class
3529+
/// constrained self parameter, return the class constraint for the
3530+
/// Self type.
3531+
ClassDecl *getWitnessMethodClass(ModuleDecl &M) const;
3532+
35273533
ExtInfo getExtInfo() const { return ExtInfo(SILFunctionTypeBits.ExtInfo); }
35283534

35293535
/// \brief Returns the language-level calling convention of the function.

lib/IRGen/GenProto.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,18 +314,35 @@ void PolymorphicConvention::considerWitnessSelf(CanSILFunctionType fnType) {
314314
selfTy);
315315

316316
if (auto *proto = fnType->getDefaultWitnessMethodProtocol(M)) {
317-
// The Self type is abstract, so we must pass in a witness table.
317+
// The Self type is abstract, so we can fulfill its metadata from
318+
// the Self metadata parameter.
318319
addSelfMetadataFulfillment(selfTy);
319320

320-
// Look at the witness table for the conformance.
321+
// The witness table for the Self : P conformance can be
322+
// fulfilled from the Self witness table parameter.
321323
Sources.emplace_back(MetadataSource::Kind::SelfWitnessTable,
322324
MetadataSource::InvalidSourceIndex,
323325
selfTy);
324326
addSelfWitnessTableFulfillment(selfTy, proto);
327+
} else if (auto *classDecl = fnType->getWitnessMethodClass(M)) {
328+
// The Self type is abstract, so we can fulfill its metadata from
329+
// the Self metadata parameter.
330+
addSelfMetadataFulfillment(selfTy);
331+
332+
// FIXME: We should fulfill the witness table too, but we don't
333+
// have the original protocol anymore -- we should store it as part
334+
// of the @convention(witness_method) bit in the SILFunctionType's
335+
// ExtInfo
325336
} else {
326337
// If the Self type is concrete, we have a witness thunk with a
327338
// fully substituted Self type. The witness table parameter is not
328339
// used.
340+
//
341+
// FIXME: As above, we should fulfill the Self metadata and
342+
// conformance from our two special paramaters here. However, the
343+
// Self metadata will be inexact.
344+
//
345+
// For now, just fulfill the generic arguments of 'Self'.
329346
considerType(selfTy, IsInexact, Sources.size() - 1, MetadataPath());
330347
}
331348
}

lib/SIL/SILFunctionType.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,10 @@ SILFunctionType::getDefaultWitnessMethodProtocol(ModuleDecl &M) const {
100100
assert(getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod);
101101
auto selfTy = getSelfInstanceType();
102102
if (auto paramTy = dyn_cast<GenericTypeParamType>(selfTy)) {
103+
assert(paramTy->getDepth() == 0 && paramTy->getIndex() == 0);
103104
auto superclass = GenericSig->getSuperclassBound(paramTy, M);
104105
if (superclass)
105106
return nullptr;
106-
assert(paramTy->getDepth() == 0 && paramTy->getIndex() == 0);
107107
auto protos = GenericSig->getConformsTo(paramTy, M);
108108
assert(protos.size() == 1);
109109
return protos[0];
@@ -112,6 +112,20 @@ SILFunctionType::getDefaultWitnessMethodProtocol(ModuleDecl &M) const {
112112
return nullptr;
113113
}
114114

115+
ClassDecl *
116+
SILFunctionType::getWitnessMethodClass(ModuleDecl &M) const {
117+
auto selfTy = getSelfInstanceType();
118+
auto genericSig = getGenericSignature();
119+
if (auto paramTy = dyn_cast<GenericTypeParamType>(selfTy)) {
120+
assert(paramTy->getDepth() == 0 && paramTy->getIndex() == 0);
121+
auto superclass = genericSig->getSuperclassBound(paramTy, M);
122+
if (superclass)
123+
return superclass->getClassOrBoundGenericClass();
124+
}
125+
126+
return nullptr;
127+
}
128+
115129
static CanType getKnownType(Optional<CanType> &cacheSlot, ASTContext &C,
116130
StringRef moduleName, StringRef typeName) {
117131
if (!cacheSlot) {

lib/SILOptimizer/Utils/Devirtualize.cpp

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,8 @@ DevirtualizationResult swift::tryDevirtualizeClassMethod(FullApplySite AI,
712712
//===----------------------------------------------------------------------===//
713713

714714
static SubstitutionMap
715-
getSubstitutionsForProtocolConformance(ProtocolConformanceRef CRef) {
715+
getSubstitutionsForProtocolConformance(ModuleDecl *M,
716+
ProtocolConformanceRef CRef) {
716717
auto C = CRef.getConcrete();
717718

718719
// Walk down to the base NormalProtocolConformance.
@@ -746,8 +747,7 @@ getSubstitutionsForProtocolConformance(ProtocolConformanceRef CRef) {
746747

747748
if (Subs.empty()) {
748749
auto *DC = NormalC->getDeclContext();
749-
return NormalC->getType()
750-
->getContextSubstitutionMap(DC->getParentModule(), DC);
750+
return NormalC->getType()->getContextSubstitutionMap(M, DC);
751751
}
752752

753753
return NormalC->getGenericSignature()->getSubstitutionMap(Subs);
@@ -764,17 +764,50 @@ getSubstitutionsForProtocolConformance(ProtocolConformanceRef CRef) {
764764
/// are written in terms of the requirement's generic signature need
765765
/// to be remapped to substitutions suitable for the witness signature.
766766
///
767+
/// Supported remappings are:
768+
///
769+
/// - (Concrete witness thunk) Original substitutions:
770+
/// [Self := ConcreteType, R0 := X0, R1 := X1, ...]
771+
/// - Requirement generic signature:
772+
/// <Self : P, R0, R1, ...>
773+
/// - Witness thunk generic signature:
774+
/// <W0, W1, ...>
775+
/// - Remapped substitutions:
776+
/// [W0 := X0, W1 := X1, ...]
777+
///
778+
/// - (Class witness thunk) Original substitutions:
779+
/// [Self := C<A0, A1>, T0 := X0, T1 := X1, ...]
780+
/// - Requirement generic signature:
781+
/// <Self : P, R0, R1, ...>
782+
/// - Witness thunk generic signature:
783+
/// <Self : C<B0, B1>, B0, B1, W0, W1, ...>
784+
/// - Remapped substitutions:
785+
/// [Self := C<B0, B1>, B0 := A0, B1 := A1, W0 := X0, W1 := X1]
786+
///
787+
/// - (Default witness thunk) Original substitutions:
788+
/// [Self := ConcreteType, R0 := X0, R1 := X1, ...]
789+
/// - Requirement generic signature:
790+
/// <Self : P, R0, R1, ...>
791+
/// - Witness thunk generic signature:
792+
/// <Self : P, W0, W1, ...>
793+
/// - Remapped substitutions:
794+
/// [Self := ConcreteType, W0 := X0, W1 := X1, ...]
795+
///
767796
/// \param conformanceRef The (possibly-specialized) conformance
768797
/// \param requirementSig The generic signature of the requirement
769798
/// \param witnessThunkSig The generic signature of the witness method
770799
/// \param origSubs The substitutions from the call instruction
800+
/// \param isDefaultWitness True if this is a default witness method
801+
/// \param classWitness The ClassDecl if this is a class witness method
771802
static SubstitutionMap
772803
getWitnessMethodSubstitutions(
804+
ModuleDecl *mod,
773805
ProtocolConformanceRef conformanceRef,
774806
GenericSignature *requirementSig,
775807
GenericSignature *witnessThunkSig,
776808
SubstitutionList origSubs,
777-
bool isDefaultWitness) {
809+
bool isDefaultWitness,
810+
ClassDecl *classWitness) {
778811

779812
if (witnessThunkSig == nullptr)
780813
return SubstitutionMap();
@@ -789,42 +822,67 @@ getWitnessMethodSubstitutions(
789822

790823
// If `Self` maps to a bound generic type, this gives us the
791824
// substitutions for the concrete type's generic parameters.
792-
auto baseSubMap = getSubstitutionsForProtocolConformance(conformanceRef);
825+
auto baseSubMap = getSubstitutionsForProtocolConformance(mod, conformanceRef);
793826

794827
unsigned baseDepth = 0;
795828
auto *rootConformance = conformance->getRootNormalConformance();
796829
if (auto *witnessSig = rootConformance->getGenericSignature())
797830
baseDepth = witnessSig->getGenericParams().back()->getDepth() + 1;
798831

799-
auto origDepth = 1;
832+
// If the witness has a class-constrained 'Self' generic parameter,
833+
// we have to build a new substitution map that shifts all generic
834+
// parameters down by one.
835+
if (classWitness != nullptr) {
836+
auto *proto = conformance->getProtocol();
837+
auto selfType = proto->getSelfInterfaceType();
838+
839+
auto selfSubMap = SubstitutionMap::getProtocolSubstitutions(
840+
proto, selfType.subst(origSubMap), conformanceRef);
841+
if (baseSubMap.empty()) {
842+
assert(baseDepth == 0);
843+
baseSubMap = selfSubMap;
844+
} else {
845+
baseSubMap = SubstitutionMap::combineSubstitutionMaps(
846+
selfSubMap,
847+
baseSubMap,
848+
CombineSubstitutionMaps::AtDepth,
849+
/*firstDepth=*/1,
850+
/*secondDepth=*/0,
851+
witnessThunkSig);
852+
}
853+
baseDepth += 1;
854+
}
800855

801856
return SubstitutionMap::combineSubstitutionMaps(
802857
baseSubMap,
803858
origSubMap,
804859
CombineSubstitutionMaps::AtDepth,
805-
baseDepth,
806-
origDepth,
860+
/*firstDepth=*/baseDepth,
861+
/*secondDepth=*/1,
807862
witnessThunkSig);
808863
}
809864

810865
static SubstitutionMap
811866
getWitnessMethodSubstitutions(SILModule &Module, ApplySite AI, SILFunction *F,
812867
ProtocolConformanceRef CRef) {
868+
auto witnessFnTy = F->getLoweredFunctionType();
869+
assert(witnessFnTy->getRepresentation() ==
870+
SILFunctionTypeRepresentation::WitnessMethod);
871+
813872
auto requirementSig = AI.getOrigCalleeType()->getGenericSignature();
814-
auto witnessThunkSig = F->getLoweredFunctionType()->getGenericSignature();
873+
auto witnessThunkSig = witnessFnTy->getGenericSignature();
815874

816875
SubstitutionList origSubs = AI.getSubstitutions();
817876

877+
auto *mod = Module.getSwiftModule();
818878
bool isDefaultWitness =
819-
F->getLoweredFunctionType()->getRepresentation()
820-
== SILFunctionTypeRepresentation::WitnessMethod &&
821-
F->getLoweredFunctionType()->getDefaultWitnessMethodProtocol(
822-
*Module.getSwiftModule())
823-
== CRef.getRequirement();
879+
(witnessFnTy->getDefaultWitnessMethodProtocol(*mod)
880+
== CRef.getRequirement());
881+
auto *classWitness = witnessFnTy->getWitnessMethodClass(*mod);
824882

825883
return getWitnessMethodSubstitutions(
826-
CRef, requirementSig, witnessThunkSig,
827-
origSubs, isDefaultWitness);
884+
mod, CRef, requirementSig, witnessThunkSig,
885+
origSubs, isDefaultWitness, classWitness);
828886
}
829887

830888
/// Generate a new apply of a function_ref to replace an apply of a

0 commit comments

Comments
 (0)