Skip to content

Commit 8fe4f08

Browse files
authored
Merge pull request #2987 from rjmccall/canonicalize-generic-function-type
Canonicalize the components of a GenericFunctionType in the context o…
2 parents 4515457 + fdd25a3 commit 8fe4f08

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)