Merge branch 'unknown-opt' into required
diff --git a/benchmarks/benchmark.cc b/benchmarks/benchmark.cc
index 60e3b53..b16cf47 100644
--- a/benchmarks/benchmark.cc
+++ b/benchmarks/benchmark.cc
@@ -163,7 +163,7 @@
     upb_benchmark_FileDescriptorProto* set =
         upb_benchmark_FileDescriptorProto_parse_ex(
             descriptor.data, descriptor.size, NULL,
-            Copy == Alias ? UPB_DECODE_ALIAS : 0, arena);
+            Copy == Alias ? kUpb_DecodeOption_AliasString : 0, arena);
     if (!set) {
       printf("Failed to parse.\n");
       exit(1);
diff --git a/cmake/google/protobuf/descriptor.upb.c b/cmake/google/protobuf/descriptor.upb.c
index 007d651..3782fb4 100644
--- a/cmake/google/protobuf/descriptor.upb.c
+++ b/cmake/google/protobuf/descriptor.upb.c
@@ -23,7 +23,7 @@
 const upb_msglayout google_protobuf_FileDescriptorSet_msginit = {
   &google_protobuf_FileDescriptorSet_submsgs[0],
   &google_protobuf_FileDescriptorSet__fields[0],
-  0, UPB_SIZE(8, 8), 1, _UPB_MSGEXT_NONE, 1, 255,
+  UPB_SIZE(8, 8), 1, _UPB_MSGEXT_NONE, 1, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_FileDescriptorProto_submsgs[6] = {
@@ -53,7 +53,7 @@
 const upb_msglayout google_protobuf_FileDescriptorProto_msginit = {
   &google_protobuf_FileDescriptorProto_submsgs[0],
   &google_protobuf_FileDescriptorProto__fields[0],
-  0, UPB_SIZE(64, 128), 12, _UPB_MSGEXT_NONE, 12, 255,
+  UPB_SIZE(64, 128), 12, _UPB_MSGEXT_NONE, 12, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_DescriptorProto_submsgs[7] = {
@@ -82,7 +82,7 @@
 const upb_msglayout google_protobuf_DescriptorProto_msginit = {
   &google_protobuf_DescriptorProto_submsgs[0],
   &google_protobuf_DescriptorProto__fields[0],
-  0, UPB_SIZE(48, 96), 10, _UPB_MSGEXT_NONE, 10, 255,
+  UPB_SIZE(48, 96), 10, _UPB_MSGEXT_NONE, 10, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_DescriptorProto_ExtensionRange_submsgs[1] = {
@@ -98,7 +98,7 @@
 const upb_msglayout google_protobuf_DescriptorProto_ExtensionRange_msginit = {
   &google_protobuf_DescriptorProto_ExtensionRange_submsgs[0],
   &google_protobuf_DescriptorProto_ExtensionRange__fields[0],
-  0, UPB_SIZE(16, 24), 3, _UPB_MSGEXT_NONE, 3, 255,
+  UPB_SIZE(16, 24), 3, _UPB_MSGEXT_NONE, 3, 255, 0,
 };
 
 static const upb_msglayout_field google_protobuf_DescriptorProto_ReservedRange__fields[2] = {
@@ -109,7 +109,7 @@
 const upb_msglayout google_protobuf_DescriptorProto_ReservedRange_msginit = {
   NULL,
   &google_protobuf_DescriptorProto_ReservedRange__fields[0],
-  0, UPB_SIZE(16, 16), 2, _UPB_MSGEXT_NONE, 2, 255,
+  UPB_SIZE(16, 16), 2, _UPB_MSGEXT_NONE, 2, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_ExtensionRangeOptions_submsgs[1] = {
@@ -123,7 +123,7 @@
 const upb_msglayout google_protobuf_ExtensionRangeOptions_msginit = {
   &google_protobuf_ExtensionRangeOptions_submsgs[0],
   &google_protobuf_ExtensionRangeOptions__fields[0],
-  0, UPB_SIZE(8, 8), 1, _UPB_MSGEXT_EXTENDABLE, 0, 255,
+  UPB_SIZE(8, 8), 1, _UPB_MSGEXT_EXTENDABLE, 0, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_FieldDescriptorProto_submsgs[3] = {
@@ -149,7 +149,7 @@
 const upb_msglayout google_protobuf_FieldDescriptorProto_msginit = {
   &google_protobuf_FieldDescriptorProto_submsgs[0],
   &google_protobuf_FieldDescriptorProto__fields[0],
-  0, UPB_SIZE(72, 112), 11, _UPB_MSGEXT_NONE, 10, 255,
+  UPB_SIZE(72, 112), 11, _UPB_MSGEXT_NONE, 10, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_OneofDescriptorProto_submsgs[1] = {
@@ -164,7 +164,7 @@
 const upb_msglayout google_protobuf_OneofDescriptorProto_msginit = {
   &google_protobuf_OneofDescriptorProto_submsgs[0],
   &google_protobuf_OneofDescriptorProto__fields[0],
-  0, UPB_SIZE(16, 32), 2, _UPB_MSGEXT_NONE, 2, 255,
+  UPB_SIZE(16, 32), 2, _UPB_MSGEXT_NONE, 2, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_EnumDescriptorProto_submsgs[3] = {
@@ -184,7 +184,7 @@
 const upb_msglayout google_protobuf_EnumDescriptorProto_msginit = {
   &google_protobuf_EnumDescriptorProto_submsgs[0],
   &google_protobuf_EnumDescriptorProto__fields[0],
-  0, UPB_SIZE(32, 64), 5, _UPB_MSGEXT_NONE, 5, 255,
+  UPB_SIZE(32, 64), 5, _UPB_MSGEXT_NONE, 5, 255, 0,
 };
 
 static const upb_msglayout_field google_protobuf_EnumDescriptorProto_EnumReservedRange__fields[2] = {
@@ -195,7 +195,7 @@
 const upb_msglayout google_protobuf_EnumDescriptorProto_EnumReservedRange_msginit = {
   NULL,
   &google_protobuf_EnumDescriptorProto_EnumReservedRange__fields[0],
-  0, UPB_SIZE(16, 16), 2, _UPB_MSGEXT_NONE, 2, 255,
+  UPB_SIZE(16, 16), 2, _UPB_MSGEXT_NONE, 2, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_EnumValueDescriptorProto_submsgs[1] = {
@@ -211,7 +211,7 @@
 const upb_msglayout google_protobuf_EnumValueDescriptorProto_msginit = {
   &google_protobuf_EnumValueDescriptorProto_submsgs[0],
   &google_protobuf_EnumValueDescriptorProto__fields[0],
-  0, UPB_SIZE(24, 32), 3, _UPB_MSGEXT_NONE, 3, 255,
+  UPB_SIZE(24, 32), 3, _UPB_MSGEXT_NONE, 3, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_ServiceDescriptorProto_submsgs[2] = {
@@ -228,7 +228,7 @@
 const upb_msglayout google_protobuf_ServiceDescriptorProto_msginit = {
   &google_protobuf_ServiceDescriptorProto_submsgs[0],
   &google_protobuf_ServiceDescriptorProto__fields[0],
-  0, UPB_SIZE(24, 48), 3, _UPB_MSGEXT_NONE, 3, 255,
+  UPB_SIZE(24, 48), 3, _UPB_MSGEXT_NONE, 3, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_MethodDescriptorProto_submsgs[1] = {
@@ -247,7 +247,7 @@
 const upb_msglayout google_protobuf_MethodDescriptorProto_msginit = {
   &google_protobuf_MethodDescriptorProto_submsgs[0],
   &google_protobuf_MethodDescriptorProto__fields[0],
-  0, UPB_SIZE(32, 64), 6, _UPB_MSGEXT_NONE, 6, 255,
+  UPB_SIZE(32, 64), 6, _UPB_MSGEXT_NONE, 6, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_FileOptions_submsgs[2] = {
@@ -282,7 +282,7 @@
 const upb_msglayout google_protobuf_FileOptions_msginit = {
   &google_protobuf_FileOptions_submsgs[0],
   &google_protobuf_FileOptions__fields[0],
-  0, UPB_SIZE(104, 192), 21, _UPB_MSGEXT_EXTENDABLE, 1, 255,
+  UPB_SIZE(104, 192), 21, _UPB_MSGEXT_EXTENDABLE, 1, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_MessageOptions_submsgs[1] = {
@@ -300,7 +300,7 @@
 const upb_msglayout google_protobuf_MessageOptions_msginit = {
   &google_protobuf_MessageOptions_submsgs[0],
   &google_protobuf_MessageOptions__fields[0],
-  0, UPB_SIZE(16, 16), 5, _UPB_MSGEXT_EXTENDABLE, 3, 255,
+  UPB_SIZE(16, 16), 5, _UPB_MSGEXT_EXTENDABLE, 3, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_FieldOptions_submsgs[3] = {
@@ -322,7 +322,7 @@
 const upb_msglayout google_protobuf_FieldOptions_msginit = {
   &google_protobuf_FieldOptions_submsgs[0],
   &google_protobuf_FieldOptions__fields[0],
-  0, UPB_SIZE(24, 24), 7, _UPB_MSGEXT_EXTENDABLE, 3, 255,
+  UPB_SIZE(24, 24), 7, _UPB_MSGEXT_EXTENDABLE, 3, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_OneofOptions_submsgs[1] = {
@@ -336,7 +336,7 @@
 const upb_msglayout google_protobuf_OneofOptions_msginit = {
   &google_protobuf_OneofOptions_submsgs[0],
   &google_protobuf_OneofOptions__fields[0],
-  0, UPB_SIZE(8, 8), 1, _UPB_MSGEXT_EXTENDABLE, 0, 255,
+  UPB_SIZE(8, 8), 1, _UPB_MSGEXT_EXTENDABLE, 0, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_EnumOptions_submsgs[1] = {
@@ -352,7 +352,7 @@
 const upb_msglayout google_protobuf_EnumOptions_msginit = {
   &google_protobuf_EnumOptions_submsgs[0],
   &google_protobuf_EnumOptions__fields[0],
-  0, UPB_SIZE(8, 16), 3, _UPB_MSGEXT_EXTENDABLE, 0, 255,
+  UPB_SIZE(8, 16), 3, _UPB_MSGEXT_EXTENDABLE, 0, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_EnumValueOptions_submsgs[1] = {
@@ -367,7 +367,7 @@
 const upb_msglayout google_protobuf_EnumValueOptions_msginit = {
   &google_protobuf_EnumValueOptions_submsgs[0],
   &google_protobuf_EnumValueOptions__fields[0],
-  0, UPB_SIZE(8, 16), 2, _UPB_MSGEXT_EXTENDABLE, 1, 255,
+  UPB_SIZE(8, 16), 2, _UPB_MSGEXT_EXTENDABLE, 1, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_ServiceOptions_submsgs[1] = {
@@ -382,7 +382,7 @@
 const upb_msglayout google_protobuf_ServiceOptions_msginit = {
   &google_protobuf_ServiceOptions_submsgs[0],
   &google_protobuf_ServiceOptions__fields[0],
-  0, UPB_SIZE(8, 16), 2, _UPB_MSGEXT_EXTENDABLE, 0, 255,
+  UPB_SIZE(8, 16), 2, _UPB_MSGEXT_EXTENDABLE, 0, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_MethodOptions_submsgs[2] = {
@@ -399,7 +399,7 @@
 const upb_msglayout google_protobuf_MethodOptions_msginit = {
   &google_protobuf_MethodOptions_submsgs[0],
   &google_protobuf_MethodOptions__fields[0],
-  0, UPB_SIZE(16, 24), 3, _UPB_MSGEXT_EXTENDABLE, 0, 255,
+  UPB_SIZE(16, 24), 3, _UPB_MSGEXT_EXTENDABLE, 0, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_UninterpretedOption_submsgs[1] = {
@@ -419,7 +419,7 @@
 const upb_msglayout google_protobuf_UninterpretedOption_msginit = {
   &google_protobuf_UninterpretedOption_submsgs[0],
   &google_protobuf_UninterpretedOption__fields[0],
-  0, UPB_SIZE(64, 96), 7, _UPB_MSGEXT_NONE, 0, 255,
+  UPB_SIZE(64, 96), 7, _UPB_MSGEXT_NONE, 0, 255, 0,
 };
 
 static const upb_msglayout_field google_protobuf_UninterpretedOption_NamePart__fields[2] = {
@@ -430,7 +430,7 @@
 const upb_msglayout google_protobuf_UninterpretedOption_NamePart_msginit = {
   NULL,
   &google_protobuf_UninterpretedOption_NamePart__fields[0],
-  6, UPB_SIZE(16, 32), 2, _UPB_MSGEXT_NONE, 2, 255,
+  UPB_SIZE(16, 32), 2, _UPB_MSGEXT_NONE, 2, 255, 2,
 };
 
 static const upb_msglayout_sub google_protobuf_SourceCodeInfo_submsgs[1] = {
@@ -444,7 +444,7 @@
 const upb_msglayout google_protobuf_SourceCodeInfo_msginit = {
   &google_protobuf_SourceCodeInfo_submsgs[0],
   &google_protobuf_SourceCodeInfo__fields[0],
-  0, UPB_SIZE(8, 8), 1, _UPB_MSGEXT_NONE, 1, 255,
+  UPB_SIZE(8, 8), 1, _UPB_MSGEXT_NONE, 1, 255, 0,
 };
 
 static const upb_msglayout_field google_protobuf_SourceCodeInfo_Location__fields[5] = {
@@ -458,7 +458,7 @@
 const upb_msglayout google_protobuf_SourceCodeInfo_Location_msginit = {
   NULL,
   &google_protobuf_SourceCodeInfo_Location__fields[0],
-  0, UPB_SIZE(32, 64), 5, _UPB_MSGEXT_NONE, 4, 255,
+  UPB_SIZE(32, 64), 5, _UPB_MSGEXT_NONE, 4, 255, 0,
 };
 
 static const upb_msglayout_sub google_protobuf_GeneratedCodeInfo_submsgs[1] = {
@@ -472,7 +472,7 @@
 const upb_msglayout google_protobuf_GeneratedCodeInfo_msginit = {
   &google_protobuf_GeneratedCodeInfo_submsgs[0],
   &google_protobuf_GeneratedCodeInfo__fields[0],
-  0, UPB_SIZE(8, 8), 1, _UPB_MSGEXT_NONE, 1, 255,
+  UPB_SIZE(8, 8), 1, _UPB_MSGEXT_NONE, 1, 255, 0,
 };
 
 static const upb_msglayout_field google_protobuf_GeneratedCodeInfo_Annotation__fields[4] = {
@@ -485,7 +485,7 @@
 const upb_msglayout google_protobuf_GeneratedCodeInfo_Annotation_msginit = {
   NULL,
   &google_protobuf_GeneratedCodeInfo_Annotation__fields[0],
-  0, UPB_SIZE(24, 48), 4, _UPB_MSGEXT_NONE, 4, 255,
+  UPB_SIZE(24, 48), 4, _UPB_MSGEXT_NONE, 4, 255, 0,
 };
 
 static const upb_msglayout *messages_layout[27] = {
@@ -518,21 +518,15 @@
   &google_protobuf_GeneratedCodeInfo_Annotation_msginit,
 };
 
-const upb_enumlayout google_protobuf_FieldDescriptorProto_Type_enuminit = {
-  NULL,
-  0x7fffeULL,
-  0,
-};
-
 const upb_enumlayout google_protobuf_FieldDescriptorProto_Label_enuminit = {
   NULL,
   0xeULL,
   0,
 };
 
-const upb_enumlayout google_protobuf_FileOptions_OptimizeMode_enuminit = {
+const upb_enumlayout google_protobuf_FieldDescriptorProto_Type_enuminit = {
   NULL,
-  0xeULL,
+  0x7fffeULL,
   0,
 };
 
@@ -548,6 +542,12 @@
   0,
 };
 
+const upb_enumlayout google_protobuf_FileOptions_OptimizeMode_enuminit = {
+  NULL,
+  0xeULL,
+  0,
+};
+
 const upb_enumlayout google_protobuf_MethodOptions_IdempotencyLevel_enuminit = {
   NULL,
   0x7ULL,
@@ -555,11 +555,11 @@
 };
 
 static const upb_enumlayout *enums_layout[6] = {
-  &google_protobuf_FieldDescriptorProto_Type_enuminit,
   &google_protobuf_FieldDescriptorProto_Label_enuminit,
-  &google_protobuf_FileOptions_OptimizeMode_enuminit,
+  &google_protobuf_FieldDescriptorProto_Type_enuminit,
   &google_protobuf_FieldOptions_CType_enuminit,
   &google_protobuf_FieldOptions_JSType_enuminit,
+  &google_protobuf_FileOptions_OptimizeMode_enuminit,
   &google_protobuf_MethodOptions_IdempotencyLevel_enuminit,
 };
 
diff --git a/cmake/google/protobuf/descriptor.upb.h b/cmake/google/protobuf/descriptor.upb.h
index f3d7963..c0bdc9e 100644
--- a/cmake/google/protobuf/descriptor.upb.h
+++ b/cmake/google/protobuf/descriptor.upb.h
@@ -103,6 +103,12 @@
 extern const upb_msglayout google_protobuf_GeneratedCodeInfo_Annotation_msginit;
 
 typedef enum {
+  google_protobuf_FieldDescriptorProto_LABEL_OPTIONAL = 1,
+  google_protobuf_FieldDescriptorProto_LABEL_REQUIRED = 2,
+  google_protobuf_FieldDescriptorProto_LABEL_REPEATED = 3
+} google_protobuf_FieldDescriptorProto_Label;
+
+typedef enum {
   google_protobuf_FieldDescriptorProto_TYPE_DOUBLE = 1,
   google_protobuf_FieldDescriptorProto_TYPE_FLOAT = 2,
   google_protobuf_FieldDescriptorProto_TYPE_INT64 = 3,
@@ -124,18 +130,6 @@
 } google_protobuf_FieldDescriptorProto_Type;
 
 typedef enum {
-  google_protobuf_FieldDescriptorProto_LABEL_OPTIONAL = 1,
-  google_protobuf_FieldDescriptorProto_LABEL_REQUIRED = 2,
-  google_protobuf_FieldDescriptorProto_LABEL_REPEATED = 3
-} google_protobuf_FieldDescriptorProto_Label;
-
-typedef enum {
-  google_protobuf_FileOptions_SPEED = 1,
-  google_protobuf_FileOptions_CODE_SIZE = 2,
-  google_protobuf_FileOptions_LITE_RUNTIME = 3
-} google_protobuf_FileOptions_OptimizeMode;
-
-typedef enum {
   google_protobuf_FieldOptions_STRING = 0,
   google_protobuf_FieldOptions_CORD = 1,
   google_protobuf_FieldOptions_STRING_PIECE = 2
@@ -148,17 +142,23 @@
 } google_protobuf_FieldOptions_JSType;
 
 typedef enum {
+  google_protobuf_FileOptions_SPEED = 1,
+  google_protobuf_FileOptions_CODE_SIZE = 2,
+  google_protobuf_FileOptions_LITE_RUNTIME = 3
+} google_protobuf_FileOptions_OptimizeMode;
+
+typedef enum {
   google_protobuf_MethodOptions_IDEMPOTENCY_UNKNOWN = 0,
   google_protobuf_MethodOptions_NO_SIDE_EFFECTS = 1,
   google_protobuf_MethodOptions_IDEMPOTENT = 2
 } google_protobuf_MethodOptions_IdempotencyLevel;
 
 
-extern const upb_enumlayout google_protobuf_FieldDescriptorProto_Type_enuminit;
 extern const upb_enumlayout google_protobuf_FieldDescriptorProto_Label_enuminit;
-extern const upb_enumlayout google_protobuf_FileOptions_OptimizeMode_enuminit;
+extern const upb_enumlayout google_protobuf_FieldDescriptorProto_Type_enuminit;
 extern const upb_enumlayout google_protobuf_FieldOptions_CType_enuminit;
 extern const upb_enumlayout google_protobuf_FieldOptions_JSType_enuminit;
+extern const upb_enumlayout google_protobuf_FileOptions_OptimizeMode_enuminit;
 extern const upb_enumlayout google_protobuf_MethodOptions_IdempotencyLevel_enuminit;
 
 /* google.protobuf.FileDescriptorSet */
diff --git a/tests/conformance_upb.c b/tests/conformance_upb.c
index 6330433..dd21c9f 100644
--- a/tests/conformance_upb.c
+++ b/tests/conformance_upb.c
@@ -88,7 +88,7 @@
   upb_strview proto =
       conformance_ConformanceRequest_protobuf_payload(c->request);
   if (upb_decode(proto.data, proto.size, msg, upb_msgdef_layout(m), c->arena) ==
-      UPB_DECODE_OK) {
+      kUpb_DecodeStatus_Ok) {
     return true;
   } else {
     static const char msg[] = "Parse error";
diff --git a/upb/bindings/lua/def.c b/upb/bindings/lua/def.c
index bf3fbca..d5366ea 100644
--- a/upb/bindings/lua/def.c
+++ b/upb/bindings/lua/def.c
@@ -908,7 +908,6 @@
 }
 
 static int lupb_symtab_tostring(lua_State *L) {
-  const upb_symtab *s = lupb_symtab_check(L, 1);
   lua_pushfstring(L, "<upb.SymbolTable>");
   return 1;
 }
diff --git a/upb/bindings/lua/msg.c b/upb/bindings/lua/msg.c
index 530d9b3..6e47c2f 100644
--- a/upb/bindings/lua/msg.c
+++ b/upb/bindings/lua/msg.c
@@ -961,7 +961,7 @@
   buf = upb_arena_malloc(arena, len);
   memcpy(buf, pb, len);
 
-  ok = _upb_decode(buf, len, msg, layout, NULL, UPB_DECODE_ALIAS, arena) == 0;
+  ok = _upb_decode(buf, len, msg, layout, NULL, kUpb_DecodeOption_AliasString, arena) == 0;
 
   if (!ok) {
     lua_pushstring(L, "Error decoding protobuf.");
diff --git a/upb/decode.c b/upb/decode.c
index b5997ff..8fa69d5 100644
--- a/upb/decode.c
+++ b/upb/decode.c
@@ -189,7 +189,7 @@
 static const char *decode_msg(upb_decstate *d, const char *ptr, upb_msg *msg,
                               const upb_msglayout *layout);
 
-UPB_NORETURN static void *decode_err(upb_decstate *d, upb_decodestatus status) {
+UPB_NORETURN static void *decode_err(upb_decstate *d, upb_DecodeStatus status) {
   UPB_LONGJMP(d->err, status);
 }
 
@@ -198,13 +198,13 @@
   return NULL;
 }
 static void decode_verifyutf8(upb_decstate *d, const char *buf, int len) {
-  if (!decode_verifyutf8_inl(buf, len)) decode_err(d, UPB_DECODE_BAD_UTF8);
+  if (!decode_verifyutf8_inl(buf, len)) decode_err(d, kUpb_DecodeStatus_BadUtf8);
 }
 
 static bool decode_reserve(upb_decstate *d, upb_array *arr, size_t elem) {
   bool need_realloc = arr->size - arr->len < elem;
   if (need_realloc && !_upb_array_realloc(arr, arr->len + elem, &d->arena)) {
-    decode_err(d, UPB_DECODE_OOM);
+    decode_err(d, kUpb_DecodeStatus_OutOfMemory);
   }
   return need_realloc;
 }
@@ -240,7 +240,7 @@
     return ptr + 1;
   } else {
     decode_vret res = decode_longvarint64(ptr, byte);
-    if (!res.ptr) return decode_err(d, UPB_DECODE_MALFORMED);
+    if (!res.ptr) return decode_err(d, kUpb_DecodeStatus_Malformed);
     *val = res.val;
     return res.ptr;
   }
@@ -258,7 +258,7 @@
     ptr = res.ptr;
     *val = res.val;
     if (!ptr || *val > UINT32_MAX || ptr - start > 5) {
-      return decode_err(d, UPB_DECODE_MALFORMED);
+      return decode_err(d, kUpb_DecodeStatus_Malformed);
     }
     return ptr;
   }
@@ -308,11 +308,11 @@
 
 static const char *decode_readstr(upb_decstate *d, const char *ptr, int size,
                                   upb_strview *str) {
-  if (d->options & UPB_DECODE_ALIAS) {
+  if (d->options & kUpb_DecodeOption_AliasString) {
     str->data = ptr;
   } else {
     char *data =  upb_arena_malloc(&d->arena, size);
-    if (!data) return decode_err(d, UPB_DECODE_OOM);
+    if (!data) return decode_err(d, kUpb_DecodeStatus_OutOfMemory);
     memcpy(data, ptr, size);
     str->data = data;
   }
@@ -325,11 +325,11 @@
                                     upb_msg *submsg, const upb_msglayout *subl,
                                     int size) {
   int saved_delta = decode_pushlimit(d, ptr, size);
-  if (--d->depth < 0) return decode_err(d, UPB_DECODE_MAXDEPTH_EXCEEDED);
+  if (--d->depth < 0) return decode_err(d, kUpb_DecodeStatus_MaxDepthExceeded);
   if (!decode_isdone(d, &ptr)) {
     ptr = decode_msg(d, ptr, submsg, subl);
   }
-  if (d->end_group != DECODE_NOGROUP) return decode_err(d, UPB_DECODE_MALFORMED);
+  if (d->end_group != DECODE_NOGROUP) return decode_err(d, kUpb_DecodeStatus_Malformed);
   decode_poplimit(d, ptr, saved_delta);
   d->depth++;
   return ptr;
@@ -348,12 +348,12 @@
 static const char *decode_group(upb_decstate *d, const char *ptr,
                                 upb_msg *submsg, const upb_msglayout *subl,
                                 uint32_t number) {
-  if (--d->depth < 0) return decode_err(d, UPB_DECODE_MAXDEPTH_EXCEEDED);
+  if (--d->depth < 0) return decode_err(d, kUpb_DecodeStatus_MaxDepthExceeded);
   if (decode_isdone(d, &ptr)) {
-    return decode_err(d, UPB_DECODE_MALFORMED);
+    return decode_err(d, kUpb_DecodeStatus_Malformed);
   }
   ptr = decode_msg(d, ptr, submsg, subl);
-  if (d->end_group != number) return decode_err(d, UPB_DECODE_MALFORMED);
+  if (d->end_group != number) return decode_err(d, kUpb_DecodeStatus_Malformed);
   d->end_group = DECODE_NOGROUP;
   d->depth++;
   return ptr;
@@ -398,7 +398,7 @@
   end = encode_varint32(v, end);
 
   if (!_upb_msg_addunknown(msg, buf, end - buf, &d->arena)) {
-    decode_err(d, UPB_DECODE_OOM);
+    decode_err(d, kUpb_DecodeStatus_OutOfMemory);
   }
 
   return false;
@@ -429,7 +429,6 @@
   return ptr;
 }
 
-#include <stdio.h>
 UPB_FORCEINLINE
 static const char *decode_fixed_packed(upb_decstate *d, const char *ptr,
                                        upb_array *arr, wireval *val,
@@ -439,7 +438,7 @@
   size_t count = val->size >> lg2;
   if ((val->size & mask) != 0) {
     // Length isn't a round multiple of elem size.
-    return decode_err(d, UPB_DECODE_MALFORMED);
+    return decode_err(d, kUpb_DecodeStatus_Malformed);
   }
   decode_reserve(d, arr, count);
   void *mem = UPB_PTR_AT(_upb_array_ptr(arr), arr->len << lg2, void);
@@ -513,7 +512,7 @@
   } else {
     size_t lg2 = desctype_to_elem_size_lg2[field->descriptortype];
     arr = _upb_array_new(&d->arena, 4, lg2);
-    if (!arr) return decode_err(d, UPB_DECODE_OOM);
+    if (!arr) return decode_err(d, kUpb_DecodeStatus_OutOfMemory);
     *arrp = arr;
   }
 
@@ -551,7 +550,6 @@
     case OP_FIXPCK_LG2(3):
       return decode_fixed_packed(d, ptr, arr, val, field,
                                  op - OP_FIXPCK_LG2(0));
-        return decode_err(d, UPB_DECODE_MALFORMED); 
     case OP_VARPCK_LG2(0):
     case OP_VARPCK_LG2(2):
     case OP_VARPCK_LG2(3):
@@ -662,7 +660,7 @@
   }
 
   return ptr;
-} 
+}
 
 UPB_FORCEINLINE
 static bool decode_checkrequired(upb_decstate *d, const upb_msg *msg,
@@ -834,7 +832,7 @@
     default:
       break;
   }
-  return decode_err(d, UPB_DECODE_MALFORMED);
+  return decode_err(d, kUpb_DecodeStatus_Malformed);
 }
 
 UPB_FORCEINLINE
@@ -848,7 +846,7 @@
   if (UPB_UNLIKELY(mode & _UPB_MODE_IS_EXTENSION)) {
     const upb_msglayout_ext *ext_layout = (const upb_msglayout_ext*)field;
     upb_msg_ext *ext = _upb_msg_getorcreateext(msg, ext_layout, &d->arena);
-        if (UPB_UNLIKELY(!ext)) return decode_err(d, UPB_DECODE_OOM);
+        if (UPB_UNLIKELY(!ext)) return decode_err(d, kUpb_DecodeStatus_OutOfMemory);
     msg = &ext->data;
     subs = &ext->ext->sub;
   }
@@ -865,26 +863,54 @@
   }
 }
 
-UPB_FORCEINLINE
+static const char *decode_reverse_skip_varint(const char *ptr, uint64_t val) {
+  uint64_t seen = 0;
+  do {
+    ptr--;
+    seen <<= 7;
+    seen |= *ptr & 0x7f;
+  } while (seen != val);
+  return ptr;
+}
+
 static const char *decode_unknown(upb_decstate *d, const char *ptr,
                                   upb_msg *msg, int field_number, int wire_type,
-                                  wireval val, const char **field_start) {
-  if (field_number == 0) return decode_err(d, UPB_DECODE_MALFORMED);
+                                  wireval val) {
+  if (field_number == 0) return decode_err(d, kUpb_DecodeStatus_Malformed);
 
+  const char *start = ptr;
   if (wire_type == UPB_WIRE_TYPE_DELIMITED) ptr += val.size;
   if (msg) {
+    switch (wire_type) {
+      case UPB_WIRE_TYPE_VARINT:
+      case UPB_WIRE_TYPE_DELIMITED:
+        start--;
+        while (start[-1] & 0x80) start--;
+        break;
+      case UPB_WIRE_TYPE_32BIT:
+        start -= 4;
+        break;
+      case UPB_WIRE_TYPE_64BIT:
+        start -= 8;
+        break;
+      default:
+        break;
+    }
+
+    assert(start == d->debug_valstart);
+    start = decode_reverse_skip_varint(start, (field_number << 3) | wire_type);
+    assert(start == d->debug_tagstart);
+
     if (wire_type == UPB_WIRE_TYPE_START_GROUP) {
-      d->unknown = *field_start;
+      d->unknown = start;
       d->unknown_msg = msg;
       ptr = decode_group(d, ptr, NULL, NULL, field_number);
+      start = d->unknown;
       d->unknown_msg = NULL;
-      *field_start = d->unknown;
-      // XXX: pointer could have flipped during decoding the group,
-      // ptr - d->unknown below will be borked.
+      d->unknown = NULL;
     }
-    if (!_upb_msg_addunknown(msg, *field_start, ptr - *field_start,
-                             &d->arena)) {
-      return decode_err(d, UPB_DECODE_OOM);
+    if (!_upb_msg_addunknown(msg, start, ptr - start, &d->arena)) {
+      return decode_err(d, kUpb_DecodeStatus_OutOfMemory);
     }
   } else if (wire_type == UPB_WIRE_TYPE_START_GROUP) {
     ptr = decode_group(d, ptr, NULL, NULL, field_number);
@@ -897,7 +923,6 @@
                               const upb_msglayout *layout) {
   int last_field_index = 0;
   while (true) {
-    const char *field_start = ptr;
     uint32_t tag;
     const upb_msglayout_field *field;
     int field_number;
@@ -905,11 +930,19 @@
     wireval val;
     int op;
 
+#ifndef NDEBUG
+    d->debug_tagstart = ptr;
+#endif
+
     UPB_ASSERT(ptr < d->limit_ptr);
     ptr = decode_tag(d, ptr, &tag);
     field_number = tag >> 3;
     wire_type = tag & 7;
 
+#ifndef NDEBUG
+    d->debug_valstart = ptr;
+#endif
+
     if (wire_type == UPB_WIRE_TYPE_END_GROUP) {
       d->end_group = field_number;
       return ptr;
@@ -923,8 +956,7 @@
     } else {
       switch (op) {
         case OP_UNKNOWN:
-          ptr = decode_unknown(d, ptr, msg, field_number, wire_type, val,
-                               &field_start);
+          ptr = decode_unknown(d, ptr, msg, field_number, wire_type, val);
           break;
         case OP_MSGSET_ITEM:
           ptr = decode_msgset(d, ptr, msg, layout);
@@ -942,7 +974,7 @@
     if (decode_tryfastdispatch(d, &ptr, msg, layout)) break;
   }
 
-  if (false && UPB_UNLIKELY(d->options & UPB_CHECK_REQUIRED) &&
+  if (UPB_UNLIKELY(d->options & kUpb_DecodeOption_CheckRequired) &&
       !decode_checkrequired(d, msg, layout)) {
     d->missing_required = true;
   }
@@ -958,31 +990,31 @@
   return decode_msg(d, ptr, msg, decode_totablep(table));
 }
 
-static upb_decodestatus decode_top(struct upb_decstate *d, const char *buf,
+static upb_DecodeStatus decode_top(struct upb_decstate *d, const char *buf,
                                    void *msg, const upb_msglayout *l) {
   if (!decode_tryfastdispatch(d, &buf, msg, l)) {
     decode_msg(d, buf, msg, l);
   }
-  if (d->end_group != DECODE_NOGROUP) return UPB_DECODE_MALFORMED;
-  if (d->missing_required) return UPB_DECODE_MISSING_REQUIRED;
-  return UPB_DECODE_OK;
+  if (d->end_group != DECODE_NOGROUP) return kUpb_DecodeStatus_Malformed;
+  if (d->missing_required) return kUpb_DecodeStatus_MissingRequired;
+  return kUpb_DecodeStatus_Ok;
 }
 
-upb_decodestatus _upb_decode(const char *buf, size_t size, void *msg,
+upb_DecodeStatus _upb_decode(const char *buf, size_t size, void *msg,
                              const upb_msglayout *l, const upb_extreg *extreg,
                              int options, upb_arena *arena) {
   upb_decstate state;
   unsigned depth = (unsigned)options >> 16;
 
   if (size == 0) {
-    return UPB_DECODE_OK;
+    return kUpb_DecodeStatus_Ok;
   } else if (size <= 16) {
     memset(&state.patch, 0, 32);
     memcpy(&state.patch, buf, size);
     buf = state.patch;
     state.end = buf + size;
     state.limit = 0;
-    options &= ~UPB_DECODE_ALIAS;  // Can't alias patch buf.
+    options &= ~kUpb_DecodeOption_AliasString;  // Can't alias patch buf.
   } else {
     state.end = buf + size - 16;
     state.limit = 16;
@@ -1000,7 +1032,7 @@
   state.arena.cleanup_metadata = arena->cleanup_metadata;
   state.arena.parent = arena;
 
-  upb_decodestatus status = UPB_SETJMP(state.err);
+  upb_DecodeStatus status = UPB_SETJMP(state.err);
   if (UPB_LIKELY(!status)) {
     status = decode_top(&state, buf, msg, l);
   }
diff --git a/upb/decode.h b/upb/decode.h
index 98bf884..ad8a915 100644
--- a/upb/decode.h
+++ b/upb/decode.h
@@ -44,7 +44,7 @@
 enum {
   /* If set, strings will alias the input buffer instead of copying into the
    * arena. */
-  UPB_DECODE_ALIAS = 1,
+  kUpb_DecodeOption_AliasString = 1,
 
   /* If set, the parse will return failure if any message is missing any required
    * fields when the message data ends.  The parse will still continue, and the
@@ -63,27 +63,29 @@
    *    incomplete sub-message.  For this reason, this check is only useful for
    *    implemting ParseFromString() semantics.  For MergeFromString(), a
    *    post-parse validation step will always be necessary. */
-  UPB_CHECK_REQUIRED = 2,
+  kUpb_DecodeOption_CheckRequired = 2,
 };
 
 #define UPB_DECODE_MAXDEPTH(depth) ((depth) << 16)
 
 typedef enum {
-  UPB_DECODE_OK = 0,
-  // UPB_CHECK_REQUIRED failed (see above), but the parse otherwise succeeded.
-  UPB_DECODE_MISSING_REQUIRED = 1,
-  UPB_DECODE_OOM = 2,               // Arena alloc failed.
-  UPB_DECODE_BAD_UTF8 = 3,          // String field had bad UTF-8.
-  UPB_DECODE_MAXDEPTH_EXCEEDED = 4,
-  UPB_DECODE_MALFORMED = 5,         // Binary data was malformed.
-} upb_decodestatus;
+  kUpb_DecodeStatus_Ok = 0,
+  kUpb_DecodeStatus_Malformed = 1,          // Wire format was corrupt
+  kUpb_DecodeStatus_OutOfMemory = 2,        // Arena alloc failed
+  kUpb_DecodeStatus_BadUtf8 = 3,            // String field had bad UTF-8
+  kUpb_DecodeStatus_MaxDepthExceeded = 4,   // Exceeded UPB_DECODE_MAXDEPTH
 
-upb_decodestatus _upb_decode(const char *buf, size_t size, upb_msg *msg,
+  // kUpb_DecodeOption_CheckRequired failed (see above), but the parse otherwise
+  // succeeded.
+  kUpb_DecodeStatus_MissingRequired = 5,
+} upb_DecodeStatus;
+
+upb_DecodeStatus _upb_decode(const char *buf, size_t size, upb_msg *msg,
                              const upb_msglayout *l, const upb_extreg *extreg,
                              int options, upb_arena *arena);
 
 UPB_INLINE
-upb_decodestatus upb_decode(const char *buf, size_t size, upb_msg *msg,
+upb_DecodeStatus upb_decode(const char *buf, size_t size, upb_msg *msg,
                             const upb_msglayout *l, upb_arena *arena) {
   return _upb_decode(buf, size, msg, l, NULL, 0, arena);
 }
diff --git a/upb/decode_fast.c b/upb/decode_fast.c
index 5287bfd..65865b8 100644
--- a/upb/decode_fast.c
+++ b/upb/decode_fast.c
@@ -398,7 +398,7 @@
                                                                                \
   ptr += tagbytes;                                                             \
   ptr = fastdecode_varint64(ptr, &val);                                        \
-  if (ptr == NULL) return fastdecode_err(d, UPB_DECODE_MALFORMED);             \
+  if (ptr == NULL) return fastdecode_err(d, kUpb_DecodeStatus_Malformed);      \
   val = fastdecode_munge(val, valbytes, zigzag);                               \
   memcpy(dst, &val, valbytes);                                                 \
                                                                                \
@@ -462,7 +462,7 @@
   ptr = fastdecode_delimited(d, ptr, &fastdecode_topackedvarint, &ctx);        \
                                                                                \
   if (UPB_UNLIKELY(ptr == NULL)) {                                             \
-    return fastdecode_err(d, UPB_DECODE_MALFORMED);                            \
+    return fastdecode_err(d, kUpb_DecodeStatus_Malformed);                     \
   }                                                                            \
                                                                                \
   UPB_MUSTTAIL return fastdecode_dispatch(d, ptr, msg, table, hasbits, 0);
@@ -579,7 +579,7 @@
                                                                             \
   if (UPB_UNLIKELY(fastdecode_boundscheck(ptr, size, d->limit_ptr) ||       \
                    (size % valbytes) != 0)) {                               \
-    return fastdecode_err(d, UPB_DECODE_MALFORMED);                         \
+    return fastdecode_err(d, kUpb_DecodeStatus_Malformed);                  \
   }                                                                         \
                                                                             \
   upb_array **arr_p = fastdecode_fieldmem(msg, data);                       \
@@ -590,7 +590,7 @@
   if (UPB_LIKELY(!arr)) {                                                   \
     *arr_p = arr = _upb_array_new(&d->arena, elems, elem_size_lg2);         \
     if (!arr) {                                                             \
-      return fastdecode_err(d, UPB_DECODE_MALFORMED);                       \
+      return fastdecode_err(d, kUpb_DecodeStatus_Malformed);                \
     }                                                                       \
   } else {                                                                  \
     _upb_array_resize(arr, elems, &d->arena);                               \
@@ -656,7 +656,7 @@
                                          uint64_t hasbits, uint64_t data) {
   upb_strview *dst = (upb_strview*)data;
   if (!decode_verifyutf8_inl(dst->data, dst->size)) {
-    return fastdecode_err(d, UPB_DECODE_BAD_UTF8);
+    return fastdecode_err(d, kUpb_DecodeStatus_BadUtf8);
   }
   UPB_MUSTTAIL return fastdecode_dispatch(UPB_PARSE_ARGS);
 }
@@ -670,16 +670,16 @@
                                                                                \
   if (UPB_UNLIKELY(fastdecode_boundscheck(ptr, size, d->limit_ptr))) {         \
     dst->size = 0;                                                             \
-    return fastdecode_err(d, UPB_DECODE_MALFORMED);                            \
+    return fastdecode_err(d, kUpb_DecodeStatus_Malformed);                     \
   }                                                                            \
                                                                                \
-  if (d->options & UPB_DECODE_ALIAS) {                                         \
+  if (d->options & kUpb_DecodeOption_AliasString) {                            \
     dst->data = ptr;                                                           \
     dst->size = size;                                                          \
   } else {                                                                     \
     char *data = upb_arena_malloc(&d->arena, size);                            \
     if (!data) {                                                               \
-      return fastdecode_err(d, UPB_DECODE_OOM);                                \
+      return fastdecode_err(d, kUpb_DecodeStatus_OutOfMemory);                 \
     }                                                                          \
     memcpy(data, ptr, size);                                                   \
     dst->data = data;                                                          \
@@ -732,7 +732,7 @@
   size_t common_has;                                                           \
   char *buf;                                                                   \
                                                                                \
-  UPB_ASSERT((d->options & UPB_DECODE_ALIAS) == 0);                            \
+  UPB_ASSERT((d->options & kUpb_DecodeOption_AliasString) == 0);               \
   UPB_ASSERT(fastdecode_checktag(data, tagbytes));                             \
                                                                                \
   dst = fastdecode_getfield(d, ptr, msg, &data, &hasbits, &farr,               \
@@ -773,7 +773,7 @@
                                                                                \
   if (card == CARD_r) {                                                        \
     if (validate_utf8 && !decode_verifyutf8_inl(dst->data, dst->size)) {       \
-      return fastdecode_err(d, UPB_DECODE_BAD_UTF8);                           \
+      return fastdecode_err(d, kUpb_DecodeStatus_BadUtf8);                     \
     }                                                                          \
     fastdecode_nextret ret = fastdecode_nextrepeated(                          \
         d, dst, &ptr, &farr, data, tagbytes, sizeof(upb_strview));             \
@@ -816,7 +816,7 @@
     RETURN_GENERIC("string field tag mismatch\n");                             \
   }                                                                            \
                                                                                \
-  if (UPB_UNLIKELY((d->options & UPB_DECODE_ALIAS) == 0)) {                    \
+  if (UPB_UNLIKELY((d->options & kUpb_DecodeOption_AliasString) == 0)) {       \
     UPB_MUSTTAIL return copyfunc(UPB_PARSE_ARGS);                              \
   }                                                                            \
                                                                                \
@@ -848,14 +848,14 @@
                                                                                \
   if (card == CARD_r) {                                                        \
     if (validate_utf8 && !decode_verifyutf8_inl(dst->data, dst->size)) {       \
-      return fastdecode_err(d, UPB_DECODE_BAD_UTF8);                           \
+      return fastdecode_err(d, kUpb_DecodeStatus_BadUtf8);                     \
     }                                                                          \
     fastdecode_nextret ret = fastdecode_nextrepeated(                          \
         d, dst, &ptr, &farr, data, tagbytes, sizeof(upb_strview));             \
     switch (ret.next) {                                                        \
       case FD_NEXT_SAMEFIELD:                                                  \
         dst = ret.dst;                                                         \
-        if (UPB_UNLIKELY((d->options & UPB_DECODE_ALIAS) == 0)) {              \
+        if (UPB_UNLIKELY((d->options & kUpb_DecodeOption_AliasString) == 0)) { \
           /* Buffer flipped and we can't alias any more. Bounce to */          \
           /* copyfunc(), but via dispatch since we need to reload table */     \
           /* data also. */                                                     \
@@ -961,7 +961,7 @@
   }                                                                       \
                                                                           \
   if (--d->depth == 0) {                                                  \
-    return fastdecode_err(d, UPB_DECODE_MAXDEPTH_EXCEEDED);               \
+    return fastdecode_err(d, kUpb_DecodeStatus_MaxDepthExceeded);         \
   }                                                                       \
                                                                           \
   upb_msg **dst;                                                          \
@@ -998,7 +998,7 @@
   ptr = fastdecode_delimited(d, ptr, fastdecode_tosubmsg, &submsg);       \
                                                                           \
   if (UPB_UNLIKELY(ptr == NULL || d->end_group != DECODE_NOGROUP)) {      \
-    return fastdecode_err(d, UPB_DECODE_MALFORMED);                       \
+    return fastdecode_err(d, kUpb_DecodeStatus_Malformed);                \
   }                                                                       \
                                                                           \
   if (card == CARD_r) {                                                   \
diff --git a/upb/decode_internal.h b/upb/decode_internal.h
index 2085ae4..13fb0a9 100644
--- a/upb/decode_internal.h
+++ b/upb/decode_internal.h
@@ -59,6 +59,11 @@
   char patch[32];
   upb_arena arena;
   jmp_buf err;
+
+#ifndef NDEBUG
+  const char *debug_tagstart;
+  const char *debug_valstart;
+#endif
 } upb_decstate;
 
 /* Error function that will abort decoding with longjmp(). We can't declare this
@@ -115,7 +120,7 @@
     if (d->unknown_msg) {
       if (!_upb_msg_addunknown(d->unknown_msg, d->unknown, ptr - d->unknown,
                                &d->arena)) {
-        *status = UPB_DECODE_OOM;
+        *status = kUpb_DecodeStatus_OutOfMemory;
         return NULL;
       }
       d->unknown = &d->patch[0] + overrun;
@@ -126,11 +131,11 @@
     d->end = &d->patch[16];
     d->limit -= 16;
     d->limit_ptr = d->end + d->limit;
-    d->options &= ~UPB_DECODE_ALIAS;
+    d->options &= ~kUpb_DecodeOption_AliasString;
     UPB_ASSERT(ptr < d->limit_ptr);
     return ptr;
   } else {
-    *status = UPB_DECODE_MALFORMED;
+    *status = kUpb_DecodeStatus_Malformed;
     return NULL;
   }
 }
diff --git a/upb/def.c b/upb/def.c
index b9f5dd7..a574720 100644
--- a/upb/def.c
+++ b/upb/def.c
@@ -1592,6 +1592,7 @@
   l->fields = fields;
   l->subs = subs;
   l->table_mask = 0;
+  l->required_count = 0;
 
   if (upb_msgdef_extrangecount(m) > 0) {
     if (google_protobuf_MessageOptions_message_set_wire_format(m->opts)) {
@@ -1654,6 +1655,19 @@
   /* Assign hasbits for required fields first. */
   size_t hasbit = 0;
 
+  for (int i = 0; i < m->field_count; i++) {
+    const upb_fielddef* f = &m->fields[i];
+    upb_msglayout_field *field = &fields[upb_fielddef_index(f)];
+    if (upb_fielddef_label(f) == UPB_LABEL_REQUIRED) {
+      field->presence = ++hasbit;
+      if (hasbit >= 63) {
+        symtab_errf(ctx, "Message with >=63 required fields: %s",
+                    upb_msgdef_fullname(m));
+      }
+      l->required_count++;
+    }
+  }
+
   /* Allocate hasbits and set basic field attributes. */
   sublayout_count = 0;
   for (int i = 0; i < m->field_count; i++) {
@@ -2998,7 +3012,7 @@
   }
 
   file = google_protobuf_FileDescriptorProto_parse_ex(
-      init->descriptor.data, init->descriptor.size, NULL, UPB_DECODE_ALIAS,
+      init->descriptor.data, init->descriptor.size, NULL, kUpb_DecodeOption_AliasString,
       arena);
   s->bytes_loaded += init->descriptor.size;
 
diff --git a/upb/table.c b/upb/table.c
index 63fecf2..a3b33ae 100644
--- a/upb/table.c
+++ b/upb/table.c
@@ -812,7 +812,7 @@
     while (++i < t->array_size) {
       upb_tabval ent = t->array[i];
       if (upb_arrhas(ent)) {
-        *key = i; 
+        *key = i;
         *val = _upb_value_val(ent.val);
         *iter = i;
         return true;
diff --git a/upbc/protoc-gen-upb.cc b/upbc/protoc-gen-upb.cc
index 446ea1c..fa6b1c5 100644
--- a/upbc/protoc-gen-upb.cc
+++ b/upbc/protoc-gen-upb.cc
@@ -83,6 +83,12 @@
   }
 }
 
+template <class T>
+void SortDefs(std::vector<T>* defs) {
+  std::sort(defs->begin(), defs->end(),
+            [](T a, T b) { return a->full_name() < b->full_name(); });
+}
+
 std::vector<const protobuf::EnumDescriptor*> SortedEnums(
     const protobuf::FileDescriptor* file) {
   std::vector<const protobuf::EnumDescriptor*> enums;
@@ -92,6 +98,7 @@
   for (int i = 0; i < file->message_type_count(); i++) {
     AddEnums(file->message_type(i), &enums);
   }
+  SortDefs(&enums);
   return enums;
 }
 
@@ -1354,7 +1361,8 @@
   output("    &$0,\n", MessageInit(ext->containing_type()));
   if (ext->message_type()) {
     output("    {.submsg = &$0},\n", MessageInit(ext->message_type()));
-  } else if (ext->enum_type() && ext->enum_type()->file()->syntax() == protobuf::FileDescriptor::SYNTAX_PROTO2) {
+  } else if (ext->enum_type() && ext->enum_type()->file()->syntax() ==
+                                     protobuf::FileDescriptor::SYNTAX_PROTO2) {
     output("    {.subenum = &$0},\n", EnumInit(ext->enum_type()));
   } else {
     output("    {.submsg = NULL},\n");