Skip to content
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
28 changes: 25 additions & 3 deletions lib/Sema/TypeCheckNameLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,8 +358,25 @@ LookupResult TypeChecker::lookupMember(DeclContext *dc,
return result;
}

static bool doesTypeAliasFullyConstrainAllOuterGenericParams(
TypeAliasDecl *aliasDecl) {
auto parentSig = aliasDecl->getDeclContext()->getGenericSignatureOfContext();
auto genericSig = aliasDecl->getGenericSignature();

if (!parentSig || !genericSig)
return false;

for (auto *paramType : parentSig->getGenericParams()) {
if (!genericSig->isConcreteType(paramType))
return false;
}

return true;
}

TypeChecker::UnsupportedMemberTypeAccessKind
TypeChecker::isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl) {
TypeChecker::isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl,
bool hasUnboundOpener) {
// We don't allow lookups of a non-generic typealias of an unbound
// generic type, because we have no way to model such a type in the
// AST.
Expand All @@ -376,13 +393,18 @@ TypeChecker::isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl) {
// underlying type is not dependent.
if (auto *aliasDecl = dyn_cast<TypeAliasDecl>(typeDecl)) {
if (!aliasDecl->isGeneric() &&
aliasDecl->getUnderlyingType()->hasTypeParameter()) {
aliasDecl->getUnderlyingType()->hasTypeParameter() &&
!doesTypeAliasFullyConstrainAllOuterGenericParams(aliasDecl)) {
return UnsupportedMemberTypeAccessKind::TypeAliasOfUnboundGeneric;
}
}

if (isa<AssociatedTypeDecl>(typeDecl))
return UnsupportedMemberTypeAccessKind::AssociatedTypeOfUnboundGeneric;

if (isa<NominalTypeDecl>(typeDecl))
if (!hasUnboundOpener)
return UnsupportedMemberTypeAccessKind::NominalTypeOfUnboundGeneric;
}

if (type->isExistentialType() &&
Expand Down Expand Up @@ -433,7 +455,7 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc,
continue;
}

if (isUnsupportedMemberTypeAccess(type, typeDecl)
if (isUnsupportedMemberTypeAccess(type, typeDecl, true)
!= TypeChecker::UnsupportedMemberTypeAccessKind::None) {
auto memberType = typeDecl->getDeclaredInterfaceType();

Expand Down
82 changes: 53 additions & 29 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -713,11 +713,7 @@ static Type applyGenericArguments(Type type, TypeResolution resolution,
if (const auto boundTy = openerFn(unboundTy))
return boundTy;

// Complain if we're allowed to and bail out with an error.
if (!options.contains(TypeResolutionFlags::SilenceErrors))
diagnoseUnboundGenericType(type, loc);

return ErrorType::get(resolution.getASTContext());
return type;
}
}

Expand Down Expand Up @@ -915,11 +911,19 @@ Type TypeResolution::applyUnboundGenericArguments(
return BoundGenericType::get(nominalDecl, parentTy, genericArgs);
}

assert(!resultType->hasTypeParameter());
return resultType;
if (!resultType->hasTypeParameter())
return resultType;

auto parentSig = decl->getDeclContext()->getGenericSignatureOfContext();
if (parentSig) {
for (auto gp : parentSig->getGenericParams())
subs[gp->getCanonicalType()->castTo<GenericTypeParamType>()] =
genericSig->getConcreteType(gp);
}
} else {
subs = parentTy->getContextSubstitutions(decl->getDeclContext());
}

subs = parentTy->getContextSubstitutions(decl->getDeclContext());
skipRequirementsCheck |= parentTy->hasTypeVariable();
} else if (auto genericEnv =
decl->getDeclContext()->getGenericEnvironmentOfContext()) {
Expand Down Expand Up @@ -1497,16 +1501,21 @@ static Type resolveNestedIdentTypeComponent(TypeResolution resolution,

auto maybeDiagnoseBadMemberType = [&](TypeDecl *member, Type memberType,
AssociatedTypeDecl *inferredAssocType) {
bool hasUnboundOpener = !!resolution.getUnboundTypeOpener();

if (options.contains(TypeResolutionFlags::SilenceErrors)) {
if (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member)
if (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member,
hasUnboundOpener)
!= TypeChecker::UnsupportedMemberTypeAccessKind::None)
return ErrorType::get(ctx);
}

switch (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member)) {
switch (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member,
hasUnboundOpener)) {
case TypeChecker::UnsupportedMemberTypeAccessKind::None:
break;

case TypeChecker::UnsupportedMemberTypeAccessKind::NominalTypeOfUnboundGeneric:
case TypeChecker::UnsupportedMemberTypeAccessKind::TypeAliasOfUnboundGeneric:
case TypeChecker::UnsupportedMemberTypeAccessKind::AssociatedTypeOfUnboundGeneric:
diagnoseUnboundGenericType(parentTy, parentRange.End);
Expand Down Expand Up @@ -1626,29 +1635,44 @@ static Type
resolveIdentTypeComponent(TypeResolution resolution,
GenericParamList *silParams,
ArrayRef<ComponentIdentTypeRepr *> components) {
auto comp = components.back();

// The first component uses unqualified lookup.
const auto parentComps = components.drop_back();
if (parentComps.empty()) {
return resolveTopLevelIdentTypeComponent(resolution, silParams,
comp);
}
auto topLevelComp = components.front();
auto result = resolveTopLevelIdentTypeComponent(resolution, silParams,
topLevelComp);
if (result->hasError())
return ErrorType::get(result->getASTContext());

// Remaining components are resolved via iterated qualified lookups.
SourceRange parentRange(topLevelComp->getStartLoc(),
topLevelComp->getEndLoc());
for (auto nestedComp : components.drop_front()) {
result = resolveNestedIdentTypeComponent(resolution, silParams,
result, parentRange,
nestedComp);
if (result->hasError())
return ErrorType::get(result->getASTContext());

parentRange.End = nestedComp->getEndLoc();
}

// Diagnose an error if the last component's generic arguments are missing.
auto lastComp = components.back();
auto options = resolution.getOptions();

if (result->is<UnboundGenericType>() &&
!isa<GenericIdentTypeRepr>(lastComp) &&
!resolution.getUnboundTypeOpener() &&
!options.is(TypeResolverContext::TypeAliasDecl)) {

// All remaining components use qualified lookup.
if (!options.contains(TypeResolutionFlags::SilenceErrors)) {
diagnoseUnboundGenericType(result,
lastComp->getNameLoc().getBaseNameLoc());
}

// Resolve the parent type.
Type parentTy = resolveIdentTypeComponent(resolution, silParams,
parentComps);
if (!parentTy || parentTy->hasError()) return parentTy;

SourceRange parentRange(parentComps.front()->getStartLoc(),
parentComps.back()->getEndLoc());
return ErrorType::get(result->getASTContext());
}

// Resolve the nested type.
return resolveNestedIdentTypeComponent(resolution, silParams,
parentTy, parentRange,
comp);
return result;
}

// Hack to apply context-specific @escaping to an AST function type.
Expand Down
6 changes: 4 additions & 2 deletions lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -849,13 +849,15 @@ enum class UnsupportedMemberTypeAccessKind : uint8_t {
TypeAliasOfUnboundGeneric,
TypeAliasOfExistential,
AssociatedTypeOfUnboundGeneric,
AssociatedTypeOfExistential
AssociatedTypeOfExistential,
NominalTypeOfUnboundGeneric
};

/// Check whether the given declaration can be written as a
/// member of the given base type.
UnsupportedMemberTypeAccessKind
isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl);
isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl,
bool hasUnboundOpener);

/// @}

Expand Down
13 changes: 13 additions & 0 deletions test/Generics/unbound.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,16 @@ func makeInner() -> Outer<String, String>.Middle.Inner {
var innerProperty: Outer.Middle.Inner = makeInner()
// expected-error@-1 {{reference to generic type 'Outer' requires arguments in <...>}}

// Some nested generic cases
struct OuterStruct<T> { // expected-note 2{{generic type 'OuterStruct' declared here}}
struct InnerStruct<U> {} // expected-note {{generic type 'InnerStruct' declared here}}
}

func nested(_: OuterStruct.InnerStruct) {}
// expected-error@-1 {{reference to generic type 'OuterStruct' requires arguments in <...>}}

func nested(_: OuterStruct.InnerStruct<Int>) {}
// expected-error@-1 {{reference to generic type 'OuterStruct' requires arguments in <...>}}

func nested(_: OuterStruct<Int>.InnerStruct) {}
// expected-error@-1 {{reference to generic type 'OuterStruct<Int>.InnerStruct' requires arguments in <...>}}
64 changes: 64 additions & 0 deletions test/decl/typealias/fully_constrained.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// RUN: %target-typecheck-verify-swift

struct OtherGeneric<U> {}

struct Generic<T> {
// FIXME: Should work with 'T' as well
typealias NonGeneric = Int where T == Int

typealias Unbound = OtherGeneric where T == Int
typealias Generic = OtherGeneric where T == Int
}

extension Generic where T == Int {
// FIXME: Should work with 'T' as well
typealias NonGenericInExtension = Int

typealias UnboundInExtension = OtherGeneric
typealias GenericInExtension = OtherGeneric
}

func use(_: Generic.NonGeneric,
_: Generic.Unbound<String>,
_: Generic.Generic<String>,
_: Generic.NonGenericInExtension,
_: Generic.UnboundInExtension<String>,
_: Generic.GenericInExtension<String>) {

// FIXME: Get these working too
#if false
let _ = Generic.NonGeneric.self
let _ = Generic.Unbound<String>.self
let _ = Generic.Generic<String>.self

let _ = Generic.NonGenericInExtension.self
let _ = Generic.UnboundInExtension<String>.self
let _ = Generic.GenericInExtension<String>.self

let _: Generic.NonGeneric = 123
let _: Generic.NonGenericInExtension = 123
#endif

let _: Generic.Unbound = OtherGeneric<String>()
let _: Generic.Generic = OtherGeneric<String>()

let _: Generic.UnboundInExtension = OtherGeneric<String>()
let _: Generic.GenericInExtension = OtherGeneric<String>()
}

struct Use {
let a1: Generic.NonGeneric
let b1: Generic.Unbound<String>
let c1: Generic.Generic<String>
let a2: Generic.NonGenericInExtension
let b2: Generic.UnboundInExtension<String>
let c2: Generic.GenericInExtension<String>
}

extension Generic.NonGeneric {}
extension Generic.Unbound {}
extension Generic.Generic {}

extension Generic.NonGenericInExtension {}
extension Generic.UnboundInExtension {}
extension Generic.GenericInExtension {}