From 8ec8902e56fdc2aab926ee8518b5f0b058fe6030 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Sun, 5 Oct 2025 11:20:05 +0100 Subject: [PATCH] [Sema] Reject tuple extensions early when feature is disabled Tuple extensions are still an experimental feature, but we have a number of crashers in the repo for them because we still use their type-checker machinery even when disabled. Change the logic here such that we reject them early in extension binding when the feature is disabled. This will ensure we address the crashers before productizing. --- lib/AST/NameLookup.cpp | 12 +++++++++++- lib/Sema/TypeCheckDecl.cpp | 7 +++++++ lib/Sema/TypeCheckDeclPrimary.cpp | 6 ++---- .../38a36bbe0fc496da.swift | 2 +- .../0217ed2d301f5d7b.swift | 2 +- .../4e921915ae337fb5.swift | 2 +- .../548b48b935d4153a.swift | 2 +- .../662d871271047482.swift | 2 +- .../666bd08556187ed3.swift | 2 +- .../compiler_crashers_2_fixed/966aa8f86a7565d3.swift | 12 ++++++++++++ .../983a37d706b4923e.swift | 2 +- .../9adf18151655a6ae.swift | 2 +- .../b4746f0a8b4efd96.swift | 2 +- .../d5eedcc26a9c0a1f.swift | 2 +- .../e6a483a39992886f.swift | 2 +- .../e92d0acb419dec8e.swift | 2 +- 16 files changed, 44 insertions(+), 17 deletions(-) rename validation-test/IDE/{crashers => crashers_fixed}/38a36bbe0fc496da.swift (51%) rename validation-test/{compiler_crashers_2 => compiler_crashers_2_fixed}/0217ed2d301f5d7b.swift (71%) rename validation-test/{compiler_crashers_2 => compiler_crashers_2_fixed}/4e921915ae337fb5.swift (86%) rename validation-test/{compiler_crashers_2 => compiler_crashers_2_fixed}/548b48b935d4153a.swift (86%) rename validation-test/{compiler_crashers_2 => compiler_crashers_2_fixed}/662d871271047482.swift (80%) rename validation-test/{compiler_crashers_2 => compiler_crashers_2_fixed}/666bd08556187ed3.swift (79%) create mode 100644 validation-test/compiler_crashers_2_fixed/966aa8f86a7565d3.swift rename validation-test/{compiler_crashers_2 => compiler_crashers_2_fixed}/983a37d706b4923e.swift (80%) rename validation-test/{compiler_crashers_2 => compiler_crashers_2_fixed}/9adf18151655a6ae.swift (81%) rename validation-test/{compiler_crashers_2 => compiler_crashers_2_fixed}/b4746f0a8b4efd96.swift (79%) rename validation-test/{compiler_crashers_2 => compiler_crashers_2_fixed}/d5eedcc26a9c0a1f.swift (78%) rename validation-test/{compiler_crashers_2 => compiler_crashers_2_fixed}/e6a483a39992886f.swift (81%) rename validation-test/{compiler_crashers_2 => compiler_crashers_2_fixed}/e92d0acb419dec8e.swift (85%) diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 4f785711cf9f1..99a0310aa1c58 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -3630,7 +3630,17 @@ NominalTypeDecl *ExtensionDecl::computeExtendedNominal( if (nominalTypes.empty()) return nullptr; - return nominalTypes[0]; + auto *result = nominalTypes[0]; + + // Tuple extensions are experimental, if the feature isn't enabled let's not + // bind this extension at all. This fixes a bunch of crashers that we don't + // yet properly handle with the feature enabled. + if (isa(result) && + !ctx.LangOpts.hasFeature(Feature::TupleConformances)) { + return nullptr; + } + + return result; } NominalTypeDecl * diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 40ad1ab0cfbbd..b91c36536f66b 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -3089,6 +3089,13 @@ ExtendedTypeRequest::evaluate(Evaluator &eval, ExtensionDecl *ext) const { return error(); } + // Tuple extensions are experimental. + if (extendedType->is() && + !ctx.LangOpts.hasFeature(Feature::TupleConformances)) { + diags.diagnose(ext, diag::experimental_tuple_extension); + return error(); + } + // Cannot extend function types, metatypes, existentials, etc. if (!extendedType->is() && !extendedType->getAnyNominal() && diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 21835411094e3..5849acc3e7857 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -3652,10 +3652,8 @@ class DeclChecker : public DeclVisitor { return; auto &ctx = ED->getASTContext(); - - if (!ctx.LangOpts.hasFeature(Feature::TupleConformances)) { - ED->diagnose(diag::experimental_tuple_extension); - } + ASSERT(ctx.LangOpts.hasFeature(Feature::TupleConformances) && + "Extension binding should not have permitted this"); if (!isValidExtendedTypeForTupleExtension(ED)) { ED->diagnose(diag::tuple_extension_wrong_type, diff --git a/validation-test/IDE/crashers/38a36bbe0fc496da.swift b/validation-test/IDE/crashers_fixed/38a36bbe0fc496da.swift similarity index 51% rename from validation-test/IDE/crashers/38a36bbe0fc496da.swift rename to validation-test/IDE/crashers_fixed/38a36bbe0fc496da.swift index 32668edc7cec7..71248dbbe607b 100644 --- a/validation-test/IDE/crashers/38a36bbe0fc496da.swift +++ b/validation-test/IDE/crashers_fixed/38a36bbe0fc496da.swift @@ -1,5 +1,5 @@ // {"kind":"complete","original":"61e9155a","signature":"swift::ide::CodeCompletionResultBuilder::takeResult()"} -// RUN: not --crash %target-swift-ide-test -code-completion -batch-code-completion -skip-filecheck -code-completion-diagnostics -source-filename %s +// RUN: %target-swift-ide-test -code-completion -batch-code-completion -skip-filecheck -code-completion-diagnostics -source-filename %s extension () where #^^# == <#type#> diff --git a/validation-test/compiler_crashers_2/0217ed2d301f5d7b.swift b/validation-test/compiler_crashers_2_fixed/0217ed2d301f5d7b.swift similarity index 71% rename from validation-test/compiler_crashers_2/0217ed2d301f5d7b.swift rename to validation-test/compiler_crashers_2_fixed/0217ed2d301f5d7b.swift index e35344c055316..1ff01f3b60eb5 100644 --- a/validation-test/compiler_crashers_2/0217ed2d301f5d7b.swift +++ b/validation-test/compiler_crashers_2_fixed/0217ed2d301f5d7b.swift @@ -1,5 +1,5 @@ // {"kind":"typecheck","original":"679d3d53","signature":"(anonymous namespace)::PrintAST::visit(swift::Decl*)"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s extension () { class let a } diff --git a/validation-test/compiler_crashers_2/4e921915ae337fb5.swift b/validation-test/compiler_crashers_2_fixed/4e921915ae337fb5.swift similarity index 86% rename from validation-test/compiler_crashers_2/4e921915ae337fb5.swift rename to validation-test/compiler_crashers_2_fixed/4e921915ae337fb5.swift index 72481efdf0a9a..f223e88995d46 100644 --- a/validation-test/compiler_crashers_2/4e921915ae337fb5.swift +++ b/validation-test/compiler_crashers_2_fixed/4e921915ae337fb5.swift @@ -1,3 +1,3 @@ // {"kind":"typecheck","signature":"swift::ConditionalRequirementsRequest::evaluate(swift::Evaluator&, swift::NormalProtocolConformance*) const","signatureAssert":"Assertion failed: (typeSig.getCanonicalSignature().getGenericParams() == extensionSig.getCanonicalSignature().getGenericParams()), function evaluate"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s protocol a typealias b = () extension b : a diff --git a/validation-test/compiler_crashers_2/548b48b935d4153a.swift b/validation-test/compiler_crashers_2_fixed/548b48b935d4153a.swift similarity index 86% rename from validation-test/compiler_crashers_2/548b48b935d4153a.swift rename to validation-test/compiler_crashers_2_fixed/548b48b935d4153a.swift index 97ac040706a86..cf72a8ae48cee 100644 --- a/validation-test/compiler_crashers_2/548b48b935d4153a.swift +++ b/validation-test/compiler_crashers_2_fixed/548b48b935d4153a.swift @@ -1,4 +1,4 @@ // {"kind":"typecheck","signature":"swift::NominalType::get(swift::NominalTypeDecl*, swift::Type, swift::ASTContext const&)","signatureAssert":"Assertion failed: ((!Parent || Parent->is() || Parent->is() || Parent->is()) && \"parent must be a nominal type\"), function get"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s extension() { struct a extension a diff --git a/validation-test/compiler_crashers_2/662d871271047482.swift b/validation-test/compiler_crashers_2_fixed/662d871271047482.swift similarity index 80% rename from validation-test/compiler_crashers_2/662d871271047482.swift rename to validation-test/compiler_crashers_2_fixed/662d871271047482.swift index d39c4dcb85b60..5c8751cea11dd 100644 --- a/validation-test/compiler_crashers_2/662d871271047482.swift +++ b/validation-test/compiler_crashers_2_fixed/662d871271047482.swift @@ -1,5 +1,5 @@ // {"kind":"typecheck","original":"2e2249a5","signature":"swift::TypeBase::computeCanonicalType()","signatureAssert":"Assertion failed: (Result->isCanonical()), function computeCanonicalType"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s typealias a = ( >extension a where b == { c diff --git a/validation-test/compiler_crashers_2/666bd08556187ed3.swift b/validation-test/compiler_crashers_2_fixed/666bd08556187ed3.swift similarity index 79% rename from validation-test/compiler_crashers_2/666bd08556187ed3.swift rename to validation-test/compiler_crashers_2_fixed/666bd08556187ed3.swift index 27c4b8479be50..d57b439a466d4 100644 --- a/validation-test/compiler_crashers_2/666bd08556187ed3.swift +++ b/validation-test/compiler_crashers_2_fixed/666bd08556187ed3.swift @@ -1,3 +1,3 @@ // {"kind":"typecheck","signature":"(anonymous namespace)::DeclChecker::visit(swift::Decl*)","signatureAssert":"Assertion failed: (false && \"Huh?\"), function isValidExtendedTypeForTupleExtension"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s extension repeat ( diff --git a/validation-test/compiler_crashers_2_fixed/966aa8f86a7565d3.swift b/validation-test/compiler_crashers_2_fixed/966aa8f86a7565d3.swift new file mode 100644 index 0000000000000..e14eabc6f6918 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/966aa8f86a7565d3.swift @@ -0,0 +1,12 @@ +// {"kind":"typecheck","original":"71a26232","signature":"swift::Decl::getDescriptiveKind() const"} +// RUN: not %target-swift-frontend -typecheck %s +typealias a = () +extension a { + func + < (b: Self, c: Self) + { + for (d, e) in repeat (each b each c) { + d < &e + } + } +} diff --git a/validation-test/compiler_crashers_2/983a37d706b4923e.swift b/validation-test/compiler_crashers_2_fixed/983a37d706b4923e.swift similarity index 80% rename from validation-test/compiler_crashers_2/983a37d706b4923e.swift rename to validation-test/compiler_crashers_2_fixed/983a37d706b4923e.swift index 9299180abfa71..ef84dbe79382f 100644 --- a/validation-test/compiler_crashers_2/983a37d706b4923e.swift +++ b/validation-test/compiler_crashers_2_fixed/983a37d706b4923e.swift @@ -1,4 +1,4 @@ // {"kind":"typecheck","signature":"swift::TypeBase::getNominalParent()","signatureAssert":"Assertion failed: (isa(Val) && \"cast() argument of incompatible type!\"), function cast"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s protocol a { typealias b = () extension b diff --git a/validation-test/compiler_crashers_2/9adf18151655a6ae.swift b/validation-test/compiler_crashers_2_fixed/9adf18151655a6ae.swift similarity index 81% rename from validation-test/compiler_crashers_2/9adf18151655a6ae.swift rename to validation-test/compiler_crashers_2_fixed/9adf18151655a6ae.swift index 37243ebbb1521..1a0b27892b349 100644 --- a/validation-test/compiler_crashers_2/9adf18151655a6ae.swift +++ b/validation-test/compiler_crashers_2_fixed/9adf18151655a6ae.swift @@ -1,3 +1,3 @@ // {"kind":"typecheck","signature":"(anonymous namespace)::DeclChecker::visit(swift::Decl*)","signatureAssert":"Assertion failed: (false && \"Huh?\"), function isValidExtendedTypeForTupleExtension"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s typealias a = (repeat b)protocol c extension a : c diff --git a/validation-test/compiler_crashers_2/b4746f0a8b4efd96.swift b/validation-test/compiler_crashers_2_fixed/b4746f0a8b4efd96.swift similarity index 79% rename from validation-test/compiler_crashers_2/b4746f0a8b4efd96.swift rename to validation-test/compiler_crashers_2_fixed/b4746f0a8b4efd96.swift index 84c7ca9db57cb..7b6ebf63339da 100644 --- a/validation-test/compiler_crashers_2/b4746f0a8b4efd96.swift +++ b/validation-test/compiler_crashers_2_fixed/b4746f0a8b4efd96.swift @@ -1,3 +1,3 @@ // {"kind":"typecheck","signature":"(anonymous namespace)::DeclChecker::visit(swift::Decl*)","signatureAssert":"Assertion failed: (false && \"Huh?\"), function isValidExtendedTypeForTupleExtension"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s extension(a[ 0.0 b diff --git a/validation-test/compiler_crashers_2/d5eedcc26a9c0a1f.swift b/validation-test/compiler_crashers_2_fixed/d5eedcc26a9c0a1f.swift similarity index 78% rename from validation-test/compiler_crashers_2/d5eedcc26a9c0a1f.swift rename to validation-test/compiler_crashers_2_fixed/d5eedcc26a9c0a1f.swift index 696bbd91d2247..1ad323fd2c252 100644 --- a/validation-test/compiler_crashers_2/d5eedcc26a9c0a1f.swift +++ b/validation-test/compiler_crashers_2_fixed/d5eedcc26a9c0a1f.swift @@ -1,5 +1,5 @@ // {"kind":"typecheck","signature":"swift::TypeBase::getReducedShape()","signatureAssert":"Assertion failed: (!isTypeVariableOrMember()), function getReducedShape"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s typealias a = () extension a { c { diff --git a/validation-test/compiler_crashers_2/e6a483a39992886f.swift b/validation-test/compiler_crashers_2_fixed/e6a483a39992886f.swift similarity index 81% rename from validation-test/compiler_crashers_2/e6a483a39992886f.swift rename to validation-test/compiler_crashers_2_fixed/e6a483a39992886f.swift index 995c5d5db1242..da06b506ba23a 100644 --- a/validation-test/compiler_crashers_2/e6a483a39992886f.swift +++ b/validation-test/compiler_crashers_2_fixed/e6a483a39992886f.swift @@ -1,3 +1,3 @@ // {"kind":"typecheck","signature":"swift::BuiltinTupleDecl::getTupleSelfType(swift::ExtensionDecl const*) const","signatureAssert":"Assertion failed: (genericParams->getParams().size() == 1), function getTupleSelfType"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s typealias a = () extension a diff --git a/validation-test/compiler_crashers_2/e92d0acb419dec8e.swift b/validation-test/compiler_crashers_2_fixed/e92d0acb419dec8e.swift similarity index 85% rename from validation-test/compiler_crashers_2/e92d0acb419dec8e.swift rename to validation-test/compiler_crashers_2_fixed/e92d0acb419dec8e.swift index 2eee34e3357e4..561aee26d4f84 100644 --- a/validation-test/compiler_crashers_2/e92d0acb419dec8e.swift +++ b/validation-test/compiler_crashers_2_fixed/e92d0acb419dec8e.swift @@ -1,4 +1,4 @@ // {"kind":"typecheck","signature":"swift::GenericTypeParamType::GenericTypeParamType(swift::GenericTypeParamDecl*, swift::RecursiveTypeProperties)","signatureAssert":"Assertion failed: (param->getDepth() != GenericTypeParamDecl::InvalidDepth), function GenericTypeParamType"} -// RUN: not --crash %target-swift-frontend -typecheck %s +// RUN: not %target-swift-frontend -typecheck %s typealias a = () extension a { typealias b = () extension b