Skip to content

[GSB] Infer requirements from uses of generic typealiases. #15439

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 3 commits into from
Mar 22, 2018
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
24 changes: 20 additions & 4 deletions lib/AST/GenericSignatureBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4171,7 +4171,7 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement(

auto inheritedReqResult =
addInheritedRequirements(proto, selfType.getUnresolvedType(), source,
/*inferForModule=*/nullptr);
proto->getModuleContext());
if (isErrorResult(inheritedReqResult))
return inheritedReqResult;
}
Expand All @@ -4187,7 +4187,7 @@ ConstraintResult GenericSignatureBuilder::expandConformanceRequirement(
auto innerSource = FloatingRequirementSource::viaProtocolRequirement(
source, proto, &req, /*inferred=*/false);
addRequirement(&req, innerSource, &protocolSubMap,
/*inferForModule=*/nullptr);
proto->getModuleContext());
}
}

Expand Down Expand Up @@ -5430,9 +5430,25 @@ class GenericSignatureBuilder::InferRequirementsWalker : public TypeWalker {
}

Action walkToTypePost(Type ty) override {
auto decl = ty->getAnyNominal();
if (!decl)
// Infer from generic typealiases.
if (auto boundNameAlias = dyn_cast<BoundNameAliasType>(ty.getPointer())) {
auto decl = boundNameAlias->getDecl();
auto genericSig = decl->getGenericSignature();
if (!genericSig)
return Action::Continue;

auto subMap = boundNameAlias->getSubstitutionMap();
for (const auto &rawReq : genericSig->getRequirements()) {
if (auto req = rawReq.subst(subMap))
Builder.addRequirement(*req, source, nullptr);
}

return Action::Continue;
}

// Infer from generic nominal types.
auto decl = ty->getAnyNominal();
if (!decl) return Action::Continue;

auto genericSig = decl->getGenericSignature();
if (!genericSig)
Expand Down
5 changes: 4 additions & 1 deletion lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3154,6 +3154,8 @@ Type TypeResolver::buildProtocolType(
Type TypeChecker::substMemberTypeWithBase(ModuleDecl *module,
TypeDecl *member,
Type baseTy) {
Type sugaredBaseTy = baseTy;

// For type members of a base class, make sure we use the right
// derived class as the parent type.
if (auto *ownerClass = member->getDeclContext()
Expand Down Expand Up @@ -3207,7 +3209,8 @@ Type TypeChecker::substMemberTypeWithBase(ModuleDecl *module,
// If we're referring to a typealias within a generic context, build
// a sugared alias type.
if (aliasDecl && aliasDecl->getGenericSignature()) {
resultType = BoundNameAliasType::get(aliasDecl, baseTy, subs, resultType);
resultType = BoundNameAliasType::get(aliasDecl, sugaredBaseTy, subs,
resultType);
}

return resultType;
Expand Down
42 changes: 40 additions & 2 deletions test/Generics/requirement_inference.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %target-typecheck-verify-swift -typecheck %s -verify
// RUN: %target-typecheck-verify-swift -typecheck -debug-generic-signatures %s > %t.dump 2>&1
// RUN: %target-typecheck-verify-swift -typecheck -verify
// RUN: %target-typecheck-verify-swift -typecheck -debug-generic-signatures > %t.dump 2>&1
// RUN: %FileCheck %s < %t.dump

protocol P1 {
Expand Down Expand Up @@ -460,3 +460,41 @@ func conditionalNested1<U>(_: [ConditionalNested<U>.Inner?]) {}
// CHECK: Generic signature: <U where U : P35, U : P36>
// CHECK: Canonical generic signature: <τ_0_0 where τ_0_0 : P35, τ_0_0 : P36>
func conditionalNested2<U>(_: [ConditionalNested<U>.Inner.Inner2?]) {}

//
// Generate typalias adds requirements that can be inferred
//
typealias X1WithP2<T: P2> = X1<T>

// Inferred requirement T: P2 from the typealias
func testX1WithP2<T>(_: X1WithP2<T>) {
_ = X5<T>() // requires P2
}

// Overload based on the inferred requirement.
func testX1WithP2Overloading<T>(_: X1<T>) {
_ = X5<T>() // expected-error{{type 'T' does not conform to protocol 'P2'}}
}

func testX1WithP2Overloading<T>(_: X1WithP2<T>) {
_ = X5<T>() // requires P2
}

// Extend using the inferred requirement.
// FIXME: Currently broken.
extension X1WithP2 {
func f() {
_ = X5<T>() // FIXME: expected-error{{type 'T' does not conform to protocol 'P2'}}
}
}

// Inference from protocol inheritance clauses.
typealias ExistentialP4WithP2Assoc<T: P4> = P4 where T.P4Assoc : P2

protocol P37 : ExistentialP4WithP2Assoc<Self> { }

extension P37 {
func f() {
_ = X5<P4Assoc>() // requires P2
}
}
12 changes: 12 additions & 0 deletions test/decl/typealias/dependent_types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,15 @@ let _: GenericStruct.MetaAlias = metaFoo()
// ... but if the typealias has a fully concrete underlying type,
// we are OK.
let _: GenericStruct.Concrete = foo()

class SuperG<T, U> {
typealias Composed = (T, U)
}

class SubG<T> : SuperG<T, T> { }

typealias SubGX<T> = SubG<T?>

func checkSugar(gs: SubGX<Int>.Composed) {
let i4: Int = gs // expected-error{{cannot convert value of type 'SubGX<Int>.Composed' (aka '(Optional<Int>, Optional<Int>)') to specified type 'Int'}}
}