Skip to content

Commit 7e481a5

Browse files
authored
Merge pull request #14159 from DougGregor/assoc-type-inference-fixes-4.1
[4.1] Associated type fixes for extraneous recursion, crashes, inference failure
2 parents c171921 + 7cb5b5c commit 7e481a5

11 files changed

+103
-27
lines changed

lib/AST/ProtocolConformance.cpp

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -530,8 +530,9 @@ bool NormalProtocolConformance::hasTypeWitness(AssociatedTypeDecl *assocType,
530530
if (Loader)
531531
resolveLazyInfo();
532532

533-
if (TypeWitnesses.find(assocType) != TypeWitnesses.end()) {
534-
return true;
533+
auto found = TypeWitnesses.find(assocType);
534+
if (found != TypeWitnesses.end()) {
535+
return !found->getSecond().first.isNull();
535536
}
536537
if (resolver) {
537538
PrettyStackTraceRequirement trace("resolving", this, assocType);
@@ -556,24 +557,25 @@ NormalProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
556557
if (known != TypeWitnesses.end())
557558
return known->second;
558559

559-
// If this conformance is in a state where it is inferring type witnesses,
560-
// check tentative witnesses.
561-
if (getState() == ProtocolConformanceState::CheckingTypeWitnesses) {
562-
// If there is a tentative-type-witness function, use it.
563-
if (options.getTentativeTypeWitness) {
564-
if (Type witnessType =
565-
Type(options.getTentativeTypeWitness(this, assocType)))
566-
return { witnessType, nullptr };
567-
}
560+
// If there is a tentative-type-witness function, use it.
561+
if (options.getTentativeTypeWitness) {
562+
if (Type witnessType =
563+
Type(options.getTentativeTypeWitness(this, assocType)))
564+
return { witnessType, nullptr };
565+
}
568566

569-
// Otherwise, we fail; this is the only case in which we can return a
570-
// null type.
567+
// If this conformance is in a state where it is inferring type witnesses but
568+
// we didn't find anything, fail.
569+
if (getState() == ProtocolConformanceState::CheckingTypeWitnesses) {
571570
return { Type(), nullptr };
572571
}
573572

574573
// Otherwise, resolve the type witness.
575574
PrettyStackTraceRequirement trace("resolving", this, assocType);
576575
assert(resolver && "Unable to resolve type witness");
576+
577+
// Block recursive resolution of this type witness.
578+
TypeWitnesses[assocType] = { Type(), nullptr };
577579
resolver->resolveTypeWitness(this, assocType);
578580

579581
known = TypeWitnesses.find(assocType);
@@ -586,7 +588,9 @@ void NormalProtocolConformance::setTypeWitness(AssociatedTypeDecl *assocType,
586588
TypeDecl *typeDecl) const {
587589
assert(getProtocol() == cast<ProtocolDecl>(assocType->getDeclContext()) &&
588590
"associated type in wrong protocol");
589-
assert(TypeWitnesses.count(assocType) == 0 && "Type witness already known");
591+
assert((TypeWitnesses.count(assocType) == 0 ||
592+
TypeWitnesses[assocType].first.isNull()) &&
593+
"Type witness already known");
590594
assert((!isComplete() || isInvalid()) && "Conformance already complete?");
591595
assert(!type->hasArchetype() && "type witnesses must be interface types");
592596
TypeWitnesses[assocType] = std::make_pair(type, typeDecl);

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,8 +1796,8 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType,
17961796

17971797
// If we already recoded this type witness, there's nothing to do.
17981798
if (Conformance->hasTypeWitness(assocType)) {
1799-
assert(Conformance->getTypeWitness(assocType, nullptr)
1800-
->isEqual(type) && "Conflicting type witness deductions");
1799+
assert(Conformance->getTypeWitness(assocType, nullptr)->isEqual(type) &&
1800+
"Conflicting type witness deductions");
18011801
return;
18021802
}
18031803

@@ -2656,8 +2656,12 @@ CheckTypeWitnessResult swift::checkTypeWitness(TypeChecker &tc, DeclContext *dc,
26562656
auto *depTy = DependentMemberType::get(proto->getSelfInterfaceType(),
26572657
assocType);
26582658

2659+
if (type->hasError())
2660+
return ErrorType::get(tc.Context);
2661+
26592662
Type contextType = type->hasTypeParameter() ? dc->mapTypeIntoContext(type)
26602663
: type;
2664+
26612665
if (auto superclass = genericSig->getSuperclassBound(depTy)) {
26622666
if (!superclass->isExactSuperclassOf(contextType))
26632667
return superclass;
@@ -2747,6 +2751,16 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup(
27472751
}
27482752
}
27492753

2754+
// If there are no viable witnesses, and all nonviable candidates came from
2755+
// protocol extensions, treat this as "missing".
2756+
if (viable.empty() &&
2757+
std::find_if(nonViable.begin(), nonViable.end(),
2758+
[](const std::pair<TypeDecl *, CheckTypeWitnessResult> &x) {
2759+
return x.first->getDeclContext()
2760+
->getAsProtocolOrProtocolExtensionContext() == nullptr;
2761+
}) == nonViable.end())
2762+
return ResolveWitnessResult::Missing;
2763+
27502764
// If there is a single viable candidate, form a substitution for it.
27512765
if (viable.size() == 1) {
27522766
auto interfaceType = viable.front().second;
@@ -2781,7 +2795,8 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup(
27812795
[nonViable](NormalProtocolConformance *conformance) {
27822796
auto &diags = conformance->getDeclContext()->getASTContext().Diags;
27832797
for (auto candidate : nonViable) {
2784-
if (candidate.first->getDeclaredInterfaceType()->hasError())
2798+
if (candidate.first->getDeclaredInterfaceType()->hasError() ||
2799+
candidate.second.isError())
27852800
continue;
27862801

27872802
diags.diagnose(

lib/Sema/TypeCheckProtocol.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ class CheckTypeWitnessResult {
7878
bool isConformanceRequirement() const {
7979
return Requirement->isExistentialType();
8080
}
81-
81+
bool isError() const {
82+
return Requirement->is<ErrorType>();
83+
}
8284
explicit operator bool() const { return !Requirement.isNull(); }
8385
};
8486

lib/Sema/TypeCheckProtocolInference.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,7 @@ Type AssociatedTypeInference::computeDefaultTypeWitness(
818818

819819
if (auto failed = checkTypeWitness(tc, dc, proto, assocType, defaultType)) {
820820
// Record the failure, if we haven't seen one already.
821-
if (!failedDefaultedAssocType) {
821+
if (!failedDefaultedAssocType && !failed.isError()) {
822822
failedDefaultedAssocType = defaultedAssocType;
823823
failedDefaultedWitness = defaultType;
824824
failedDefaultedResult = failed;
@@ -1680,6 +1680,9 @@ bool AssociatedTypeInference::diagnoseNoSolutions(
16801680
diags.diagnose(assocType, diag::bad_associated_type_deduction,
16811681
assocType->getFullName(), proto->getFullName());
16821682
for (const auto &failed : failedSet) {
1683+
if (failed.Result.isError())
1684+
continue;
1685+
16831686
diags.diagnose(failed.Witness,
16841687
diag::associated_type_deduction_witness_failed,
16851688
failed.Requirement->getFullName(),

test/Constraints/same_types.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %target-typecheck-verify-swift -verify-ignore-unknown
22

33
protocol Fooable {
4-
associatedtype Foo
4+
associatedtype Foo // expected-note{{protocol requires nested type 'Foo'; do you want to add it?}}
55

66
var foo: Foo { get }
77
}
@@ -148,7 +148,7 @@ rdar19137463(1)
148148

149149
struct Brunch<U : Fooable> where U.Foo == X {}
150150

151-
struct BadFooable : Fooable {
151+
struct BadFooable : Fooable { // expected-error{{type 'BadFooable' does not conform to protocol 'Fooable'}}
152152
typealias Foo = DoesNotExist // expected-error{{use of undeclared type 'DoesNotExist'}}
153153
var foo: Foo { while true {} }
154154
}

test/decl/nested/type_in_function.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func hasAClosure() {
2323
}
2424

2525
protocol Racoon {
26-
associatedtype Stripes
26+
associatedtype Stripes // expected-note{{protocol requires nested type 'Stripes'; do you want to add it?}}
2727
}
2828

2929
// Types inside generic functions -- not supported yet
@@ -117,7 +117,7 @@ struct OuterGenericStruct<A> {
117117
}
118118

119119
func middleFunction() {
120-
struct ConformingType : Racoon {
120+
struct ConformingType : Racoon { // expected-error{{type 'ConformingType' does not conform to protocol 'Racoon'}}
121121
// expected-error@-1 {{type 'ConformingType' cannot be nested in generic function 'middleFunction()'}}
122122
typealias Stripes = A
123123
}

test/decl/protocol/req/associated_type_inference.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,3 +469,34 @@ protocol P17 {
469469
protocol Q17 : P17 where T == Int { }
470470

471471
struct S17 : Q17 { }
472+
473+
// Typealiases from protocol extensions should not inhibit associated type
474+
// inference.
475+
protocol P18 {
476+
associatedtype A
477+
}
478+
479+
protocol P19 : P18 {
480+
associatedtype B
481+
}
482+
483+
extension P18 where Self: P19 {
484+
typealias A = B
485+
}
486+
487+
struct X18<A> : P18 { }
488+
489+
// rdar://problem/16316115
490+
protocol HasAssoc {
491+
associatedtype Assoc
492+
}
493+
494+
struct DefaultAssoc {}
495+
496+
protocol RefinesAssocWithDefault: HasAssoc {
497+
associatedtype Assoc = DefaultAssoc
498+
}
499+
500+
struct Foo: RefinesAssocWithDefault {
501+
}
502+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %target-swift-frontend %s -emit-ir -o 0
2+
3+
extension Slice where Base == UnsafeBufferPointer<UInt16> {
4+
var rebased: UnsafeBufferPointer<UInt16> {
5+
return UnsafeBufferPointer(rebasing: self)
6+
}
7+
}
8+
9+
extension Slice where Base == UnsafeMutableBufferPointer<UInt16> {
10+
var rebased: UnsafeMutableBufferPointer<UInt16> {
11+
return UnsafeMutableBufferPointer(rebasing: self)
12+
}
13+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// RUN: not %target-swift-frontend %s -typecheck
2+
struct Foo: Strideable {
3+
// typealias Stride = Int
4+
let x: Int
5+
6+
func distance(to other: Foo) -> Foo.Stride { return abs(other.x - x) }
7+
func advanced(by n: Foo.Stride) -> Foo { return Foo(x: x + n) }
8+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
// See https://swift.org/LICENSE.txt for license information
66
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
77

8-
// REQUIRES: asserts
9-
// RUN: not --crash %target-swift-frontend %s -emit-ir
8+
9+
// RUN: not %target-swift-frontend %s -emit-ir
1010
protocol P{{}func a{}typealias e}struct A:P{typealias e:Self.a{}typealias e:Self.a{}typealias e:P

validation-test/compiler_crashers/28854-known-typewitnesses-end-didnt-resolve-witness.swift renamed to validation-test/compiler_crashers_fixed/28854-known-typewitnesses-end-didnt-resolve-witness.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
// See https://swift.org/LICENSE.txt for license information
66
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
77

8-
// REQUIRES: asserts
9-
// RUN: not --crash %target-swift-frontend %s -emit-ir
8+
9+
// RUN: not %target-swift-frontend %s -emit-ir
1010
@objc protocol A
1111
{
1212
typealias a

0 commit comments

Comments
 (0)