Skip to content

Commit 5618553

Browse files
authored
Merge pull request #12174 from slavapestov/there-can-only-be-one-self
Fix issue with 'Self' metadata when class conforms to protocol with default implementations
2 parents 488aa70 + f8b06dc commit 5618553

File tree

9 files changed

+723
-294
lines changed

9 files changed

+723
-294
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 "

lib/SILOptimizer/Utils/Devirtualize.cpp

Lines changed: 95 additions & 13 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,7 +822,7 @@ 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();
@@ -798,6 +831,39 @@ getWitnessMethodSubstitutions(
798831

799832
auto origDepth = 1;
800833

834+
// If the witness has a class-constrained 'Self' generic parameter,
835+
// we have to build a new substitution map that shifts all generic
836+
// parameters down by one.
837+
if (classWitness != nullptr) {
838+
baseDepth += 1;
839+
840+
auto &ctx = mod->getASTContext();
841+
auto *proto = conformance->getProtocol();
842+
auto selfType = proto->getSelfInterfaceType();
843+
844+
auto witnessThunkToWitnessMap = witnessThunkSig->getSubstitutionMap(
845+
[&](SubstitutableType *type) -> Type {
846+
if (type->isEqual(selfType))
847+
return classWitness->getSelfInterfaceType();
848+
849+
auto *origParamTy = cast<GenericTypeParamType>(type);
850+
auto *substParamTy = GenericTypeParamType::get(
851+
origParamTy->getDepth() - 1,
852+
origParamTy->getIndex(),
853+
ctx);
854+
855+
return substParamTy;
856+
},
857+
[&](CanType origType, Type replacementType, ProtocolType *protoType)
858+
-> Optional<ProtocolConformanceRef> {
859+
assert(!origType->isEqual(selfType));
860+
861+
return ProtocolConformanceRef(protoType->getDecl());
862+
});
863+
864+
baseSubMap = witnessThunkToWitnessMap.subst(baseSubMap);
865+
}
866+
801867
return SubstitutionMap::combineSubstitutionMaps(
802868
baseSubMap,
803869
origSubMap,
@@ -807,24 +873,40 @@ getWitnessMethodSubstitutions(
807873
witnessThunkSig);
808874
}
809875

876+
static ClassDecl *
877+
getWitnessMethodClass(SILFunctionType *witnessFnTy, ModuleDecl &M) {
878+
auto selfTy = witnessFnTy->getSelfInstanceType();
879+
auto genericSig = witnessFnTy->getGenericSignature();
880+
if (auto paramTy = dyn_cast<GenericTypeParamType>(selfTy)) {
881+
auto superclass = genericSig->getSuperclassBound(paramTy, M);
882+
if (superclass)
883+
return superclass->getClassOrBoundGenericClass();
884+
}
885+
886+
return nullptr;
887+
}
888+
810889
static SubstitutionMap
811890
getWitnessMethodSubstitutions(SILModule &Module, ApplySite AI, SILFunction *F,
812891
ProtocolConformanceRef CRef) {
892+
auto witnessFnTy = F->getLoweredFunctionType();
893+
assert(witnessFnTy->getRepresentation() ==
894+
SILFunctionTypeRepresentation::WitnessMethod);
895+
813896
auto requirementSig = AI.getOrigCalleeType()->getGenericSignature();
814-
auto witnessThunkSig = F->getLoweredFunctionType()->getGenericSignature();
897+
auto witnessThunkSig = witnessFnTy->getGenericSignature();
815898

816899
SubstitutionList origSubs = AI.getSubstitutions();
817900

901+
auto *mod = Module.getSwiftModule();
818902
bool isDefaultWitness =
819-
F->getLoweredFunctionType()->getRepresentation()
820-
== SILFunctionTypeRepresentation::WitnessMethod &&
821-
F->getLoweredFunctionType()->getDefaultWitnessMethodProtocol(
822-
*Module.getSwiftModule())
823-
== CRef.getRequirement();
903+
(witnessFnTy->getDefaultWitnessMethodProtocol(*mod)
904+
== CRef.getRequirement());
905+
auto *classWitness = getWitnessMethodClass(witnessFnTy, *mod);
824906

825907
return getWitnessMethodSubstitutions(
826-
CRef, requirementSig, witnessThunkSig,
827-
origSubs, isDefaultWitness);
908+
mod, CRef, requirementSig, witnessThunkSig,
909+
origSubs, isDefaultWitness, classWitness);
828910
}
829911

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

0 commit comments

Comments
 (0)