AST: Better cope with UnboundGenericType in TypeBase::getSuperclass()

Returning the unsubstituted superclass type is not correct,
because it may contain type parameters. Let's form a new
UnboundGenericType instead.

- Fixes https://github.com/swiftlang/swift/issues/82160.
- Fixes rdar://152989888.
This commit is contained in:
Slava Pestov
2025-06-27 15:09:14 -04:00
parent 0127926255
commit eabc8efb41
3 changed files with 43 additions and 6 deletions

View File

@@ -2249,10 +2249,24 @@ Type TypeBase::getSuperclass(bool useArchetypes) {
Type superclassTy = classDecl->getSuperclass();
// If there's no superclass, or it is fully concrete, we're done.
if (!superclassTy || !superclassTy->hasTypeParameter() ||
hasUnboundGenericType())
if (!superclassTy || !superclassTy->hasTypeParameter())
return superclassTy;
auto hasUnboundGenericType = [&]() {
Type t(this);
while (t) {
if (t->is<UnboundGenericType>())
return true;
t = t->getNominalParent();
}
return false;
};
// If we started with an UnboundGenericType, we cannot apply the
// context substitution map. Return the unbound form of the superclass.
if (hasUnboundGenericType())
return superclassTy->getAnyNominal()->getDeclaredType();
// Gather substitutions from the self type, and apply them to the original
// superclass type to form the substituted superclass type.
auto subMap = getContextSubstitutionMap(classDecl,

View File

@@ -10,8 +10,8 @@
//
//===----------------------------------------------------------------------===//
//
// This file implements validation for Swift types, emitting semantic errors as
// appropriate and checking default initializer values.
// This file implements type resolution, which converts syntactic type
// representations into semantic types, emitting diagnostics as appropriate.
//
//===----------------------------------------------------------------------===//
@@ -6344,14 +6344,23 @@ Type TypeChecker::substMemberTypeWithBase(TypeDecl *member,
: member->getDeclaredInterfaceType();
SubstitutionMap subs;
if (baseTy) {
// Cope with the presence of unbound generic types, which are ill-formed
// at this point but break the invariants of getContextSubstitutionMap().
// If the base type contains an unbound generic type, we cannot
// proceed to the getContextSubstitutionMap() call below.
//
// In general, this means the user program is ill-formed, but we
// do allow type aliases to be referenced with an unbound generic
// type as the base, if the underlying type of the type alias
// does not contain type parameters.
if (baseTy->hasUnboundGenericType()) {
memberType = memberType->getReducedType(aliasDecl->getGenericSignature());
// This is the error case. The diagnostic is emitted elsewhere,
// in TypeChecker::isUnsupportedMemberTypeAccess().
if (memberType->hasTypeParameter())
return ErrorType::get(memberType);
// Otherwise, there's no substitution to be performed, so we
// just drop the base type.
return memberType;
}

View File

@@ -65,6 +65,7 @@ let _: GenericStruct.ReferencesConcrete = foo()
class SuperG<T, U> {
typealias Composed = (T, U)
typealias Concrete = Int
}
class SubG<T> : SuperG<T, T> { }
@@ -74,3 +75,16 @@ typealias SubGX<T> = SubG<T?>
func checkSugar(gs: SubGX<Int>.Composed) {
let i4: Int = gs // expected-error{{cannot convert value of type 'SubGX<Int>.Composed' (aka '(Optional<Int>, Optional<Int>)') to specified type 'Int'}}
}
// https://github.com/swiftlang/swift/issues/82160
let x1: SuperG.Concrete = 123
let x2: SubG.Concrete = 123
func f1() -> SuperG.Concrete {
return 123
}
func f2() -> SubG.Concrete {
return 123
}