diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 8c0bc28b15c8d..1477cf8fe948f 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -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. @@ -376,13 +393,18 @@ TypeChecker::isUnsupportedMemberTypeAccess(Type type, TypeDecl *typeDecl) { // underlying type is not dependent. if (auto *aliasDecl = dyn_cast(typeDecl)) { if (!aliasDecl->isGeneric() && - aliasDecl->getUnderlyingType()->hasTypeParameter()) { + aliasDecl->getUnderlyingType()->hasTypeParameter() && + !doesTypeAliasFullyConstrainAllOuterGenericParams(aliasDecl)) { return UnsupportedMemberTypeAccessKind::TypeAliasOfUnboundGeneric; } } if (isa(typeDecl)) return UnsupportedMemberTypeAccessKind::AssociatedTypeOfUnboundGeneric; + + if (isa(typeDecl)) + if (!hasUnboundOpener) + return UnsupportedMemberTypeAccessKind::NominalTypeOfUnboundGeneric; } if (type->isExistentialType() && @@ -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(); diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 692c25716b3a3..aa7f062603014 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -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; } } @@ -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()] = + genericSig->getConcreteType(gp); + } + } else { + subs = parentTy->getContextSubstitutions(decl->getDeclContext()); } - subs = parentTy->getContextSubstitutions(decl->getDeclContext()); skipRequirementsCheck |= parentTy->hasTypeVariable(); } else if (auto genericEnv = decl->getDeclContext()->getGenericEnvironmentOfContext()) { @@ -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); @@ -1626,29 +1635,44 @@ static Type resolveIdentTypeComponent(TypeResolution resolution, GenericParamList *silParams, ArrayRef 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() && + !isa(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. diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index c1443e0c309c3..b544a01b60c13 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -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); /// @} diff --git a/test/Generics/unbound.swift b/test/Generics/unbound.swift index 8f57d930ee13c..88e30dc03954e 100644 --- a/test/Generics/unbound.swift +++ b/test/Generics/unbound.swift @@ -91,3 +91,16 @@ func makeInner() -> Outer.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 { // expected-note 2{{generic type 'OuterStruct' declared here}} + struct InnerStruct {} // 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) {} +// expected-error@-1 {{reference to generic type 'OuterStruct' requires arguments in <...>}} + +func nested(_: OuterStruct.InnerStruct) {} +// expected-error@-1 {{reference to generic type 'OuterStruct.InnerStruct' requires arguments in <...>}} diff --git a/test/decl/typealias/fully_constrained.swift b/test/decl/typealias/fully_constrained.swift new file mode 100644 index 0000000000000..2f84c36a78ceb --- /dev/null +++ b/test/decl/typealias/fully_constrained.swift @@ -0,0 +1,64 @@ +// RUN: %target-typecheck-verify-swift + +struct OtherGeneric {} + +struct Generic { + // 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, + _: Generic.Generic, + _: Generic.NonGenericInExtension, + _: Generic.UnboundInExtension, + _: Generic.GenericInExtension) { + + // FIXME: Get these working too +#if false + let _ = Generic.NonGeneric.self + let _ = Generic.Unbound.self + let _ = Generic.Generic.self + + let _ = Generic.NonGenericInExtension.self + let _ = Generic.UnboundInExtension.self + let _ = Generic.GenericInExtension.self + + let _: Generic.NonGeneric = 123 + let _: Generic.NonGenericInExtension = 123 +#endif + + let _: Generic.Unbound = OtherGeneric() + let _: Generic.Generic = OtherGeneric() + + let _: Generic.UnboundInExtension = OtherGeneric() + let _: Generic.GenericInExtension = OtherGeneric() +} + +struct Use { + let a1: Generic.NonGeneric + let b1: Generic.Unbound + let c1: Generic.Generic + let a2: Generic.NonGenericInExtension + let b2: Generic.UnboundInExtension + let c2: Generic.GenericInExtension +} + +extension Generic.NonGeneric {} +extension Generic.Unbound {} +extension Generic.Generic {} + +extension Generic.NonGenericInExtension {} +extension Generic.UnboundInExtension {} +extension Generic.GenericInExtension {}