Skip to content

Ban Nested some Types in Existentials #58675

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 5, 2022
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
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -5032,6 +5032,9 @@ ERROR(unable_to_parse_c_function_type,none,
ERROR(unsupported_opaque_type,none,
"'some' types are only permitted in properties, subscripts, and functions", ())

ERROR(unsupported_opaque_type_in_existential,none,
"'some' types cannot be used in constraints on existential types", ())

ERROR(opaque_type_unsupported_pattern,none,
"'some' type can only be declared on a single property declaration", ())

Expand Down
33 changes: 28 additions & 5 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ static void diagnoseUnboundGenericType(Type ty, SourceLoc loc);
static Type applyGenericArguments(Type type, TypeResolution resolution,
GenericParamList *silParams,
ComponentIdentTypeRepr *comp) {
const auto options = resolution.getOptions();
auto options = resolution.getOptions();
auto dc = resolution.getDeclContext();
auto loc = comp->getNameLoc().getBaseNameLoc();

Expand Down Expand Up @@ -657,6 +657,10 @@ static Type applyGenericArguments(Type type, TypeResolution resolution,
return ErrorType::get(ctx);
}

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

Expand Down Expand Up @@ -2126,10 +2130,26 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr,
// This decl is implicit in the source and is created in such contexts by
// evaluation of an `OpaqueResultTypeRequest`.
auto opaqueRepr = cast<OpaqueReturnTypeRepr>(repr);
auto diagnoseDisallowedExistential = [&](Type ty) {
if (!(options & TypeResolutionFlags::SilenceErrors) &&
options.contains(TypeResolutionFlags::DisallowOpaqueTypes)) {
// We're specifically looking at an existential type `any P<some Q>`,
// so emit a tailored diagnostic. We don't emit an ErrorType here
// for better recovery.
diagnose(opaqueRepr->getOpaqueLoc(),
diag::unsupported_opaque_type_in_existential);
// FIXME: We shouldn't have to invalid the type repr here, but not
// doing so causes a double-diagnostic.
opaqueRepr->setInvalid();
}
return ty;
};

auto *DC = getDeclContext();
if (auto opaqueDecl = dyn_cast<OpaqueTypeDecl>(DC)) {
if (auto ordinal = opaqueDecl->getAnonymousOpaqueParamOrdinal(opaqueRepr))
return getIdentityOpaqueTypeArchetypeType(opaqueDecl, *ordinal);
return diagnoseDisallowedExistential(
getIdentityOpaqueTypeArchetypeType(opaqueDecl, *ordinal));
}

// Check whether any of the generic parameters in the context represents
Expand All @@ -2139,7 +2159,8 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr,
if (auto genericParams = genericContext->getGenericParams()) {
for (auto genericParam : *genericParams) {
if (genericParam->getOpaqueTypeRepr() == opaqueRepr)
return genericParam->getDeclaredInterfaceType();
return diagnoseDisallowedExistential(
genericParam->getDeclaredInterfaceType());
}
}
}
Expand Down Expand Up @@ -3997,8 +4018,10 @@ TypeResolver::resolveExistentialType(ExistentialTypeRepr *repr,
NeverNullType TypeResolver::resolveMetatypeType(MetatypeTypeRepr *repr,
TypeResolutionOptions options) {
// The instance type of a metatype is always abstract, not SIL-lowered.
auto ty = resolveType(repr->getBase(),
options.withContext(TypeResolverContext::MetatypeBase));
if (options.is(TypeResolverContext::ExistentialConstraint))
options |= TypeResolutionFlags::DisallowOpaqueTypes;
options = options.withContext(TypeResolverContext::MetatypeBase);
auto ty = resolveType(repr->getBase(), options);
if (ty->hasError()) {
return ErrorType::get(getASTContext());
}
Expand Down
6 changes: 6 additions & 0 deletions lib/Sema/TypeCheckType.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ enum class TypeResolutionFlags : uint16_t {

/// Make internal @usableFromInline and @inlinable decls visible.
AllowUsableFromInline = 1 << 8,

/// Forbid \c some types from resolving as opaque types.
///
/// Needed to enforce that \c any P<some Q> does not resolve to a
/// parameterized existential with an opaque type constraint.
DisallowOpaqueTypes = 1 << 9,
};

/// Type resolution contexts that require special handling.
Expand Down
30 changes: 30 additions & 0 deletions test/type/opaque_parameterized_existential.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// RUN: %target-swift-frontend -enable-parameterized-existential-types -disable-availability-checking -typecheck -verify %s

// I do not like nested some type params,
// I do not like them Σam-i-am
protocol P<T> {
associatedtype T
}

extension Never: P { typealias T = Never }

// I do not like them written clear
func test() -> any P<some P> { fatalError() } // expected-error {{'some' types cannot be used in constraints on existential types}} expected-error {{generic parameter}}
// I do not like them nested here
func test() -> any P<[some P]> { fatalError() } // expected-error {{'some' types cannot be used in constraints on existential types}} expected-error {{generic parameter}}
// I do not like them under questions
func test() -> any P<(some P)??> { fatalError() } // expected-error {{'some' types cannot be used in constraints on existential types}} expected-error {{generic parameter}}
// I do not like meta-type intentions
func test() -> (any P<some P>).Type { fatalError() } // expected-error {{'some' types cannot be used in constraints on existential types}} expected-error {{generic parameter}}
// I do not like them (meta)static-ly
func test() -> any P<some P>.Type { fatalError() } // expected-error {{'some' types cannot be used in constraints on existential types}} expected-error {{generic parameter}}
// I do not like them tupled-three
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}}
// I do not like them in generics
struct Wrapper<T> {}
func test() -> any P<Wrapper<some P>> { fatalError() } // expected-error {{'some' types cannot be used in constraints on existential types}} expected-error {{generic parameter}}
// Your attempts to nest them put me in hysterics.
func test(_ x: any P<some P>) {} // expected-error {{'some' types cannot be used in constraints on existential types}}

// No, I do not like nested some type params,
// I do not like them Σam-i-am
2 changes: 2 additions & 0 deletions test/type/parameterized_existential.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ struct Collapse<T: DoubleWide>: DoubleWide {
}

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

func diagonalizeAny(_ x: any Sequence<Int>) -> any Sequence<(Int, Int)> {
return x.map { ($0, $0) }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-swift-frontend -disable-availability-checking -emit-ir -enable-parameterized-existential-types %s
// RUN: not %target-swift-frontend -disable-availability-checking -emit-ir -enable-parameterized-existential-types %s

protocol P<X, Y> {
associatedtype X : P
Expand Down