Skip to content

Canonicalize the components of a GenericFunctionType in the context o… #2987

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions include/swift/AST/GenericSignature.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@ class GenericSignature final : public llvm::FoldingSetNode,
/// The type parameters must be known to not be concrete within the context.
bool areSameTypeParameterInContext(Type type1, Type type2, ModuleDecl &mod);

/// Return the canonical version of the given type under this generic
/// signature.
CanType getCanonicalTypeInContext(Type type, ModuleDecl &mod);
bool isCanonicalTypeInContext(Type type, ModuleDecl &mod);

static void Profile(llvm::FoldingSetNodeID &ID,
ArrayRef<GenericTypeParamType *> genericParams,
ArrayRef<Requirement> requirements);
Expand Down
6 changes: 4 additions & 2 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -2494,8 +2494,10 @@ BEGIN_CAN_TYPE_WRAPPER(GenericFunctionType, AnyFunctionType)
static CanGenericFunctionType get(CanGenericSignature sig,
CanType input, CanType result,
const ExtInfo &info) {
return CanGenericFunctionType(
GenericFunctionType::get(sig, input, result, info));
// Knowing that the argument types are independently canonical is
// not suffiicient to guarantee that the function type will be canonical.
auto fnType = GenericFunctionType::get(sig, input, result, info);
return cast<GenericFunctionType>(fnType->getCanonicalType());
}

CanGenericSignature getGenericSignature() const {
Expand Down
18 changes: 14 additions & 4 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3050,14 +3050,23 @@ GenericFunctionType::get(GenericSignature *sig,
// Do we already have this generic function type?
void *insertPos;
if (auto result
= ctx.Impl.GenericFunctionTypes.FindNodeOrInsertPos(id, insertPos))
= ctx.Impl.GenericFunctionTypes.FindNodeOrInsertPos(id, insertPos)) {
return result;
}

// We have to construct this generic function type. Determine whether
// it's canonical.
// it's canonical. Unfortunately, isCanonicalTypeInContext can cause
// new GenericFunctionTypes to be created and thus invalidate our insertion
// point.
auto &moduleForCanonicality = *ctx.TheBuiltinModule;
bool isCanonical = sig->isCanonical()
&& input->isCanonical()
&& output->isCanonical();
&& sig->isCanonicalTypeInContext(input, moduleForCanonicality)
&& sig->isCanonicalTypeInContext(output, moduleForCanonicality);

if (auto result
= ctx.Impl.GenericFunctionTypes.FindNodeOrInsertPos(id, insertPos)) {
return result;
}

// Allocate storage for the object.
void *mem = ctx.Allocate(sizeof(GenericFunctionType),
Expand All @@ -3067,6 +3076,7 @@ GenericFunctionType::get(GenericSignature *sig,
auto result = new (mem) GenericFunctionType(sig, input, output, info,
isCanonical ? &ctx : nullptr,
properties);

ctx.Impl.GenericFunctionTypes.InsertNode(result, insertPos);
return result;
}
Expand Down
55 changes: 55 additions & 0 deletions lib/AST/GenericSignature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -540,3 +540,58 @@ bool GenericSignature::areSameTypeParameterInContext(Type type1, Type type2,

return pa1 == pa2;
}

bool GenericSignature::isCanonicalTypeInContext(Type type, ModuleDecl &mod) {
// If the type isn't independently canonical, it's certainly not canonical
// in this context.
if (!type->isCanonical())
return false;

// All the contextual canonicality rules apply to type parameters, so if the
// type doesn't involve any type parameters, it's already canonical.
if (!type->hasTypeParameter())
return true;

auto &builder = *getArchetypeBuilder(mod);

// Look for non-canonical type parameters.
return !type.findIf([&](Type component) -> bool {
if (!component->isTypeParameter()) return false;

auto pa = builder.resolveArchetype(component);
if (!pa) return false;

auto rep = pa->getArchetypeAnchor();
return (rep->isConcreteType() || pa != rep);
});
}

CanType GenericSignature::getCanonicalTypeInContext(Type type, ModuleDecl &mod) {
type = type->getCanonicalType();

// All the contextual canonicality rules apply to type parameters, so if the
// type doesn't involve any type parameters, it's already canonical.
if (!type->hasTypeParameter())
return CanType(type);

auto &builder = *getArchetypeBuilder(mod);

// Replace non-canonical type parameters.
type = type.transform([&](Type component) -> Type {
if (!component->isTypeParameter()) return component;

// Resolve the potential archetype. This can be null in nested generic
// types, which we can't immediately canonicalize.
auto pa = builder.resolveArchetype(component);
if (!pa) return component;

auto rep = pa->getArchetypeAnchor();
if (rep->isConcreteType()) {
return getCanonicalTypeInContext(rep->getConcreteType(), mod);
} else {
return rep->getDependentType(builder, /*allowUnresolved*/ false);
}
});

return type->getCanonicalType();
}
26 changes: 6 additions & 20 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1103,29 +1103,15 @@ CanType TypeBase::getCanonicalType() {
GenericSignature *sig = function->getGenericSignature()
->getCanonicalSignature();

// Canonicalize generic parameters.
SmallVector<GenericTypeParamType *, 4> genericParams;
for (auto param : function->getGenericParams()) {
auto newParam = param->getCanonicalType()->castTo<GenericTypeParamType>();
genericParams.push_back(newParam);
}

// Transform requirements.
SmallVector<Requirement, 4> requirements;
for (const auto &req : function->getRequirements()) {
auto firstType = req.getFirstType()->getCanonicalType();
auto secondType = req.getSecondType();
if (secondType)
secondType = secondType->getCanonicalType();
requirements.push_back(Requirement(req.getKind(), firstType, secondType));
}

// Transform input type.
auto inputTy = function->getInput()->getCanonicalType();
auto resultTy = function->getResult()->getCanonicalType();
// Transform the input and result types.
auto &ctx = function->getInput()->getASTContext();
auto &mod = *ctx.TheBuiltinModule;
auto inputTy = sig->getCanonicalTypeInContext(function->getInput(), mod);
auto resultTy = sig->getCanonicalTypeInContext(function->getResult(), mod);

Result = GenericFunctionType::get(sig, inputTy, resultTy,
function->getExtInfo());
assert(Result->isCanonical());
break;
}

Expand Down
4 changes: 1 addition & 3 deletions test/IDE/complete_override_access_control.swift
Original file line number Diff line number Diff line change
Expand Up @@ -196,11 +196,9 @@ public class TestPublicED : ProtocolEPublic, ProtocolDPrivate {
#^TEST_PUBLIC_ED^#
}

// FIXME: there should be no duplicates in the results below.

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

// TEST_INTERNAL_ED: Begin completions, 2 items
// TEST_INTERNAL_ED-DAG: Decl[InstanceMethod]/Super: func collidingGeneric<T>(x: T) {|}{{; name=.+$}}
Expand Down
17 changes: 17 additions & 0 deletions test/SILGen/generic_tuples.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,20 @@ struct Blub {}
func foo<T>(_ x: T) {}
// CHECK-LABEL: sil hidden @_TF14generic_tuples3bar
func bar(_ x: (Blub, Blub)) { foo(x) }


// rdar://26279628
// A type parameter constrained to be a concrete type must be handled
// as that concrete type throughout SILGen. That's especially true
// if it's constrained to be a tuple.

protocol HasAssoc {
associatedtype A
}
extension HasAssoc where A == (Int, Int) {
func returnTupleAlias() -> A {
return (0, 0)
}
}
// 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) {
// CHECK: return {{.*}} : $(Int, Int)