Skip to content

Commit fdd25a3

Browse files
committed
Canonicalize the components of a GenericFunctionType in the context of the generic signature.
This is obviously the right thing to do in terms of ensuring that two different expressions of the same signature always result in the same type. It also has the pleasant side-effect of causing the canonical function type to never be expressed in terms of type parameters which have been equated with concrete types, which means that various consumers that work primarily with canonical types (such as SILGen and IRGen) no longer have to worry about such types, at least when decomposing a generic function signature.
1 parent efb4b54 commit fdd25a3

File tree

7 files changed

+102
-29
lines changed

7 files changed

+102
-29
lines changed

include/swift/AST/GenericSignature.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,11 @@ class GenericSignature final : public llvm::FoldingSetNode,
232232
/// The type parameters must be known to not be concrete within the context.
233233
bool areSameTypeParameterInContext(Type type1, Type type2, ModuleDecl &mod);
234234

235+
/// Return the canonical version of the given type under this generic
236+
/// signature.
237+
CanType getCanonicalTypeInContext(Type type, ModuleDecl &mod);
238+
bool isCanonicalTypeInContext(Type type, ModuleDecl &mod);
239+
235240
static void Profile(llvm::FoldingSetNodeID &ID,
236241
ArrayRef<GenericTypeParamType *> genericParams,
237242
ArrayRef<Requirement> requirements);

include/swift/AST/Types.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2494,8 +2494,10 @@ BEGIN_CAN_TYPE_WRAPPER(GenericFunctionType, AnyFunctionType)
24942494
static CanGenericFunctionType get(CanGenericSignature sig,
24952495
CanType input, CanType result,
24962496
const ExtInfo &info) {
2497-
return CanGenericFunctionType(
2498-
GenericFunctionType::get(sig, input, result, info));
2497+
// Knowing that the argument types are independently canonical is
2498+
// not suffiicient to guarantee that the function type will be canonical.
2499+
auto fnType = GenericFunctionType::get(sig, input, result, info);
2500+
return cast<GenericFunctionType>(fnType->getCanonicalType());
24992501
}
25002502

25012503
CanGenericSignature getGenericSignature() const {

lib/AST/ASTContext.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3050,14 +3050,23 @@ GenericFunctionType::get(GenericSignature *sig,
30503050
// Do we already have this generic function type?
30513051
void *insertPos;
30523052
if (auto result
3053-
= ctx.Impl.GenericFunctionTypes.FindNodeOrInsertPos(id, insertPos))
3053+
= ctx.Impl.GenericFunctionTypes.FindNodeOrInsertPos(id, insertPos)) {
30543054
return result;
3055+
}
30553056

30563057
// We have to construct this generic function type. Determine whether
3057-
// it's canonical.
3058+
// it's canonical. Unfortunately, isCanonicalTypeInContext can cause
3059+
// new GenericFunctionTypes to be created and thus invalidate our insertion
3060+
// point.
3061+
auto &moduleForCanonicality = *ctx.TheBuiltinModule;
30583062
bool isCanonical = sig->isCanonical()
3059-
&& input->isCanonical()
3060-
&& output->isCanonical();
3063+
&& sig->isCanonicalTypeInContext(input, moduleForCanonicality)
3064+
&& sig->isCanonicalTypeInContext(output, moduleForCanonicality);
3065+
3066+
if (auto result
3067+
= ctx.Impl.GenericFunctionTypes.FindNodeOrInsertPos(id, insertPos)) {
3068+
return result;
3069+
}
30613070

30623071
// Allocate storage for the object.
30633072
void *mem = ctx.Allocate(sizeof(GenericFunctionType),
@@ -3067,6 +3076,7 @@ GenericFunctionType::get(GenericSignature *sig,
30673076
auto result = new (mem) GenericFunctionType(sig, input, output, info,
30683077
isCanonical ? &ctx : nullptr,
30693078
properties);
3079+
30703080
ctx.Impl.GenericFunctionTypes.InsertNode(result, insertPos);
30713081
return result;
30723082
}

lib/AST/GenericSignature.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,3 +540,58 @@ bool GenericSignature::areSameTypeParameterInContext(Type type1, Type type2,
540540

541541
return pa1 == pa2;
542542
}
543+
544+
bool GenericSignature::isCanonicalTypeInContext(Type type, ModuleDecl &mod) {
545+
// If the type isn't independently canonical, it's certainly not canonical
546+
// in this context.
547+
if (!type->isCanonical())
548+
return false;
549+
550+
// All the contextual canonicality rules apply to type parameters, so if the
551+
// type doesn't involve any type parameters, it's already canonical.
552+
if (!type->hasTypeParameter())
553+
return true;
554+
555+
auto &builder = *getArchetypeBuilder(mod);
556+
557+
// Look for non-canonical type parameters.
558+
return !type.findIf([&](Type component) -> bool {
559+
if (!component->isTypeParameter()) return false;
560+
561+
auto pa = builder.resolveArchetype(component);
562+
if (!pa) return false;
563+
564+
auto rep = pa->getArchetypeAnchor();
565+
return (rep->isConcreteType() || pa != rep);
566+
});
567+
}
568+
569+
CanType GenericSignature::getCanonicalTypeInContext(Type type, ModuleDecl &mod) {
570+
type = type->getCanonicalType();
571+
572+
// All the contextual canonicality rules apply to type parameters, so if the
573+
// type doesn't involve any type parameters, it's already canonical.
574+
if (!type->hasTypeParameter())
575+
return CanType(type);
576+
577+
auto &builder = *getArchetypeBuilder(mod);
578+
579+
// Replace non-canonical type parameters.
580+
type = type.transform([&](Type component) -> Type {
581+
if (!component->isTypeParameter()) return component;
582+
583+
// Resolve the potential archetype. This can be null in nested generic
584+
// types, which we can't immediately canonicalize.
585+
auto pa = builder.resolveArchetype(component);
586+
if (!pa) return component;
587+
588+
auto rep = pa->getArchetypeAnchor();
589+
if (rep->isConcreteType()) {
590+
return getCanonicalTypeInContext(rep->getConcreteType(), mod);
591+
} else {
592+
return rep->getDependentType(builder, /*allowUnresolved*/ false);
593+
}
594+
});
595+
596+
return type->getCanonicalType();
597+
}

lib/AST/Type.cpp

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,29 +1103,15 @@ CanType TypeBase::getCanonicalType() {
11031103
GenericSignature *sig = function->getGenericSignature()
11041104
->getCanonicalSignature();
11051105

1106-
// Canonicalize generic parameters.
1107-
SmallVector<GenericTypeParamType *, 4> genericParams;
1108-
for (auto param : function->getGenericParams()) {
1109-
auto newParam = param->getCanonicalType()->castTo<GenericTypeParamType>();
1110-
genericParams.push_back(newParam);
1111-
}
1112-
1113-
// Transform requirements.
1114-
SmallVector<Requirement, 4> requirements;
1115-
for (const auto &req : function->getRequirements()) {
1116-
auto firstType = req.getFirstType()->getCanonicalType();
1117-
auto secondType = req.getSecondType();
1118-
if (secondType)
1119-
secondType = secondType->getCanonicalType();
1120-
requirements.push_back(Requirement(req.getKind(), firstType, secondType));
1121-
}
1122-
1123-
// Transform input type.
1124-
auto inputTy = function->getInput()->getCanonicalType();
1125-
auto resultTy = function->getResult()->getCanonicalType();
1106+
// Transform the input and result types.
1107+
auto &ctx = function->getInput()->getASTContext();
1108+
auto &mod = *ctx.TheBuiltinModule;
1109+
auto inputTy = sig->getCanonicalTypeInContext(function->getInput(), mod);
1110+
auto resultTy = sig->getCanonicalTypeInContext(function->getResult(), mod);
11261111

11271112
Result = GenericFunctionType::get(sig, inputTy, resultTy,
11281113
function->getExtInfo());
1114+
assert(Result->isCanonical());
11291115
break;
11301116
}
11311117

test/IDE/complete_override_access_control.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,9 @@ public class TestPublicED : ProtocolEPublic, ProtocolDPrivate {
196196
#^TEST_PUBLIC_ED^#
197197
}
198198

199-
// FIXME: there should be no duplicates in the results below.
200-
201199
// TEST_PRIVATE_ED: Begin completions, 2 items
202-
// TEST_PRIVATE_ED-DAG: Decl[InstanceMethod]/Super: private func collidingGeneric<T>(x: T) {|}{{; name=.+$}}
203200
// TEST_PRIVATE_ED-DAG: Decl[InstanceMethod]/Super: private func colliding() {|}{{; name=.+$}}
201+
// TEST_PRIVATE_ED-DAG: Decl[InstanceMethod]/Super: private func collidingGeneric<T>(x: T) {|}{{; name=.+$}}
204202

205203
// TEST_INTERNAL_ED: Begin completions, 2 items
206204
// TEST_INTERNAL_ED-DAG: Decl[InstanceMethod]/Super: func collidingGeneric<T>(x: T) {|}{{; name=.+$}}

test/SILGen/generic_tuples.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,20 @@ struct Blub {}
1919
func foo<T>(_ x: T) {}
2020
// CHECK-LABEL: sil hidden @_TF14generic_tuples3bar
2121
func bar(_ x: (Blub, Blub)) { foo(x) }
22+
23+
24+
// rdar://26279628
25+
// A type parameter constrained to be a concrete type must be handled
26+
// as that concrete type throughout SILGen. That's especially true
27+
// if it's constrained to be a tuple.
28+
29+
protocol HasAssoc {
30+
associatedtype A
31+
}
32+
extension HasAssoc where A == (Int, Int) {
33+
func returnTupleAlias() -> A {
34+
return (0, 0)
35+
}
36+
}
37+
// CHECK-LABEL: sil hidden @_TFe14generic_tuplesRxS_8HasAssocwx1AzTSiSi_rS0_16returnTupleAliasfT_wxS1_ : $@convention(method) <Self where Self : HasAssoc, Self.A == (Int, Int)> (@in_guaranteed Self) -> (Int, Int) {
38+
// CHECK: return {{.*}} : $(Int, Int)

0 commit comments

Comments
 (0)