Skip to content

Commit 9c2557d

Browse files
committed
AST: Unwrap one-element tuple conformances
1 parent eefa409 commit 9c2557d

File tree

3 files changed

+94
-0
lines changed

3 files changed

+94
-0
lines changed

lib/AST/ASTContext.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2731,6 +2731,13 @@ ASTContext::getSpecializedConformance(Type type,
27312731
if (auto result = specializedConformances.FindNodeOrInsertPos(id, insertPos))
27322732
return result;
27332733

2734+
// Vanishing tuple conformances must be handled by the caller.
2735+
if (isa<BuiltinTupleDecl>(generic->getDeclContext()->getSelfNominalTypeDecl())) {
2736+
assert(type->is<TupleType>() && "Vanishing tuple substitution is not "
2737+
"here. Did you mean to use ProtocolConformanceRef::subst() "
2738+
"instead?");
2739+
}
2740+
27342741
// Build a new specialized conformance.
27352742
auto result
27362743
= new (*this, arena) SpecializedProtocolConformance(type, generic,

lib/AST/ProtocolConformance.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "swift/AST/InFlightSubstitution.h"
2626
#include "swift/AST/LazyResolver.h"
2727
#include "swift/AST/Module.h"
28+
#include "swift/AST/PackConformance.h"
2829
#include "swift/AST/TypeCheckRequests.h"
2930
#include "swift/AST/Types.h"
3031
#include "swift/Basic/Statistic.h"
@@ -933,6 +934,38 @@ ProtocolConformance::subst(TypeSubstitutionFn subs,
933934
return subst(IFS);
934935
}
935936

937+
/// Check if the replacement is a one-element pack with a scalar type.
938+
static bool isVanishingTupleConformance(
939+
RootProtocolConformance *generic,
940+
SubstitutionMap substitutions) {
941+
if (!isa<BuiltinTupleDecl>(generic->getDeclContext()->getSelfNominalTypeDecl()))
942+
return false;
943+
944+
auto replacementTypes = substitutions.getReplacementTypes();
945+
assert(replacementTypes.size() == 1);
946+
auto packType = replacementTypes[0]->castTo<PackType>();
947+
948+
return (packType->getNumElements() == 1 &&
949+
!packType->getElementTypes()[0]->is<PackExpansionType>());
950+
}
951+
952+
/// Don't form a tuple conformance if the substituted type is unwrapped
953+
/// from a one-element tuple.
954+
///
955+
/// That is, [(repeat each T): P] ⊗ {each T := Pack{U};
956+
/// [each T: P]: Pack{ [U: P] }}
957+
/// => [U: P]
958+
static ProtocolConformanceRef unwrapVanishingTupleConformance(
959+
SubstitutionMap substitutions) {
960+
auto conformances = substitutions.getConformances();
961+
assert(conformances.size() == 1);
962+
assert(conformances[0].isPack());
963+
auto packConformance = conformances[0].getPack();
964+
965+
assert(packConformance->getPatternConformances().size() == 1);
966+
return packConformance->getPatternConformances()[0];
967+
}
968+
936969
ProtocolConformanceRef
937970
ProtocolConformance::subst(InFlightSubstitution &IFS) const {
938971
auto *mutableThis = const_cast<ProtocolConformance *>(this);
@@ -951,6 +984,9 @@ ProtocolConformance::subst(InFlightSubstitution &IFS) const {
951984
auto *generic = cast<NormalProtocolConformance>(mutableThis);
952985
auto subMap = SubstitutionMap::get(getGenericSignature(), IFS);
953986

987+
if (isVanishingTupleConformance(generic, subMap))
988+
return unwrapVanishingTupleConformance(subMap);
989+
954990
auto &ctx = substType->getASTContext();
955991
auto *concrete = ctx.getSpecializedConformance(substType, generic, subMap);
956992

@@ -1016,6 +1052,9 @@ ProtocolConformance::subst(InFlightSubstitution &IFS) const {
10161052
auto *generic = spec->getGenericConformance();
10171053
auto subMap = spec->getSubstitutionMap().subst(IFS);
10181054

1055+
if (isVanishingTupleConformance(generic, subMap))
1056+
return unwrapVanishingTupleConformance(subMap);
1057+
10191058
auto substType = spec->getType().subst(IFS);
10201059

10211060
auto &ctx = substType->getASTContext();
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// RUN: %target-swift-frontend -emit-sil %s -enable-experimental-feature TupleConformances | %FileCheck %s
2+
3+
public typealias Tuple<each T> = (repeat each T)
4+
5+
public protocol P {
6+
static func protocolMethod()
7+
}
8+
9+
extension Tuple: P where repeat each T: P {
10+
public static func protocolMethod() {}
11+
}
12+
13+
extension Int: P {
14+
public static func protocolMethod() {}
15+
}
16+
17+
public func callee<T: P>(t: T.Type) {
18+
}
19+
20+
@_transparent
21+
public func transparentCallee<T: P>(t: T.Type) {
22+
t.protocolMethod()
23+
}
24+
25+
@_transparent public func transparentCaller<each T: P>(tuple: (repeat each T).Type) {
26+
callee(t: tuple)
27+
transparentCallee(t: tuple)
28+
tuple.protocolMethod()
29+
}
30+
31+
// Inlining transparentCaller() into caller() exercises the code path to unwrap
32+
// a one-element tuple conformance.
33+
34+
public func caller() {
35+
transparentCaller(tuple: Int.self)
36+
}
37+
38+
// CHECK-LABEL: sil @$s4main6calleryyF : $@convention(thin) () -> () {
39+
// CHECK: [[FN:%.*]] = function_ref @$s4main6callee1tyxm_tAA1PRzlF : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@thick τ_0_0.Type) -> ()
40+
// CHECK: apply [[FN]]<Int>({{.*}}) : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@thick τ_0_0.Type) -> ()
41+
42+
// FIXME: These should call Int.protocolMethod()!
43+
44+
// CHECK: [[FN:%.*]] = function_ref @$sBT4mainRvzAA1PRzlE14protocolMethodyyFZ : $@convention(method) <each τ_0_0 where repeat each τ_0_0 : P> (@thin (repeat each τ_0_0).Type) -> ()
45+
// CHECK: apply [[FN]]<Pack{Int}>(%4) : $@convention(method) <each τ_0_0 where repeat each τ_0_0 : P> (@thin (repeat each τ_0_0).Type) -> ()
46+
47+
// CHECK: [[FN:%.*]] = function_ref @$sBT4mainRvzAA1PRzlE14protocolMethodyyFZ : $@convention(method) <each τ_0_0 where repeat each τ_0_0 : P> (@thin (repeat each τ_0_0).Type) -> ()
48+
// CHECK: apply [[FN]]<Pack{Int}>(%0) : $@convention(method) <each τ_0_0 where repeat each τ_0_0 : P> (@thin (repeat each τ_0_0).Type) -> ()

0 commit comments

Comments
 (0)