Removed `syntax` and added `has_presence`/`is_packed`.
diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c
index 8761b84..9141c7b 100644
--- a/ruby/ext/google/protobuf_c/defs.c
+++ b/ruby/ext/google/protobuf_c/defs.c
@@ -489,7 +489,7 @@
  * call-seq:
  *     FileDescriptor.new => file
  *
- * Returns a new file descriptor. The syntax must be set before it's passed
+ * Returns a new file descriptor. May
  * to a builder.
  */
 static VALUE FileDescriptor_initialize(VALUE _self, VALUE cookie,
@@ -521,28 +521,6 @@
 
 /*
  * call-seq:
- *     FileDescriptor.syntax => syntax
- *
- * Returns this file descriptors syntax.
- *
- * Valid syntax versions are:
- *     :proto2 or :proto3.
- */
-static VALUE FileDescriptor_syntax(VALUE _self) {
-  FileDescriptor* self = ruby_to_FileDescriptor(_self);
-
-  switch (upb_FileDef_Syntax(self->filedef)) {
-    case kUpb_Syntax_Proto3:
-      return ID2SYM(rb_intern("proto3"));
-    case kUpb_Syntax_Proto2:
-      return ID2SYM(rb_intern("proto2"));
-    default:
-      return Qnil;
-  }
-}
-
-/*
- * call-seq:
  *     FileDescriptor.options => options
  *
  * Returns the `FileOptions` for this `FileDescriptor`.
@@ -564,7 +542,6 @@
   rb_define_alloc_func(klass, FileDescriptor_alloc);
   rb_define_method(klass, "initialize", FileDescriptor_initialize, 3);
   rb_define_method(klass, "name", FileDescriptor_name, 0);
-  rb_define_method(klass, "syntax", FileDescriptor_syntax, 0);
   rb_define_method(klass, "options", FileDescriptor_options, 0);
   rb_gc_register_address(&cFileDescriptor);
   cFileDescriptor = klass;
@@ -738,6 +715,28 @@
 
 /*
  * call-seq:
+ *     FieldDescriptor.has_presence? => bool
+ *
+ * Returns whether this field tracks presence.
+ */
+static VALUE FieldDescriptor_has_presence(VALUE _self) {
+  FieldDescriptor* self = ruby_to_FieldDescriptor(_self);
+  return upb_FieldDef_HasPresence(self->fielddef) ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq:
+ *     FieldDescriptor.is_packed? => bool
+ *
+ * Returns whether this is a repeated field that uses packed encoding.
+ */
+static VALUE FieldDescriptor_is_packed(VALUE _self) {
+  FieldDescriptor* self = ruby_to_FieldDescriptor(_self);
+  return upb_FieldDef_IsPacked(self->fielddef) ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq:
  *     FieldDescriptor.json_name => json_name
  *
  * Returns this field's json_name, as a Ruby string, or nil if not yet set.
@@ -943,6 +942,8 @@
   rb_define_method(klass, "name", FieldDescriptor_name, 0);
   rb_define_method(klass, "type", FieldDescriptor__type, 0);
   rb_define_method(klass, "default", FieldDescriptor_default, 0);
+  rb_define_method(klass, "has_presence?", FieldDescriptor_has_presence, 0);
+  rb_define_method(klass, "is_packed?", FieldDescriptor_is_packed, 0);
   rb_define_method(klass, "json_name", FieldDescriptor_json_name, 0);
   rb_define_method(klass, "label", FieldDescriptor_label, 0);
   rb_define_method(klass, "number", FieldDescriptor_number, 0);
@@ -1165,6 +1166,17 @@
 
 /*
  * call-seq:
+ *     EnumDescriptor.is_closed? => bool
+ *
+ * Returns whether this enum is open or closed.
+ */
+static VALUE EnumDescriptor_is_closed(VALUE _self) {
+  EnumDescriptor* self = ruby_to_EnumDescriptor(_self);
+  return upb_EnumDef_IsClosed(self->enumdef) ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq:
  *     EnumDescriptor.name => name
  *
  * Returns the name of this enum type.
@@ -1275,6 +1287,7 @@
   rb_define_method(klass, "each", EnumDescriptor_each, 0);
   rb_define_method(klass, "enummodule", EnumDescriptor_enummodule, 0);
   rb_define_method(klass, "file_descriptor", EnumDescriptor_file_descriptor, 0);
+  rb_define_method(klass, "is_closed?", EnumDescriptor_is_closed, 0);
   rb_define_method(klass, "options", EnumDescriptor_options, 0);
   rb_include_module(klass, rb_mEnumerable);
   rb_gc_register_address(&cEnumDescriptor);
diff --git a/ruby/lib/google/protobuf/ffi/descriptor.rb b/ruby/lib/google/protobuf/ffi/descriptor.rb
index 3eb1086..3175d41 100644
--- a/ruby/lib/google/protobuf/ffi/descriptor.rb
+++ b/ruby/lib/google/protobuf/ffi/descriptor.rb
@@ -158,7 +158,6 @@
       attach_function :oneof_count,          :upb_MessageDef_OneofCount,              [Descriptor], :int
       attach_function :message_options,      :Descriptor_serialized_options,          [Descriptor, :pointer, Internal::Arena], :pointer
       attach_function :get_well_known_type,  :upb_MessageDef_WellKnownType,           [Descriptor], WellKnown
-      attach_function :message_def_syntax,   :upb_MessageDef_Syntax,                  [Descriptor], Syntax
       attach_function :find_msg_def_by_name, :upb_MessageDef_FindByNameWithSize,      [Descriptor, :string, :size_t, :FieldDefPointer, :OneofDefPointer], :bool
     end
   end
diff --git a/ruby/lib/google/protobuf/ffi/field_descriptor.rb b/ruby/lib/google/protobuf/ffi/field_descriptor.rb
index b15c910..cbc1e82 100644
--- a/ruby/lib/google/protobuf/ffi/field_descriptor.rb
+++ b/ruby/lib/google/protobuf/ffi/field_descriptor.rb
@@ -156,6 +156,14 @@
         @has_presence ||= Google::Protobuf::FFI.get_has_presence(self)
       end
 
+      ##
+      # Tests if this is a repeated field that uses packed encoding.
+      #
+      # @return [Boolean] True iff this field is packed
+      def is_packed?
+        @is_packed ||= Google::Protobuf::FFI.get_is_packed(self)
+      end
+
       # @param msg [Google::Protobuf::Message]
       def clear(msg)
         if msg.class.descriptor != Google::Protobuf::FFI.get_containing_message_def(self)
@@ -304,6 +312,7 @@
       attach_function :get_default,                :upb_FieldDef_Default,               [FieldDescriptor], MessageValue.by_value
       attach_function :get_subtype_as_enum,        :upb_FieldDef_EnumSubDef,            [FieldDescriptor], EnumDescriptor
       attach_function :get_has_presence,           :upb_FieldDef_HasPresence,           [FieldDescriptor], :bool
+      attach_function :get_is_packed,              :upb_FieldDef_IsPacked,              [FieldDescriptor], :bool
       attach_function :is_map,                     :upb_FieldDef_IsMap,                 [FieldDescriptor], :bool
       attach_function :is_repeated,                :upb_FieldDef_IsRepeated,            [FieldDescriptor], :bool
       attach_function :is_sub_message,             :upb_FieldDef_IsSubMessage,          [FieldDescriptor], :bool
diff --git a/ruby/lib/google/protobuf/ffi/file_descriptor.rb b/ruby/lib/google/protobuf/ffi/file_descriptor.rb
index f1da9f7..2035b98 100644
--- a/ruby/lib/google/protobuf/ffi/file_descriptor.rb
+++ b/ruby/lib/google/protobuf/ffi/file_descriptor.rb
@@ -10,7 +10,6 @@
     class FFI
       # FileDescriptor
       attach_function :file_def_name,   :upb_FileDef_Name,   [:FileDef], :string
-      attach_function :file_def_syntax, :upb_FileDef_Syntax, [:FileDef], Syntax
       attach_function :file_def_pool,   :upb_FileDef_Pool,   [:FileDef], :DefPool
       attach_function :file_options,    :FileDescriptor_serialized_options,  [:FileDef, :pointer, Internal::Arena], :pointer
     end
@@ -31,17 +30,6 @@
         "#{self.class.name}: #{name}"
       end
 
-      def syntax
-        case Google::Protobuf::FFI.file_def_syntax(@file_def)
-        when :Proto3
-          :proto3
-        when :Proto2
-          :proto2
-        else
-          nil
-        end
-      end
-
       def name
         Google::Protobuf::FFI.file_def_name(@file_def)
       end
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyFileDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyFileDescriptor.java
index db9580d..ff3c6d9 100644
--- a/ruby/src/main/java/com/google/protobuf/jruby/RubyFileDescriptor.java
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyFileDescriptor.java
@@ -86,27 +86,6 @@
     return name == null ? context.nil : context.runtime.newString(name);
   }
 
-  /*
-   * call-seq:
-   *     FileDescriptor.syntax => syntax
-   *
-   * Returns this file descriptors syntax.
-   *
-   * Valid syntax versions are:
-   *     :proto2 or :proto3.
-   */
-  @JRubyMethod(name = "syntax")
-  public IRubyObject getSyntax(ThreadContext context) {
-    switch (LegacyFileDescriptor.getSyntax(fileDescriptor)) {
-      case PROTO2:
-        return context.runtime.newSymbol("proto2");
-      case PROTO3:
-        return context.runtime.newSymbol("proto3");
-      default:
-        return context.nil;
-    }
-  }
-
   @JRubyMethod
   public IRubyObject options(ThreadContext context) {
     RubyDescriptorPool pool = (RubyDescriptorPool) RubyDescriptorPool.generatedPool(null, null);
diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb
index 46e3fec..e6cd08b 100755
--- a/ruby/tests/basic.rb
+++ b/ruby/tests/basic.rb
@@ -553,12 +553,10 @@
       file_descriptor = TestMessage.descriptor.file_descriptor
       refute_nil file_descriptor
       assert_equal "basic_test.proto", file_descriptor.name
-      assert_equal :proto3, file_descriptor.syntax
 
       file_descriptor = TestEnum.descriptor.file_descriptor
       refute_nil file_descriptor
       assert_equal "basic_test.proto", file_descriptor.name
-      assert_equal :proto3, file_descriptor.syntax
     end
 
     def test_map_freeze
@@ -639,6 +637,17 @@
       end
     end
 
+    def test_has_presence
+      assert_true TestMessage.descriptor.lookup("optional_int32").has_presence?
+      assert_false TestMessage.descriptor.lookup("repeated_int32").has_presence?
+      assert_false TestSingularFields.descriptor.lookup("singular_int32").has_presence?
+    end
+
+    def test_is_packed
+      assert_false TestMessage.descriptor.lookup("optional_int32").is_packed?
+      assert_true TestMessage.descriptor.lookup("repeated_int32").is_packed?
+    end
+
     def test_file_descriptor_options
       file_descriptor = TestMessage.descriptor.file_descriptor
 
diff --git a/ruby/tests/basic_proto2.rb b/ruby/tests/basic_proto2.rb
index 3864756..e9fce57 100755
--- a/ruby/tests/basic_proto2.rb
+++ b/ruby/tests/basic_proto2.rb
@@ -231,12 +231,10 @@
       file_descriptor = TestMessage.descriptor.file_descriptor
       refute_nil file_descriptor
       assert_equal "basic_test_proto2.proto", file_descriptor.name
-      assert_equal :proto2, file_descriptor.syntax
 
       file_descriptor = TestEnum.descriptor.file_descriptor
       refute_nil file_descriptor
       assert_equal "basic_test_proto2.proto", file_descriptor.name
-      assert_equal :proto2, file_descriptor.syntax
     end
 
     def test_oneof_fields_respond_to? # regression test for issue 9202
@@ -254,6 +252,11 @@
       refute msg.has_d?
     end
 
+    def test_is_packed
+      assert_false TestMessage.descriptor.lookup("optional_int32").is_packed?
+      assert_false TestMessage.descriptor.lookup("repeated_int32").is_packed?
+    end
+
     def test_extension
       message = TestExtensions.new
       extension = Google::Protobuf::DescriptorPool.generated_pool.lookup 'basic_test_proto2.optional_int32_extension'
diff --git a/upb/reflection/field_def.h b/upb/reflection/field_def.h
index 35c3e70..b7f3427 100644
--- a/upb/reflection/field_def.h
+++ b/upb/reflection/field_def.h
@@ -42,7 +42,7 @@
 bool upb_FieldDef_IsExtension(const upb_FieldDef* f);
 UPB_API bool upb_FieldDef_IsMap(const upb_FieldDef* f);
 bool upb_FieldDef_IsOptional(const upb_FieldDef* f);
-bool upb_FieldDef_IsPacked(const upb_FieldDef* f);
+UPB_API bool upb_FieldDef_IsPacked(const upb_FieldDef* f);
 bool upb_FieldDef_IsPrimitive(const upb_FieldDef* f);
 UPB_API bool upb_FieldDef_IsRepeated(const upb_FieldDef* f);
 bool upb_FieldDef_IsRequired(const upb_FieldDef* f);