Skip to content

Commit 930827a

Browse files
committed
Ban Nested some Types in Existentials
We would like to leave room in the language for `some` types in existential constraints a la `any P<some Q>` to resolve as a part of the requirement signature of a future generalized existential type. To that end, usages of `some` types nested anywhere in the arguments of an existential will fail to resolve. rdar://92758884
1 parent 522f0cb commit 930827a

File tree

6 files changed

+70
-6
lines changed

6 files changed

+70
-6
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5032,6 +5032,9 @@ ERROR(unable_to_parse_c_function_type,none,
50325032
ERROR(unsupported_opaque_type,none,
50335033
"'some' types are only permitted in properties, subscripts, and functions", ())
50345034

5035+
ERROR(unsupported_opaque_type_in_existential,none,
5036+
"'some' types cannot be used in constraints on existential types", ())
5037+
50355038
ERROR(opaque_type_unsupported_pattern,none,
50365039
"'some' type can only be declared on a single property declaration", ())
50375040

lib/Sema/TypeCheckType.cpp

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,7 @@ static void diagnoseUnboundGenericType(Type ty, SourceLoc loc);
573573
static Type applyGenericArguments(Type type, TypeResolution resolution,
574574
GenericParamList *silParams,
575575
ComponentIdentTypeRepr *comp) {
576-
const auto options = resolution.getOptions();
576+
auto options = resolution.getOptions();
577577
auto dc = resolution.getDeclContext();
578578
auto loc = comp->getNameLoc().getBaseNameLoc();
579579

@@ -657,6 +657,10 @@ static Type applyGenericArguments(Type type, TypeResolution resolution,
657657
return ErrorType::get(ctx);
658658
}
659659

660+
// Disallow opaque types anywhere in the structure of the generic arguments
661+
// to a parameterized existential type.
662+
if (options.is(TypeResolverContext::ExistentialConstraint))
663+
options |= TypeResolutionFlags::DisallowOpaqueTypes;
660664
auto genericResolution =
661665
resolution.withOptions(adjustOptionsForGenericArgs(options));
662666

@@ -2126,10 +2130,26 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr,
21262130
// This decl is implicit in the source and is created in such contexts by
21272131
// evaluation of an `OpaqueResultTypeRequest`.
21282132
auto opaqueRepr = cast<OpaqueReturnTypeRepr>(repr);
2133+
auto diagnoseDisallowedExistential = [&](Type ty) {
2134+
if (!(options & TypeResolutionFlags::SilenceErrors) &&
2135+
options.contains(TypeResolutionFlags::DisallowOpaqueTypes)) {
2136+
// We're specifically looking at an existential type `any P<some Q>`,
2137+
// so emit a tailored diagnostic. We don't emit an ErrorType here
2138+
// for better recovery.
2139+
diagnose(opaqueRepr->getOpaqueLoc(),
2140+
diag::unsupported_opaque_type_in_existential);
2141+
// FIXME: We shouldn't have to invalid the type repr here, but not
2142+
// doing so causes a double-diagnostic.
2143+
opaqueRepr->setInvalid();
2144+
}
2145+
return ty;
2146+
};
2147+
21292148
auto *DC = getDeclContext();
21302149
if (auto opaqueDecl = dyn_cast<OpaqueTypeDecl>(DC)) {
21312150
if (auto ordinal = opaqueDecl->getAnonymousOpaqueParamOrdinal(opaqueRepr))
2132-
return getIdentityOpaqueTypeArchetypeType(opaqueDecl, *ordinal);
2151+
return diagnoseDisallowedExistential(
2152+
getIdentityOpaqueTypeArchetypeType(opaqueDecl, *ordinal));
21332153
}
21342154

21352155
// Check whether any of the generic parameters in the context represents
@@ -2139,7 +2159,8 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr,
21392159
if (auto genericParams = genericContext->getGenericParams()) {
21402160
for (auto genericParam : *genericParams) {
21412161
if (genericParam->getOpaqueTypeRepr() == opaqueRepr)
2142-
return genericParam->getDeclaredInterfaceType();
2162+
return diagnoseDisallowedExistential(
2163+
genericParam->getDeclaredInterfaceType());
21432164
}
21442165
}
21452166
}
@@ -3997,8 +4018,10 @@ TypeResolver::resolveExistentialType(ExistentialTypeRepr *repr,
39974018
NeverNullType TypeResolver::resolveMetatypeType(MetatypeTypeRepr *repr,
39984019
TypeResolutionOptions options) {
39994020
// The instance type of a metatype is always abstract, not SIL-lowered.
4000-
auto ty = resolveType(repr->getBase(),
4001-
options.withContext(TypeResolverContext::MetatypeBase));
4021+
if (options.is(TypeResolverContext::ExistentialConstraint))
4022+
options |= TypeResolutionFlags::DisallowOpaqueTypes;
4023+
options = options.withContext(TypeResolverContext::MetatypeBase);
4024+
auto ty = resolveType(repr->getBase(), options);
40024025
if (ty->hasError()) {
40034026
return ErrorType::get(getASTContext());
40044027
}

lib/Sema/TypeCheckType.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ enum class TypeResolutionFlags : uint16_t {
6161

6262
/// Make internal @usableFromInline and @inlinable decls visible.
6363
AllowUsableFromInline = 1 << 8,
64+
65+
/// Forbid \c some types from resolving as opaque types.
66+
///
67+
/// Needed to enforce that \c any P<some Q> does not resolve to a
68+
/// parameterized existential with an opaque type constraint.
69+
DisallowOpaqueTypes = 1 << 9,
6470
};
6571

6672
/// Type resolution contexts that require special handling.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %target-swift-frontend -enable-parameterized-existential-types -disable-availability-checking -typecheck -verify %s
2+
3+
// I do not like nested some type params,
4+
// I do not like them Σam-i-am
5+
protocol P<T> {
6+
associatedtype T
7+
}
8+
9+
extension Never: P { typealias T = Never }
10+
11+
// I do not like them written clear
12+
func test() -> any P<some P> { fatalError() } // expected-error {{'some' types cannot be used in constraints on existential types}} expected-error {{generic parameter}}
13+
// I do not like them nested here
14+
func test() -> any P<[some P]> { fatalError() } // expected-error {{'some' types cannot be used in constraints on existential types}} expected-error {{generic parameter}}
15+
// I do not like them under questions
16+
func test() -> any P<(some P)??> { fatalError() } // expected-error {{'some' types cannot be used in constraints on existential types}} expected-error {{generic parameter}}
17+
// I do not like meta-type intentions
18+
func test() -> (any P<some P>).Type { fatalError() } // expected-error {{'some' types cannot be used in constraints on existential types}} expected-error {{generic parameter}}
19+
// I do not like them (meta)static-ly
20+
func test() -> any P<some P>.Type { fatalError() } // expected-error {{'some' types cannot be used in constraints on existential types}} expected-error {{generic parameter}}
21+
// I do not like them tupled-three
22+
func test() -> (Int, any P<some P>, Int) { fatalError() } // expected-error {{'some' types cannot be used in constraints on existential types}} expected-error {{generic parameter}}
23+
// I do not like them in generics
24+
struct Wrapper<T> {}
25+
func test() -> any P<Wrapper<some P>> { fatalError() } // expected-error {{'some' types cannot be used in constraints on existential types}} expected-error {{generic parameter}}
26+
// Your attempts to nest them put me in hysterics.
27+
func test(_ x: any P<some P>) {} // expected-error {{'some' types cannot be used in constraints on existential types}}
28+
29+
// No, I do not like nested some type params,
30+
// I do not like them Σam-i-am

test/type/parameterized_existential.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ struct Collapse<T: DoubleWide>: DoubleWide {
3737
}
3838

3939
func test() -> any DoubleWide<some DoubleWide<Int, Int>, some DoubleWide<Int, Int>> { return Collapse<Int>(x: 42) }
40+
// expected-error@-1 {{'some' types cannot be used in constraints on existential types}}
41+
// expected-error@-2 {{'some' types cannot be used in constraints on existential types}}
4042

4143
func diagonalizeAny(_ x: any Sequence<Int>) -> any Sequence<(Int, Int)> {
4244
return x.map { ($0, $0) }

validation-test/compiler_crashers_2_fixed/unsupported_recursive_opaque_conformance.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -disable-availability-checking -emit-ir -enable-parameterized-existential-types %s
1+
// RUN: not %target-swift-frontend -disable-availability-checking -emit-ir -enable-parameterized-existential-types %s
22

33
protocol P<X, Y> {
44
associatedtype X : P

0 commit comments

Comments
 (0)