Skip to content

Fix RequirementMachine crash with invalid AST via getSuperclassForDecl() #82115

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 4 commits into from
Jun 11, 2025
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
4 changes: 0 additions & 4 deletions lib/AST/ConformanceLookupTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -862,8 +862,6 @@ DeclContext *ConformanceLookupTable::getConformingContext(
Type classTy = nominal->getDeclaredInterfaceType();
do {
Type superclassTy = classTy->getSuperclassForDecl(superclassDecl);
if (superclassTy->is<ErrorType>())
return nullptr;
auto inheritedConformance = swift::lookupConformance(
superclassTy, protocol, /*allowMissing=*/false);
if (inheritedConformance)
Expand Down Expand Up @@ -936,8 +934,6 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal,
// declared.
auto *conformingClass = cast<ClassDecl>(conformingNominal);
Type superclassTy = type->getSuperclassForDecl(conformingClass);
if (superclassTy->is<ErrorType>())
return nullptr;

// Look up the inherited conformance.
auto inheritedConformance = swift::lookupConformance(
Expand Down
2 changes: 2 additions & 0 deletions lib/AST/RequirementMachine/Symbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,8 @@ Symbol Symbol::forLayout(LayoutConstraint layout,
/// Creates a superclass symbol, representing a superclass constraint.
Symbol Symbol::forSuperclass(CanType type, ArrayRef<Term> substitutions,
RewriteContext &ctx) {
ASSERT(type.getClassOrBoundGenericClass() != nullptr);

llvm::FoldingSetNodeID id;
id.AddInteger(unsigned(Kind::Superclass));
id.AddPointer(type.getPointer());
Expand Down
26 changes: 16 additions & 10 deletions lib/AST/TypeSubstitution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -523,14 +523,20 @@ Type TypeBase::getSuperclassForDecl(const ClassDecl *baseClass,
t = t->getSuperclass(useArchetypes);
}

#ifndef NDEBUG
auto *currentClass = getConcreteTypeForSuperclassTraversing(this)
->getClassOrBoundGenericClass();
assert(baseClass->isSuperclassOf(currentClass) &&
"no inheritance relationship between given classes");
#endif

return ErrorType::get(this);
if (CONDITIONAL_ASSERT_enabled()) {
auto *currentClass = getConcreteTypeForSuperclassTraversing(this)
->getClassOrBoundGenericClass();
ASSERT(baseClass->isSuperclassOf(currentClass) &&
"no inheritance relationship between given classes");
}

// We can end up here if the AST is invalid, because then
// getSuperclassDecl() might resolve to a decl, and yet
// getSuperclass() is just an ErrorType. Make sure we still
// return a nominal type as the result though, and not an
// ErrorType, because that's what callers expect.
return baseClass->getDeclaredInterfaceType()
.subst(SubstitutionMap())->getCanonicalType();
}

SubstitutionMap TypeBase::getContextSubstitutionMap() {
Expand All @@ -546,7 +552,7 @@ SubstitutionMap TypeBase::getContextSubstitutionMap() {

Type baseTy(this);

assert(!baseTy->hasLValueType() &&
assert(!baseTy->is<LValueType>() &&
!baseTy->is<AnyMetatypeType>() &&
!baseTy->is<ErrorType>());

Expand Down Expand Up @@ -628,7 +634,7 @@ TypeBase::getContextSubstitutions(const DeclContext *dc,
assert(dc->isTypeContext());
Type baseTy(this);

assert(!baseTy->hasLValueType() &&
assert(!baseTy->is<LValueType>() &&
!baseTy->is<AnyMetatypeType>() &&
!baseTy->is<ErrorType>());

Expand Down
7 changes: 1 addition & 6 deletions lib/Sema/TypeCheckStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1146,12 +1146,7 @@ static Expr *buildStorageReference(AccessorDecl *accessor,
auto *baseClass = override->getDeclContext()->getSelfClassDecl();
selfTypeForAccess = selfTypeForAccess->getSuperclassForDecl(baseClass);

// Error recovery path. We get an ErrorType here if getSuperclassForDecl()
// fails (because, for instance, a generic parameter of a generic nominal
// type cannot be resolved).
if (!selfTypeForAccess->is<ErrorType>()) {
subs = selfTypeForAccess->getContextSubstitutionMap(baseClass);
}
subs = selfTypeForAccess->getContextSubstitutionMap(baseClass);

storage = override;

Expand Down
10 changes: 10 additions & 0 deletions test/Generics/unify_superclass_types_invalid.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// RUN: %target-typecheck-verify-swift
// RUN: not %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s

class Base<T> {}
class Derived : Base<Foo> {}
// expected-error@-1 {{cannot find type 'Foo' in scope}}

// CHECK-LABEL: unify_superclass_types_invalid.(file).f@
// CHECK: Generic signature: <T where T : Derived>
func f<T>(_: T) where T: Base<Int>, T: Derived {}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// {"signature":"swift::rewriting::PropertyMap::addSuperclassProperty(swift::rewriting::Term, swift::rewriting::Symbol, unsigned int)"}
// RUN: not --crash %target-swift-frontend -typecheck %s
// RUN: not %target-swift-frontend -typecheck %s
class a < b class c : a func d < b where b : a<Int>, b : c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// {"signature":"swift::ide::printTypeUSR(swift::Type, llvm::raw_ostream&)"}
// RUN: not --crash %target-swift-frontend -typecheck %s
// RUN: not %target-swift-frontend -typecheck %s
class a {
class b < c class e : a<> {
d = b
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// {"signature":"swift::TypeBase::getContextSubstitutions(swift::DeclContext const*, swift::GenericEnvironment*)"}
// RUN: not --crash %target-swift-frontend -typecheck %s
// RUN: not %target-swift-frontend -typecheck %s
{ $0?={