Add conformance test for edition unstable

PiperOrigin-RevId: 850054867
diff --git a/cmake/conformance.cmake b/cmake/conformance.cmake
index fbdda17..3e6ef4b 100644
--- a/cmake/conformance.cmake
+++ b/cmake/conformance.cmake
@@ -38,12 +38,16 @@
     ${protobuf_BINARY_DIR}/conformance/conformance.pb.cc
     ${protobuf_BINARY_DIR}/conformance/test_protos/test_messages_edition2023.pb.h
     ${protobuf_BINARY_DIR}/conformance/test_protos/test_messages_edition2023.pb.cc
+    ${protobuf_BINARY_DIR}/conformance/test_protos/test_messages_edition_unstable.pb.h
+    ${protobuf_BINARY_DIR}/conformance/test_protos/test_messages_edition_unstable.pb.cc
   DEPENDS ${protobuf_PROTOC_EXE}
     ${protobuf_SOURCE_DIR}/conformance/conformance.proto
     ${protobuf_SOURCE_DIR}/conformance/test_protos/test_messages_edition2023.proto
+    ${protobuf_SOURCE_DIR}/conformance/test_protos/test_messages_edition_unstable.proto
   COMMAND ${protobuf_PROTOC_EXE}
       ${protobuf_SOURCE_DIR}/conformance/conformance.proto
       ${protobuf_SOURCE_DIR}/conformance/test_protos/test_messages_edition2023.proto
+      ${protobuf_SOURCE_DIR}/conformance/test_protos/test_messages_edition_unstable.proto
       --proto_path=${protobuf_SOURCE_DIR}
       --cpp_out=${protobuf_BINARY_DIR}
 )
@@ -89,6 +93,8 @@
   ${protobuf_BINARY_DIR}/conformance/conformance.pb.cc
   ${protobuf_BINARY_DIR}/conformance/test_protos/test_messages_edition2023.pb.h
   ${protobuf_BINARY_DIR}/conformance/test_protos/test_messages_edition2023.pb.cc
+  ${protobuf_BINARY_DIR}/conformance/test_protos/test_messages_edition_unstable.pb.h
+  ${protobuf_BINARY_DIR}/conformance/test_protos/test_messages_edition_unstable.pb.cc
   ${protobuf_BINARY_DIR}/editions/golden/test_messages_proto3_editions.pb.h
   ${protobuf_BINARY_DIR}/editions/golden/test_messages_proto3_editions.pb.cc
   ${protobuf_BINARY_DIR}/editions/golden/test_messages_proto2_editions.pb.h
diff --git a/conformance/BUILD b/conformance/BUILD
index dbcb8da..a8d3a1f 100644
--- a/conformance/BUILD
+++ b/conformance/BUILD
@@ -138,6 +138,7 @@
         "//:test_messages_proto2_cc_proto",
         "//:test_messages_proto3_cc_proto",
         "//conformance/test_protos:test_messages_edition2023_cc_proto",
+        "//conformance/test_protos:test_messages_edition_unstable_cc_proto",
         "//editions:test_messages_proto2_editions_cc_proto",
         "//editions:test_messages_proto3_editions_cc_proto",
         "//src/google/protobuf",
@@ -166,6 +167,7 @@
         "//:test_messages_proto2_cc_proto",
         "//:test_messages_proto3_cc_proto",
         "//conformance/test_protos:test_messages_edition2023_cc_proto",
+        "//conformance/test_protos:test_messages_edition_unstable_cc_proto",
         "//editions:test_messages_proto2_editions_cc_proto",
         "//editions:test_messages_proto3_editions_cc_proto",
         "//src/google/protobuf",
@@ -249,6 +251,7 @@
         "//:test_messages_proto2_cc_proto",
         "//:test_messages_proto3_cc_proto",
         "//conformance/test_protos:test_messages_edition2023_cc_proto",
+        "//conformance/test_protos:test_messages_edition_unstable_cc_proto",
         "//editions:test_messages_proto2_editions_cc_proto",
         "//editions:test_messages_proto3_editions_cc_proto",
         "@googletest//:gtest",
@@ -440,6 +443,7 @@
         "//:type_cc_proto",
         "//:wrappers_cc_proto",
         "//conformance/test_protos:test_messages_edition2023_cc_proto",
+        "//conformance/test_protos:test_messages_edition_unstable_cc_proto",
         "//editions:test_messages_proto2_editions_cc_proto",
         "//editions:test_messages_proto3_editions_cc_proto",
         "//src/google/protobuf",
@@ -472,6 +476,7 @@
         "//:test_messages_proto2_java_proto",
         "//:test_messages_proto3_java_proto",
         "//conformance/test_protos:test_messages_edition2023_java_proto",
+        "//conformance/test_protos:test_messages_edition_unstable_java_proto",
         "//editions:test_messages_proto2_editions_java_proto",
         "//editions:test_messages_proto3_editions_java_proto",
     ],
@@ -492,6 +497,7 @@
         "//:test_messages_proto2_java_proto_lite",
         "//:test_messages_proto3_java_proto_lite",
         "//conformance/test_protos:test_messages_edition2023_java_proto_lite",
+        "//conformance/test_protos:test_messages_edition_unstable_java_proto_lite",
         "//editions:test_messages_proto2_editions_java_proto_lite",
         "//editions:test_messages_proto3_editions_java_proto_lite",
     ],
@@ -511,6 +517,7 @@
         ":conformance_py_proto",
         "//:protobuf_python",
         "//conformance/test_protos:test_messages_edition2023_py_pb2",
+        "//conformance/test_protos:test_messages_edition_unstable_py_pb2",
         "//editions:test_messages_proto2_editions_py_pb2",
         "//editions:test_messages_proto3_editions_py_pb2",
         "//python:_message",  # Make upb visible if we need it.
@@ -572,6 +579,7 @@
     visibility = ["//csharp:__subpackages__"],
     deps = [
         "//conformance/test_protos:test_messages_edition2023_csharp_proto",
+        "//conformance/test_protos:test_messages_edition_unstable_csharp_proto",
         "//csharp/src/Google.Protobuf.Conformance:conformance_runfiles",
     ],
 )
@@ -587,6 +595,7 @@
         "//:test_messages_proto2_objc_proto",
         "//:test_messages_proto3_objc_proto",
         "//conformance/test_protos:test_messages_edition2023_objc_proto",
+        "//conformance/test_protos:test_messages_edition_unstable_objc_proto",
         "//editions:test_messages_proto2_editions_objc_proto",
         "//editions:test_messages_proto3_editions_objc_proto",
     ],
diff --git a/conformance/ConformanceJava.java b/conformance/ConformanceJava.java
index ee90bf5..84b4884 100644
--- a/conformance/ConformanceJava.java
+++ b/conformance/ConformanceJava.java
@@ -17,6 +17,8 @@
 import com.google.protobuf.util.JsonFormat.TypeRegistry;
 import com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023;
 import com.google.protobuf_test_messages.edition2023.TestMessagesEdition2023;
+import com.google.protobuf_test_messages.edition_unstable.TestAllTypesEditionUnstable;
+import com.google.protobuf_test_messages.edition_unstable.TestMessagesEditionUnstableProto;
 import com.google.protobuf_test_messages.editions.proto2.TestMessagesProto2Editions;
 import com.google.protobuf_test_messages.editions.proto3.TestMessagesProto3Editions;
 import com.google.protobuf_test_messages.proto2.TestMessagesProto2;
@@ -217,6 +219,8 @@
         return TestAllTypesProto2.class;
       case "protobuf_test_messages.editions.TestAllTypesEdition2023":
         return TestAllTypesEdition2023.class;
+      case "protobuf_test_messages.edition_unstable.TestAllTypesEditionUnstable":
+        return TestAllTypesEditionUnstable.class;
       case "protobuf_test_messages.editions.proto3.TestAllTypesProto3":
         return TestMessagesProto3Editions.TestAllTypesProto3.class;
       case "protobuf_test_messages.editions.proto2.TestAllTypesProto2":
@@ -235,6 +239,8 @@
         return TestMessagesProto2.class;
       case "protobuf_test_messages.editions.TestAllTypesEdition2023":
         return TestMessagesEdition2023.class;
+      case "protobuf_test_messages.edition_unstable.TestAllTypesEditionUnstable":
+        return TestMessagesEditionUnstableProto.class;
       case "protobuf_test_messages.editions.proto3.TestAllTypesProto3":
         return TestMessagesProto3Editions.class;
       case "protobuf_test_messages.editions.proto2.TestAllTypesProto2":
diff --git a/conformance/ConformanceJavaLite.java b/conformance/ConformanceJavaLite.java
index 9b6c36e..67e275f 100644
--- a/conformance/ConformanceJavaLite.java
+++ b/conformance/ConformanceJavaLite.java
@@ -15,6 +15,8 @@
 import com.google.protobuf.conformance.Conformance;
 import com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023;
 import com.google.protobuf_test_messages.edition2023.TestMessagesEdition2023;
+import com.google.protobuf_test_messages.edition_unstable.TestAllTypesEditionUnstable;
+import com.google.protobuf_test_messages.edition_unstable.TestMessagesEditionUnstableProto;
 import com.google.protobuf_test_messages.editions.proto2.TestMessagesProto2Editions;
 import com.google.protobuf_test_messages.editions.proto3.TestMessagesProto3Editions;
 import com.google.protobuf_test_messages.proto2.TestMessagesProto2;
@@ -216,6 +218,8 @@
         return TestAllTypesProto2.class;
       case "protobuf_test_messages.editions.TestAllTypesEdition2023":
         return TestAllTypesEdition2023.class;
+      case "protobuf_test_messages.edition_unstable.TestAllTypesEditionUnstable":
+        return TestAllTypesEditionUnstable.class;
       case "protobuf_test_messages.editions.proto3.TestAllTypesProto3":
         return TestMessagesProto3Editions.TestAllTypesProto3.class;
       case "protobuf_test_messages.editions.proto2.TestAllTypesProto2":
@@ -234,6 +238,8 @@
         return TestMessagesProto2.class;
       case "protobuf_test_messages.editions.TestAllTypesEdition2023":
         return TestMessagesEdition2023.class;
+      case "protobuf_test_messages.edition_unstable.TestAllTypesEditionUnstable":
+        return TestMessagesEditionUnstableProto.class;
       case "protobuf_test_messages.editions.proto3.TestAllTypesProto3":
         return TestMessagesProto3Editions.class;
       case "protobuf_test_messages.editions.proto2.TestAllTypesProto2":
diff --git a/conformance/binary_json_conformance_suite.cc b/conformance/binary_json_conformance_suite.cc
index ca24002..d312b0b 100644
--- a/conformance/binary_json_conformance_suite.cc
+++ b/conformance/binary_json_conformance_suite.cc
@@ -33,6 +33,7 @@
 #include "conformance/conformance.pb.h"
 #include "conformance_test.h"
 #include "conformance/test_protos/test_messages_edition2023.pb.h"
+#include "conformance/test_protos/test_messages_edition_unstable.pb.h"
 #include "editions/golden/test_messages_proto2_editions.pb.h"
 #include "editions/golden/test_messages_proto3_editions.pb.h"
 #include "google/protobuf/json/json.h"
@@ -51,6 +52,7 @@
 using google::protobuf::FieldDescriptor;
 using google::protobuf::internal::WireFormatLite;
 using google::protobuf::util::NewTypeResolverForDescriptorPool;
+using protobuf_test_messages::edition_unstable::TestAllTypesEditionUnstable;
 using protobuf_test_messages::editions::TestAllTypesEdition2023;
 using protobuf_test_messages::proto2::TestAllTypesProto2;
 using protobuf_test_messages::proto3::TestAllTypesProto3;
@@ -318,6 +320,7 @@
     BinaryAndJsonConformanceSuiteImpl<TestAllTypesProto2Editions>(
         this, /*run_proto3_tests=*/false);
     RunDelimitedFieldTests();
+    RunUnstableTests();
   }
 }
 
@@ -374,6 +377,22 @@
       R"pb([protobuf_test_messages.editions.delimited_ext] { c: 99 })pb");
 }
 
+void BinaryAndJsonConformanceSuite::RunUnstableTests() {
+  SetTypeUrl(GetTypeUrl(TestAllTypesEditionUnstable::GetDescriptor()));
+
+  RunValidProtobufTest<TestAllTypesEditionUnstable>(
+      absl::StrCat("ValidInt32"), REQUIRED,
+      field(1, WireFormatLite::WIRETYPE_VARINT, varint(99)),
+      R"pb(optional_int32: 99)pb");
+
+  RunValidProtobufTest<TestAllTypesEditionUnstable>(
+      absl::StrCat("ValidMap.Integer"), REQUIRED,
+      len(8,
+          absl::StrCat(field(1, WireFormatLite::WIRETYPE_VARINT, varint(99)),
+                       field(2, WireFormatLite::WIRETYPE_VARINT, varint(87)))),
+      R"pb(map_int32_int32 { key: 99 value: 87 })pb");
+}
+
 void BinaryAndJsonConformanceSuite::RunMessageSetTests() {
   RunValidBinaryProtobufTest<TestAllTypesProto2>(
       absl::StrCat("ValidMessageSetEncoding"), REQUIRED,
diff --git a/conformance/binary_json_conformance_suite.h b/conformance/binary_json_conformance_suite.h
index 23ce5a7..39b8b38 100644
--- a/conformance/binary_json_conformance_suite.h
+++ b/conformance/binary_json_conformance_suite.h
@@ -57,6 +57,8 @@
 
   void RunDelimitedFieldTests();
 
+  void RunUnstableTests();
+
   void RunMessageSetTests();
 
   template <typename MessageType>
diff --git a/conformance/conformance.proto b/conformance/conformance.proto
index e3298f0..b4b2f31 100644
--- a/conformance/conformance.proto
+++ b/conformance/conformance.proto
@@ -101,7 +101,8 @@
   // protobuf_test_messages.proto2.TestAllTypesProto2 or
   // protobuf_test_messages.editions.proto2.TestAllTypesProto2 or
   // protobuf_test_messages.editions.proto3.TestAllTypesProto3 or
-  // protobuf_test_messages.editions.TestAllTypesEdition2023.
+  // protobuf_test_messages.editions.TestAllTypesEdition2023 or
+  // protobuf_test_messages.edition_unstable.TestAllTypesEditionUnstable.
   string message_type = 4;
 
   // Each test is given a specific test category. Some category may need
diff --git a/conformance/conformance_cpp.cc b/conformance/conformance_cpp.cc
index 41239eb..50becc0 100644
--- a/conformance/conformance_cpp.cc
+++ b/conformance/conformance_cpp.cc
@@ -32,6 +32,7 @@
 #include "absl/strings/str_cat.h"
 #include "conformance/conformance.pb.h"
 #include "conformance/test_protos/test_messages_edition2023.pb.h"
+#include "conformance/test_protos/test_messages_edition_unstable.pb.h"
 #include "editions/golden/test_messages_proto2_editions.pb.h"
 #include "editions/golden/test_messages_proto3_editions.pb.h"
 #include "google/protobuf/endian.h"
@@ -58,6 +59,7 @@
 using ::google::protobuf::util::MessageToJsonString;
 using ::google::protobuf::util::NewTypeResolverForDescriptorPool;
 using ::google::protobuf::util::TypeResolver;
+using ::protobuf_test_messages::edition_unstable::TestAllTypesEditionUnstable;
 using ::protobuf_test_messages::editions::TestAllTypesEdition2023;
 using ::protobuf_test_messages::proto2::TestAllTypesProto2;
 using ::protobuf_test_messages::proto3::TestAllTypesProto3;
@@ -97,6 +99,7 @@
     google::protobuf::LinkMessageReflection<TestAllTypesProto2>();
     google::protobuf::LinkMessageReflection<TestAllTypesProto3>();
     google::protobuf::LinkMessageReflection<TestAllTypesEdition2023>();
+    google::protobuf::LinkMessageReflection<TestAllTypesEditionUnstable>();
     google::protobuf::LinkMessageReflection<TestAllTypesProto2Editions>();
     google::protobuf::LinkMessageReflection<TestAllTypesProto3Editions>();
 
diff --git a/conformance/conformance_objc.m b/conformance/conformance_objc.m
index 78c7e1b..64a51a3 100644
--- a/conformance/conformance_objc.m
+++ b/conformance/conformance_objc.m
@@ -13,6 +13,7 @@
 #import "google/protobuf/TestMessagesProto2.pbobjc.h"
 #import "google/protobuf/TestMessagesProto3.pbobjc.h"
 #import "test_protos/TestMessagesEdition2023.pbobjc.h"
+#import "test_protos/TestMessagesEditionUnstable.pbobjc.h"
 
 static void Die(NSString *format, ...) __dead2;
 
@@ -66,6 +67,11 @@
         msgClass = [EditionsTestAllTypesEdition2023 class];
         registry = [EditionsTestMessagesEdition2023Root extensionRegistry];
       } else if ([request.messageType
+                     isEqual:
+                         @"protobuf_test_messages.edition_unstable.TestAllTypesEditionUnstable"]) {
+        msgClass = [EditionUnstableTestAllTypesEditionUnstable class];
+        registry = [EditionUnstableTestMessagesEditionUnstableRoot extensionRegistry];
+      } else if ([request.messageType
                      isEqual:@"protobuf_test_messages.editions.proto2.TestAllTypesProto2"]) {
         msgClass = [EditionsProto2TestAllTypesProto2 class];
         registry = [EditionsProto2TestMessagesProto2EditionsRoot extensionRegistry];
diff --git a/conformance/conformance_php.php b/conformance/conformance_php.php
index 66af473..4a0de2d 100644
--- a/conformance/conformance_php.php
+++ b/conformance/conformance_php.php
@@ -63,6 +63,9 @@
                 case 'protobuf_test_messages.editions.TestAllTypesEdition2023':
                     $response->setSkipped('PHP doesn\'t support editions-specific features yet');
                     return $response;
+                case 'protobuf_test_messages.edition_unstable.TestAllTypesEditionUnstable':
+                    $response->setSkipped('PHP doesn\'t support editions-specific features yet');
+                    return $response;
                 case '':
                     trigger_error(
                         'Protobuf request doesn\'t have specific payload type',
diff --git a/conformance/conformance_python.py b/conformance/conformance_python.py
index 29df365..eb0dc50 100755
--- a/conformance/conformance_python.py
+++ b/conformance/conformance_python.py
@@ -20,6 +20,7 @@
 from google.protobuf import test_messages_proto3_pb2
 from conformance import conformance_pb2
 from conformance.test_protos import test_messages_edition2023_pb2
+from conformance.test_protos import test_messages_edition_unstable_pb2
 from editions.golden import test_messages_proto2_editions_pb2
 from editions.golden import test_messages_proto3_editions_pb2
 
@@ -42,6 +43,11 @@
     return test_messages_proto2_editions_pb2.TestAllTypesProto2()
   if type == "protobuf_test_messages.editions.proto3.TestAllTypesProto3":
     return test_messages_proto3_editions_pb2.TestAllTypesProto3()
+  if (
+      type
+      == "protobuf_test_messages.edition_unstable.TestAllTypesEditionUnstable"
+  ):
+    return test_messages_edition_unstable_pb2.TestAllTypesEditionUnstable()
   return None
 
 
diff --git a/conformance/conformance_rust.rs b/conformance/conformance_rust.rs
index 820d67c..7748c4d 100644
--- a/conformance/conformance_rust.rs
+++ b/conformance/conformance_rust.rs
@@ -12,6 +12,7 @@
 
 use std::io::{self, ErrorKind, Read, Write};
 use test_messages_edition2023_rust_proto::TestAllTypesEdition2023;
+use test_messages_edition_unstable_rust_proto::TestAllTypesEditionUnstable;
 use test_messages_proto2_editions_rust_proto::TestAllTypesProto2 as EditionsTestAllTypesProto2;
 use test_messages_proto2_rust_proto::TestAllTypesProto2;
 use test_messages_proto3_editions_rust_proto::TestAllTypesProto3 as EditionsTestAllTypesProto3;
@@ -83,6 +84,9 @@
         b"protobuf_test_messages.editions.TestAllTypesEdition2023" => {
             roundtrip::<TestAllTypesEdition2023>(bytes)
         }
+        b"protobuf_test_messages.edition_unstable.TestAllTypesEditionUnstable" => {
+            roundtrip::<TestAllTypesEditionUnstable>(bytes)
+        }
         b"protobuf_test_messages.editions.proto2.TestAllTypesProto2" => {
             roundtrip::<EditionsTestAllTypesProto2>(bytes)
         }
diff --git a/conformance/conformance_test.cc b/conformance/conformance_test.cc
index 17f0aa0..6ad4034 100644
--- a/conformance/conformance_test.cc
+++ b/conformance/conformance_test.cc
@@ -239,6 +239,8 @@
       return "Proto3";
     case Edition::EDITION_PROTO2:
       return "Proto2";
+    case Edition::EDITION_UNSTABLE:
+      return "EditionUnstable";
     default: {
       std::string id = "Editions";
       if (prototype_message_.GetDescriptor()->name() == "TestAllTypesProto2") {
diff --git a/conformance/naming.h b/conformance/naming.h
index 98c1a30..44455ab 100644
--- a/conformance/naming.h
+++ b/conformance/naming.h
@@ -26,6 +26,8 @@
       return "Proto3";
     case Edition::EDITION_PROTO2:
       return "Proto2";
+    case Edition::EDITION_UNSTABLE:
+      return "EditionUnstable";
     default: {
       std::string id = "Editions";
       if (message.name() == "TestAllTypesProto2") {
diff --git a/conformance/naming_test.cc b/conformance/naming_test.cc
index f3e629b..75e366b 100644
--- a/conformance/naming_test.cc
+++ b/conformance/naming_test.cc
@@ -2,6 +2,7 @@
 
 #include <gtest/gtest.h>
 #include "conformance/test_protos/test_messages_edition2023.pb.h"
+#include "conformance/test_protos/test_messages_edition_unstable.pb.h"
 #include "editions/golden/test_messages_proto2_editions.pb.h"
 #include "editions/golden/test_messages_proto3_editions.pb.h"
 #include "google/protobuf/test_messages_proto2.pb.h"
@@ -12,6 +13,7 @@
 namespace conformance {
 namespace {
 
+using protobuf_test_messages::edition_unstable::TestAllTypesEditionUnstable;
 using protobuf_test_messages::editions::TestAllTypesEdition2023;
 using protobuf_test_messages::proto2::TestAllTypesProto2;
 using protobuf_test_messages::proto3::TestAllTypesProto3;
@@ -25,6 +27,8 @@
   EXPECT_EQ(GetEditionIdentifier(*TestAllTypesProto3::descriptor()), "Proto3");
   EXPECT_EQ(GetEditionIdentifier(*TestAllTypesEdition2023::descriptor()),
             "Editions");
+  EXPECT_EQ(GetEditionIdentifier(*TestAllTypesEditionUnstable::descriptor()),
+            "EditionUnstable");
   EXPECT_EQ(GetEditionIdentifier(*TestAllTypesProto2Editions::descriptor()),
             "Editions_Proto2");
   EXPECT_EQ(GetEditionIdentifier(*TestAllTypesProto3Editions::descriptor()),
diff --git a/conformance/ruby/BUILD b/conformance/ruby/BUILD
index 8b749cf..405221b 100644
--- a/conformance/ruby/BUILD
+++ b/conformance/ruby/BUILD
@@ -25,6 +25,12 @@
     srcs = [":copied_test_messages_edition2023_proto"],
 )
 
+internal_ruby_proto_library(
+    name = "test_messages_edition_unstable_ruby_proto",
+    testonly = True,
+    srcs = [":copied_test_messages_edition_unstable_proto"],
+)
+
 rb_binary(
     name = "conformance_ruby",
     testonly = True,
@@ -34,6 +40,7 @@
     deps = [
         ":conformance_ruby_proto",
         ":test_messages_edition2023_ruby_proto",
+        ":test_messages_edition_unstable_ruby_proto",
         "//ruby:conformance_editions_test_ruby_proto",
         "//ruby:conformance_test_ruby_proto",
         "//ruby:protobuf",
@@ -55,3 +62,10 @@
     srcs = ["//conformance/test_protos:test_messages_edition2023.proto"],
     strip_prefix = "conformance/test_protos",
 )
+
+internal_copy_files(
+    name = "copied_test_messages_edition_unstable_proto",
+    testonly = True,
+    srcs = ["//conformance/test_protos:test_messages_edition_unstable.proto"],
+    strip_prefix = "conformance/test_protos",
+)
diff --git a/conformance/ruby/conformance_ruby.rb b/conformance/ruby/conformance_ruby.rb
index ae68caa..6425c7f 100755
--- a/conformance/ruby/conformance_ruby.rb
+++ b/conformance/ruby/conformance_ruby.rb
@@ -14,6 +14,7 @@
 
 require 'conformance/ruby/conformance_pb'
 require 'conformance/ruby/test_messages_edition2023_pb'
+require 'conformance/ruby/test_messages_edition_unstable_pb'
 require 'google/protobuf'
 require 'google/protobuf/test_messages_proto3_pb'
 require 'google/protobuf/test_messages_proto2_pb'
diff --git a/conformance/test_protos/BUILD b/conformance/test_protos/BUILD
index 046e66b..8a64e3c 100644
--- a/conformance/test_protos/BUILD
+++ b/conformance/test_protos/BUILD
@@ -14,13 +14,21 @@
     default_visibility = ["//conformance:__subpackages__"],
 )
 
-exports_files(["test_messages_edition2023.proto"])
+exports_files([
+    "test_messages_edition2023.proto",
+    "test_messages_edition_unstable.proto",
+])
 
 internal_csharp_proto_library(
     name = "test_messages_edition2023_csharp_proto",
     srcs = ["test_messages_edition2023.proto"],
 )
 
+internal_csharp_proto_library(
+    name = "test_messages_edition_unstable_csharp_proto",
+    srcs = ["test_messages_edition_unstable.proto"],
+)
+
 # Aliases for older monolithic conformance test protos defined elsewhere.
 alias(
     name = "test_messages_proto2_editions_proto",
@@ -52,28 +60,60 @@
     visibility = ["//visibility:public"],
 )
 
+proto_library(
+    name = "test_messages_edition_unstable_proto",
+    srcs = ["test_messages_edition_unstable.proto"],
+    visibility = ["//visibility:public"],
+)
+
 java_lite_proto_library(
     name = "test_messages_edition2023_java_proto_lite",
     deps = [":test_messages_edition2023_proto"],
 )
 
+java_lite_proto_library(
+    name = "test_messages_edition_unstable_java_proto_lite",
+    deps = [":test_messages_edition_unstable_proto"],
+)
+
 java_proto_library(
     name = "test_messages_edition2023_java_proto",
     deps = [":test_messages_edition2023_proto"],
 )
 
+java_proto_library(
+    name = "test_messages_edition_unstable_java_proto",
+    deps = [":test_messages_edition_unstable_proto"],
+)
+
 cc_proto_library(
     name = "test_messages_edition2023_cc_proto",
     deps = [":test_messages_edition2023_proto"],
 )
 
+cc_proto_library(
+    name = "test_messages_edition_unstable_cc_proto",
+    deps = [":test_messages_edition_unstable_proto"],
+)
+
 internal_py_proto_library(
     name = "test_messages_edition2023_py_pb2",
     srcs = [":test_messages_edition2023.proto"],
     srcs_version = "PY2AND3",
 )
 
+internal_py_proto_library(
+    name = "test_messages_edition_unstable_py_pb2",
+    srcs = [":test_messages_edition_unstable.proto"],
+    srcs_version = "PY2AND3",
+)
+
 internal_objc_proto_library(
     name = "test_messages_edition2023_objc_proto",
     srcs = [":test_messages_edition2023.proto"],
 )
+
+internal_objc_proto_library(
+    name = "test_messages_edition_unstable_objc_proto",
+    srcs = [":test_messages_edition_unstable.proto"],
+)
diff --git a/conformance/test_protos/test_messages_edition_unstable.proto b/conformance/test_protos/test_messages_edition_unstable.proto
new file mode 100644
index 0000000..061116e
--- /dev/null
+++ b/conformance/test_protos/test_messages_edition_unstable.proto
@@ -0,0 +1,61 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2024 Google Inc.  All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+
+edition = "UNSTABLE";
+
+package protobuf_test_messages.edition_unstable;
+
+option features.message_encoding = DELIMITED;
+option java_package = "com.google.protobuf_test_messages.edition_unstable";
+option objc_class_prefix = "EditionUnstable";
+
+message ComplexMessage {
+  int32 d = 1;
+}
+
+message TestAllTypesEditionUnstable {
+  // Singular
+  int32 optional_int32 = 1;
+
+  ForeignMessageEditionUnstable optional_foreign_message = 2
+      [features.message_encoding = LENGTH_PREFIXED];
+
+  ForeignEnumEditionUnstable optional_foreign_enum = 3;
+
+  TestAllTypesEditionUnstable recursive_message = 4
+      [features.message_encoding = LENGTH_PREFIXED];
+
+  // Repeated
+  repeated int32 repeated_int32 = 5;
+  repeated ForeignMessageEditionUnstable repeated_foreign_message = 6
+      [features.message_encoding = LENGTH_PREFIXED];
+  repeated ForeignEnumEditionUnstable repeated_foreign_enum = 7;
+
+  // Map
+  map<int32, int32> map_int32_int32 = 8;
+  map<bool, bool> map_bool_bool = 9;
+  map<string, string> map_string_string = 10;
+  map<string, ForeignMessageEditionUnstable> map_string_foreign_message = 11;
+  map<string, ForeignEnumEditionUnstable> map_string_foreign_enum = 12;
+
+  // extensions
+  extensions 120 to 200;
+}
+
+message ForeignMessageEditionUnstable {
+  int32 c = 1;
+}
+
+enum ForeignEnumEditionUnstable {
+  FOREIGN_FOO = 0;
+  FOREIGN_BAR = 1;
+  FOREIGN_BAZ = 2;
+}
+
+extend TestAllTypesEditionUnstable {
+  int32 extension_int32 = 120;
+}
diff --git a/conformance/text_format_conformance_suite.cc b/conformance/text_format_conformance_suite.cc
index d0cf5a6..9ae03a8 100644
--- a/conformance/text_format_conformance_suite.cc
+++ b/conformance/text_format_conformance_suite.cc
@@ -18,6 +18,7 @@
 #include "absl/strings/str_format.h"
 #include "conformance_test.h"
 #include "conformance/test_protos/test_messages_edition2023.pb.h"
+#include "conformance/test_protos/test_messages_edition_unstable.pb.h"
 #include "editions/golden/test_messages_proto2_editions.pb.h"
 #include "editions/golden/test_messages_proto3_editions.pb.h"
 #include "google/protobuf/test_messages_proto2.pb.h"
@@ -143,6 +144,10 @@
       // There are no editions-sensitive performance tests.
       return;
     }
+    if (MessageType::GetDescriptor()->name() == "TestAllTypesEditionUnstable") {
+      // There are no editions-sensitive performance tests.
+      return;
+    }
     RunTextFormatPerformanceTests();
   } else {
     if (MessageType::GetDescriptor()->name() == "TestAllTypesProto2") {
diff --git a/csharp/generate_protos.sh b/csharp/generate_protos.sh
index fc22b7e..9cad0f6 100755
--- a/csharp/generate_protos.sh
+++ b/csharp/generate_protos.sh
@@ -59,6 +59,7 @@
     --include_source_info \
     --include_imports \
     conformance/test_protos/test_messages_edition2023.proto \
+    conformance/test_protos/test_messages_edition_unstable.proto \
     csharp/protos/map_unittest_proto3.proto \
     csharp/protos/unittest_issues.proto \
     csharp/protos/unittest_custom_options_proto3.proto \
diff --git a/csharp/src/Google.Protobuf.Conformance/Program.cs b/csharp/src/Google.Protobuf.Conformance/Program.cs
index e22a961..c09aeb7 100644
--- a/csharp/src/Google.Protobuf.Conformance/Program.cs
+++ b/csharp/src/Google.Protobuf.Conformance/Program.cs
@@ -29,6 +29,7 @@
                 ProtobufTestMessages.Proto3.TestAllTypesProto3.Descriptor,
                 ProtobufTestMessages.Proto2.TestAllTypesProto2.Descriptor,
                 ProtobufTestMessages.Editions.TestAllTypesEdition2023.Descriptor,
+                ProtobufTestMessages.EditionUnstable.TestAllTypesEditionUnstable.Descriptor,
                 ProtobufTestMessages.Editions.Proto3.TestAllTypesProto3.Descriptor,
                 ProtobufTestMessages.Editions.Proto2.TestAllTypesProto2.Descriptor);
 
@@ -83,6 +84,9 @@
               ProtobufTestMessages.Editions.TestMessagesEdition2023Extensions.DelimitedExt,
               ProtobufTestMessages.Editions.TestMessagesEdition2023Extensions.GroupLikeType
             };
+            ExtensionRegistry editionUnstableExtensionRegistry = new ExtensionRegistry {
+              ProtobufTestMessages.EditionUnstable.TestMessagesEditionUnstableExtensions.ExtensionInt32,
+            };
             IMessage message;
             try
             {
@@ -102,6 +106,9 @@
                           "protobuf_test_messages.editions.TestAllTypesEdition2023" =>
                               parser.Parse<ProtobufTestMessages.Editions.TestAllTypesEdition2023>(
                                   request.JsonPayload),
+                          "protobuf_test_messages.edition_unstable.TestAllTypesEditionUnstable" =>
+                              parser.Parse<ProtobufTestMessages.EditionUnstable.TestAllTypesEditionUnstable>(
+                                  request.JsonPayload),
                           "protobuf_test_messages.editions.proto2.TestAllTypesProto2" =>
                               parser.Parse<ProtobufTestMessages.Editions.Proto2.TestAllTypesProto2>(
                                   request.JsonPayload),
@@ -126,6 +133,10 @@
                             ProtobufTestMessages.Editions.TestAllTypesEdition2023.Parser
                                 .WithExtensionRegistry(edition2023ExtensionRegistry)
                                 .ParseFrom(request.ProtobufPayload),
+                        "protobuf_test_messages.edition_unstable.TestAllTypesEditionUnstable" =>
+                            ProtobufTestMessages.EditionUnstable.TestAllTypesEditionUnstable.Parser
+                                .WithExtensionRegistry(editionUnstableExtensionRegistry)
+                                .ParseFrom(request.ProtobufPayload),
                         "protobuf_test_messages.editions.proto2.TestAllTypesProto2" =>
                             ProtobufTestMessages.Editions.Proto2.TestAllTypesProto2.Parser
                                 .WithExtensionRegistry(editionsProto2ExtensionRegistry)
diff --git a/csharp/src/Google.Protobuf/Reflection/FeatureSetDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FeatureSetDescriptor.cs
index 0b55005..3944e9c 100644
--- a/csharp/src/Google.Protobuf/Reflection/FeatureSetDescriptor.cs
+++ b/csharp/src/Google.Protobuf/Reflection/FeatureSetDescriptor.cs
@@ -45,7 +45,7 @@
         // based on that.
         var supportedEditions = ((Edition[]) Enum.GetValues(typeof(Edition)))
             .OrderBy(x => x)
-            .Where(e => e >= featureSetDefaults.MinimumEdition && e <= featureSetDefaults.MaximumEdition);
+            .Where(e => e >= featureSetDefaults.MinimumEdition && (e <= featureSetDefaults.MaximumEdition || e == Edition.Unstable));
 
         // We assume the embedded defaults will always contain "legacy".
         var currentDescriptor = MaybeCreateDescriptor(Edition.Legacy);
diff --git a/java/core/src/main/java/com/google/protobuf/Descriptors.java b/java/core/src/main/java/com/google/protobuf/Descriptors.java
index 63d0667..87821eb 100644
--- a/java/core/src/main/java/com/google/protobuf/Descriptors.java
+++ b/java/core/src/main/java/com/google/protobuf/Descriptors.java
@@ -120,7 +120,8 @@
               + javaEditionDefaults.getMinimumEdition()
               + "!");
     }
-    if (edition.getNumber() > javaEditionDefaults.getMaximumEdition().getNumber()) {
+    if (edition.getNumber() > javaEditionDefaults.getMaximumEdition().getNumber()
+        && edition != Edition.EDITION_UNSTABLE) {
       throw new IllegalArgumentException(
           "Edition "
               + edition
diff --git a/python/google/protobuf/descriptor_pool.py b/python/google/protobuf/descriptor_pool.py
index 23ce885..a838c50 100644
--- a/python/google/protobuf/descriptor_pool.py
+++ b/python/google/protobuf/descriptor_pool.py
@@ -738,7 +738,10 @@
               ),
           )
       )
-    if edition > self._edition_defaults.maximum_edition:
+    if (
+        edition > self._edition_defaults.maximum_edition
+        and edition != descriptor_pb2.EDITION_UNSTABLE
+    ):
       raise TypeError(
           'Edition %s is later than the maximum supported edition %s!'
           % (
diff --git a/src/google/protobuf/compiler/code_generator.cc b/src/google/protobuf/compiler/code_generator.cc
index e943c54..4657417 100644
--- a/src/google/protobuf/compiler/code_generator.cc
+++ b/src/google/protobuf/compiler/code_generator.cc
@@ -106,7 +106,10 @@
 bool CanSkipEditionCheck(absl::string_view filename) {
   return absl::StartsWith(filename, "google/protobuf/") ||
          absl::StartsWith(filename, "upb/") ||
-         absl::StartsWith(filename, "com/google/protobuf/");
+         absl::StartsWith(filename, "com/google/protobuf/") ||
+         absl::StartsWith(filename, "conformance/test_protos/") ||
+         // TODO: Remove this once internal proto rule is resolved.
+         absl::StartsWith(filename, "test_messages_edition_unstable");
 }
 
 }  // namespace compiler
diff --git a/upb/reflection/file_def.c b/upb/reflection/file_def.c
index 1ba5a20..c17c5ae 100644
--- a/upb/reflection/file_def.c
+++ b/upb/reflection/file_def.c
@@ -220,7 +220,7 @@
                          upb_FileDef_EditionName(min));
     return NULL;
   }
-  if (edition > max) {
+  if (edition > max && edition != UPB_DESC(EDITION_UNSTABLE)) {
     _upb_DefBuilder_Errf(ctx,
                          "Edition %s is later than the maximum edition %s "
                          "given in the defaults",