Skip to content

Commit

Permalink
Diagnose nested type references with unsatisfied requirements
Browse files Browse the repository at this point in the history
  • Loading branch information
AnthonyLatsis committed Mar 5, 2020
1 parent 26ceb28 commit f762644
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 15 deletions.
39 changes: 24 additions & 15 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -629,9 +629,9 @@ static bool isPointerToVoid(ASTContext &Ctx, Type Ty, bool &IsMutable) {
return BGT->getGenericArgs().front()->isVoid();
}

static Type checkConstrainedExtensionRequirements(Type type,
SourceLoc loc,
DeclContext *dc) {
static Type checkContextualRequirements(Type type,
SourceLoc loc,
DeclContext *dc) {
// Even if the type is not generic, it might be inside of a generic
// context, so we need to check requirements.
GenericTypeDecl *decl;
Expand All @@ -646,25 +646,34 @@ static Type checkConstrainedExtensionRequirements(Type type,
return type;
}

// FIXME: Some day the type might also have its own 'where' clause, even
// if its not generic.

auto *ext = dyn_cast<ExtensionDecl>(decl->getDeclContext());
if (!ext || !ext->isConstrainedExtension())
return type;

if (parentTy->hasUnboundGenericType() ||
if (!parentTy || parentTy->hasUnboundGenericType() ||
parentTy->hasTypeVariable()) {
return type;
}

auto subMap = parentTy->getContextSubstitutions(ext);
// We are interested in either a contextual where clause or
// a constrained extension context.
TypeSubstitutionMap subMap;
GenericSignature genericSig;
SourceLoc noteLoc;
if (decl->getTrailingWhereClause()) {
subMap = parentTy->getContextSubstitutions(decl->getDeclContext());
genericSig = decl->getGenericSignature();
noteLoc = decl->getLoc();
} else {
const auto ext = dyn_cast<ExtensionDecl>(decl->getDeclContext());
if (ext && ext->isConstrainedExtension()) {
subMap = parentTy->getContextSubstitutions(ext);
genericSig = ext->getGenericSignature();
noteLoc = ext->getLoc();
} else {
return type;
}
}

SourceLoc noteLoc = ext->getLoc();
if (noteLoc.isInvalid())
noteLoc = loc;

auto genericSig = ext->getGenericSignature();
auto result =
TypeChecker::checkGenericArguments(
dc, loc, noteLoc, type,
Expand Down Expand Up @@ -722,7 +731,7 @@ static Type applyGenericArguments(Type type,
if (resolution.getStage() == TypeResolutionStage::Structural)
return type;

return checkConstrainedExtensionRequirements(type, loc, dc);
return checkContextualRequirements(type, loc, dc);
}

if (type->hasError()) {
Expand Down
37 changes: 37 additions & 0 deletions test/Generics/where_clause_contextually_generic_decls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,40 @@ func testMemberDeclarations<T, U: Comparable>(arg1: Class<T>, arg2: Class<U>) {
arg1[] // expected-error {{subscript 'subscript()' requires that 'T' conform to 'Sequence'}}
_ = Class<Array<Int>>()[Int.zero]
}

// Test nested types and requirements.

struct Container<T> {
typealias NestedAlias = Bool where T == Int
// expected-note@-1 {{'NestedAlias' previously declared here}}
typealias NestedAlias = Bool where T == Bool
// expected-error@-1 {{invalid redeclaration of 'NestedAlias}}
typealias NestedAlias2 = T.Magnitude where T: FixedWidthInteger

class NestedClass where T: Equatable {}
}

extension Container where T: Sequence {
struct NestedStruct {}

struct NestedStruct2 where T.Element: Comparable {
enum NestedEnum where T.Element == Double {} // expected-note {{requirement specified as 'T.Element' == 'Double' [with T = String]}}
}

struct NestedStruct3<U: Whereable> {}
}

extension Container.NestedStruct3 {
func foo(arg: U) where U.Assoc == T {}
}

_ = Container<String>.NestedAlias2.self // expected-error {{type 'String' does not conform to protocol 'FixedWidthInteger'}}
_ = Container<Container<Bool>>.NestedClass.self // expected-error {{type 'Container<Bool>' does not conform to protocol 'Equatable'}}
_ = Container<Void>.NestedStruct.self // expected-error {{type 'Void' does not conform to protocol 'Sequence'}}
_ = Container<Array<Void>>.NestedStruct2.self // expected-error {{type 'Void' does not conform to protocol 'Comparable'}}
_ = Container<String>.NestedStruct2.NestedEnum.self // expected-error {{'Container<String>.NestedStruct2.NestedEnum' requires the types 'String.Element' (aka 'Character') and 'Double' be equivalent}}
_ = Container<Int>.NestedAlias2.self
_ = Container<Bool>.NestedClass.self
_ = Container<String>.NestedStruct.self
_ = Container<Array<UInt8>>.NestedStruct2.self
_ = Container<Array<Double>>.NestedStruct2.NestedEnum.self

0 comments on commit f762644

Please sign in to comment.