Merge release branch 22.x into main
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..f3206a4
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,37 @@
+/src/ @protocolbuffers/protobuf-compiler
+
+/src/google/protobuf/compiler/cpp @protocolbuffers/protobuf-cpp
+
+/csharp/ @protocolbuffers/protobuf-csharp
+/src/google/protobuf/compiler/csharp/ @protocolbuffers/protobuf-csharp
+
+/docs/ @protocolbuffers/protobuf-docs
+/examples/ @protocolbuffers/protobuf-docs
+
+/java/ @protocolbuffers/protobuf-java
+/src/google/protobuf/compiler/java/ @protocolbuffers/protobuf-java
+
+/java/kotlin-lite/ @protocolbuffers/protobuf-kotlin
+/java/kotlin/ @protocolbuffers/protobuf-kotlin
+
+/objectivec/ @protocolbuffers/protobuf-objc
+/src/google/protobuf/compiler/objectivec/ @protocolbuffers/protobuf-objc
+
+/php/ @protocolbuffers/protobuf-php
+/src/google/protobuf/compiler/php/ @protocolbuffers/protobuf-php
+
+/python/ @protocolbuffers/protobuf-python
+/src/google/protobuf/compiler/python/ @protocolbuffers/protobuf-python
+
+/ruby/ @protocolbuffers/protobuf-ruby
+/src/google/protobuf/compiler/ruby/ @protocolbuffers/protobuf-ruby
+
+/build_defs/ @protocolbuffers/protobuf-btr
+/cmake/ @protocolbuffers/protobuf-btr
+/pkg/ @protocolbuffers/protobuf-btr
+/toolchain/ @protocolbuffers/protobuf-btr
+/conformance/ @protocolbuffers/protobuf-btr
+/kokoro/ @protocolbuffers/protobuf-btr
+/third_party/ @protocolbuffers/protobuf-btr
+*.bazel @protocolbuffers/protobuf-btr
+/.github/ @protocolbuffers/protobuf-btr
diff --git a/.github/actions/bash/action.yml b/.github/actions/bash/action.yml
deleted file mode 100644
index d105a44..0000000
--- a/.github/actions/bash/action.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-name: 'Non-Bazel Bash Run'
-description: 'Run a bash script for Protobuf CI testing with a non-Bazel build system'
-inputs:
-  credentials:
-    required: true
-    description: "The GCP credentials to use for reading the docker image"
-    type: string
-  command:
-    required: true
-    description: A command to run in the docker image
-
-runs:
-  using: 'composite'
-  steps:
-    - name: Setup Runner
-      uses: ./.github/actions/internal/setup-runner
-
-    - name: Update stale files using Bazel
-      uses: ./.github/actions/bazel
-      with:
-        credentials: ${{ inputs.credentials }}
-        bazel-cache: regenerate-stale-files
-        bash: ./regenerate_stale_files.sh $BAZEL_FLAGS
-
-    - name: Run
-      shell: bash
-      run: ${{ inputs.command }}
diff --git a/.github/actions/bazel-docker/action.yml b/.github/actions/bazel-docker/action.yml
deleted file mode 100644
index af1104e..0000000
--- a/.github/actions/bazel-docker/action.yml
+++ /dev/null
@@ -1,75 +0,0 @@
-name: 'Docker Bazel Run'
-description: 'Run a Bazel-based docker image for Protobuf CI testing'
-inputs:
-  credentials:
-    required: true
-    description: "The GCP credentials to use for reading the docker image"
-    type: string
-  image:
-    required: false
-    default: us-docker.pkg.dev/protobuf-build/containers/common/linux/bazel:5.1.1-aec4d74f2eb6938fc53ef7d9a79a4bf2da24abc1
-    description: "The docker image to use"
-    type: string
-  bazel-cache:
-    required: true
-    description: >
-      A unique path for the Bazel cache.  This will trigger the generation
-      of a BAZEL_CACHE environment variable inside the container that provides
-      the appropriate flags for any bazel command.
-    type: string
-  bazel:
-    required: false
-    description: "The Bazel command to run"
-    type: string
-  bash:
-    required: false
-    description: "A bash command to run.  $BAZEL_FLAGS will be available to use for bazel runs."
-    type: string
-
-runs:
-  using: 'composite'
-  steps:
-    - name: Authenticate
-      id: auth
-      uses: ./.github/actions/internal/gcloud-auth
-      with:
-        credentials: ${{ inputs.credentials }}
-
-    - name: Setup Runner
-      uses: ./.github/actions/internal/setup-runner
-
-    - name: Setup Bazel
-      id: bazel
-      uses: ./.github/actions/internal/bazel-setup
-      with:
-        credentials-file: /workspace/$(basename ${{ steps.auth.outputs.credentials-file }})
-        bazel-cache: ${{ inputs.bazel-cache }}
-
-    - name: Hook up repository Cache
-      shell: bash
-      run: echo "BAZEL_FLAGS=$BAZEL_FLAGS --repository_cache='/workspace/${{ env.REPOSITORY_CACHE_PATH }}'" >> $GITHUB_ENV
-
-    - name: Validate inputs
-      if: ${{ (inputs.bash && inputs.bazel) || (!inputs.bash && !inputs.bazel) }}
-      shell: bash
-      run: echo "Invalid specification of both non-Bazel and Bazel command"; exit 1
-
-    - name: Run Bash Docker
-      uses: ./.github/actions/internal/docker-run
-      if: ${{ inputs.bash }}
-      with:
-        image: ${{ inputs.image }}
-        run-flags: --entrypoint "/bin/bash"
-        command: -l -c "${{ inputs.bash }}"
-
-    - name: Run Bazel Docker
-      uses: ./.github/actions/internal/docker-run
-      if: ${{ !inputs.bash }}
-      with:
-        image: ${{ inputs.image }}
-        command: ${{ inputs.bazel }} ${{ env.BAZEL_FLAGS }}
-
-    - name: Save Bazel repository cache
-      # Only allow repository cache updates during post-submits.
-      if: ${{ github.event_name != 'pull_request' && github.event_name != 'pull_request_target' }}
-      uses: ./.github/actions/internal/repository-cache-save
diff --git a/.github/actions/bazel/action.yml b/.github/actions/bazel/action.yml
deleted file mode 100644
index 952dfad..0000000
--- a/.github/actions/bazel/action.yml
+++ /dev/null
@@ -1,134 +0,0 @@
-name: 'Docker Bazel Run'
-description: 'Run a Bazel-based docker image for Protobuf CI testing'
-inputs:
-  credentials:
-    required: true
-    description: The GCP credentials to use for reading the docker image
-    type: string
-  bazel-cache:
-    required: true
-    description: >
-      A unique path for the Bazel cache.  This will trigger the generation
-      of a BAZEL_CACHE environment variable inside the container that provides
-      the appropriate flags for any bazel command.
-    type: string
-  version:
-    required: false
-    description: A pinned Bazel version to use
-    default: '5.1.1'
-    type: string
-  bazel:
-    required: false
-    description: The Bazel command to run
-    type: string
-  bash:
-    required: false
-    description: >
-      A bash command to run.  $BAZEL_FLAGS and $BAZEL_STARTUP_FLAGS will be
-      available to use for bazel runs.
-    type: string
-
-runs:
-  using: 'composite'
-  steps:
-    - name: Authenticate
-      id: auth
-      uses: ./.github/actions/internal/gcloud-auth
-      with:
-        credentials: ${{ inputs.credentials }}
-
-    - name: Setup Runner
-      uses: ./.github/actions/internal/setup-runner
-
-    - name: Setup Bazel
-      id: bazel
-      uses: ./.github/actions/internal/bazel-setup
-      with:
-        credentials-file: ${{ steps.auth.outputs.credentials-file }}
-        bazel-cache: ${{ inputs.bazel-cache }}
-
-    - name: Get Linux bazelisk path
-      if: runner.os == 'Linux'
-      shell: bash
-      run: echo "BAZELISK_PATH=~/.cache/bazelisk" >> $GITHUB_ENV
-
-    - name: Get MacOS bazelisk path
-      if: runner.os == 'macOS'
-      shell: bash
-      run: echo "BAZELISK_PATH=~/Library/Caches/bazelisk" >> $GITHUB_ENV
-
-    - name: Get Windows bazelisk path
-      if: runner.os == 'Windows'
-      shell: bash
-      run: echo "BAZELISK_PATH=$LOCALAPPDATA\bazelisk" >> $GITHUB_ENV
-
-    - name: Cache Bazelisk
-      if: ${{ github.event_name != 'pull_request' && github.event_name != 'pull_request_target' }}
-      uses: actions/cache@627f0f41f6904a5b1efbaed9f96d9eb58e92e920 # v3.2.4
-      with:
-        path: ${{ env.BAZELISK_PATH }}
-        key: bazel-${{ runner.os }}-${{ inputs.version }}
-
-    - name: Restore Bazelisk
-      if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
-      uses: actions/cache/restore@627f0f41f6904a5b1efbaed9f96d9eb58e92e920 # v3.2.4
-      with:
-        path: ${{ env.BAZELISK_PATH }}
-        key: bazel-${{ runner.os }}-${{ inputs.version }}
-
-    - name: Hook up repository Cache
-      shell: bash
-      run: echo "BAZEL_FLAGS=$BAZEL_FLAGS --repository_cache=$(pwd)/${{ env.REPOSITORY_CACHE_PATH }}" >> $GITHUB_ENV
-
-    - name: Validate inputs
-      if: ${{ (inputs.bash && inputs.bazel) || (!inputs.bash && !inputs.bazel) }}
-      shell: bash
-      run: echo "Invalid specification of both non-Bazel and Bazel command"; exit 1
-
-    - name: Pin Bazel version
-      shell: bash
-      run: echo "USE_BAZEL_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
-
-    - name: Output Bazel version
-      shell: bash
-      run: bazelisk version
-
-    # Bazel has multiple Xcode calls with hardcoded timeouts.  Many of these
-    # end up timing out on our github runners, causing flakes on every mac
-    # build that invoked Bazel.  To work around this, we manually invoke these
-    # calls before running Bazel to make sure they end up in Xcode's cache for
-    # quicker runs later.  All of these calls are obtained from xcrun calls in
-    # https://github.com/bazelbuild/bazel/blob/e8a69f5d5acaeb6af760631490ecbf73e8a04eeb/tools/cpp/osx_cc_configure.bzl.
-    # See https://github.com/bazelbuild/bazel/issues/17437 for more details.
-    # TODO(b/269503614) Remove this once Bazel provides an official solution.
-    - name: Warm up Xcode
-      if: ${{ runner.os == 'macOS' }}
-      shell: bash
-      run: |
-        mkdir -p mac_bazel_workaround
-        bazelisk ${{ steps.bazel.outputs.bazel-startup-flags }} build @bazel_tools//tools/osx:xcode_locator.m $BAZEL_FLAGS
-        XCODE_LOCATOR_FLAGS="--sdk macosx clang -mmacosx-version-min=10.9 -fobjc-arc -framework CoreServices -framework Foundation"
-        SINGLE_ARCH_COMPILE_FLAGS="--sdk macosx clang -mmacosx-version-min=10.9 -std=c++11 -lc++ -O3"
-        COMPILE_FLAGS="$SINGLE_ARCH_COMPILE_FLAGS -arch arm64 -arch x86_64 -Wl,-no_adhoc_codesign -Wl,-no_uuid -O3"
-        time env -i DEVELOPER_DIR=$DEVELOPER_DIR xcrun $XCODE_LOCATOR_FLAGS -o mac_bazel_workaround/xcode-locator-bin $(bazel info output_base)/external/bazel_tools/tools/osx/xcode_locator.m
-        time env -i DEVELOPER_DIR=$DEVELOPER_DIR xcrun $SINGLE_ARCH_COMPILE_FLAGS -o mac_bazel_workaround/libtool_check_unique $(bazel info output_base)/external/bazel_tools/tools/objc/libtool_check_unique.cc
-        time env -i DEVELOPER_DIR=$DEVELOPER_DIR xcrun $COMPILE_FLAGS -o mac_bazel_workaround/libtool_check_unique $(bazel info output_base)/external/bazel_tools/tools/objc/libtool_check_unique.cc
-        time env -i DEVELOPER_DIR=$DEVELOPER_DIR xcrun $SINGLE_ARCH_COMPILE_FLAGS -o mac_bazel_workaround/wrapped_clang $(bazel info output_base)/external/bazel_tools/tools/osx/crosstool/wrapped_clang.cc
-        time env -i DEVELOPER_DIR=$DEVELOPER_DIR xcrun $COMPILE_FLAGS -o mac_bazel_workaround/wrapped_clang $(bazel info output_base)/external/bazel_tools/tools/osx/crosstool/wrapped_clang.cc
-
-    - name: Run Bash
-      if: ${{ inputs.bash }}
-      run: ${{ inputs.bash }}
-      shell: bash
-
-    - name: Run Bazel
-      if: ${{ !inputs.bash }}
-      run: >-
-        bazelisk ${{ steps.bazel.outputs.bazel-startup-flags }}
-        ${{ inputs.bazel }} $BAZEL_FLAGS
-      shell: bash
-
-    - name: Save Bazel repository cache
-      # Only allow repository cache updates during post-submits.
-      if: ${{ github.event_name != 'pull_request' && github.event_name != 'pull_request_target'}}
-      uses: ./.github/actions/internal/repository-cache-save
diff --git a/.github/actions/ccache/action.yml b/.github/actions/ccache/action.yml
deleted file mode 100644
index 2d70550..0000000
--- a/.github/actions/ccache/action.yml
+++ /dev/null
@@ -1,63 +0,0 @@
-name: 'CCache Setup'
-description: 'Run a Bazel-based docker image for Protobuf CI testing'
-inputs:
-  cache-prefix:
-    required: true
-    description: A unique prefix to prevent cache pollution
-    type: string
-  support-modules:
-    required: false
-    description: Whether or not we need to support modules.  This can result in extra cache misses.
-
-runs:
-  using: 'composite'
-  steps:
-    - name: Configure ccache environment variables
-      shell: bash
-      run: |
-        echo "CCACHE_BASEDIR=${{ github.workspace }}" >> $GITHUB_ENV
-        echo "CCACHE_DIR=${{ github.workspace }}/.ccache" >> $GITHUB_ENV
-        echo "CCACHE_COMPRESS=true" >> $GITHUB_ENV
-        echo "CCACHE_COMPRESSLEVEL=5" >> $GITHUB_ENV
-        echo "CCACHE_MAXSIZE=100M" >> $GITHUB_ENV
-        echo "CCACHE_SLOPPINESS=clang_index_store,include_file_ctime,include_file_mtime,file_macro,time_macros" >> $GITHUB_ENV
-        echo "CCACHE_DIRECT=true" >> $GITHUB_ENV
-        echo "CCACHE_CMAKE_FLAGS=-Dprotobuf_ALLOW_CCACHE=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache" >> $GITHUB_ENV
-
-    - name: Setup ccache on Windows
-      if: ${{ runner.os == 'Windows' }}
-      uses: ./.github/actions/internal/ccache-setup-windows
-    - name: Setup ccache on Mac
-      if: ${{ runner.os == 'macOS' }}
-      shell: bash
-      run: brew install ccache
-
-    - name: Setup fixed path ccache caching
-      uses: actions/cache@627f0f41f6904a5b1efbaed9f96d9eb58e92e920 # v3.2.4
-      with:
-        path: |
-          .ccache/**
-          !.ccache/lock
-          !.ccache/tmp
-        # Always push to a cache key unique to this commit.
-        key: ${{ format('ccache-{0}-{1}-{2}', inputs.cache-prefix, github.ref_name, github.sha) }}
-        # Select a cache to restore from with the follow order of preference:
-        # 1) The exact same commit we're running over
-        # 2) The latest cache from the current ref branch
-        # 3) The latest push to the base ref of a pull request
-        restore-keys: |
-          ${{ format('ccache-{0}-{1}-{2}', inputs.cache-prefix, github.ref_name, github.sha) }}
-          ${{ format('ccache-{0}-{1}', inputs.cache-prefix, github.ref_name) }}
-          ${{ format('ccache-{0}-{1}', inputs.cache-prefix, github.base_ref) }}
-
-    - name: Enable module support
-      if: ${{ inputs.support-modules }}
-      shell: bash
-      run: |
-        echo "CCACHE_SLOPPINESS=$CCACHE_SLOPPINESS,modules" >> $GITHUB_ENV
-        echo "CCACHE_DEPEND=true" >> $GITHUB_ENV
-
-    - name: Zero out ccache
-      if: ${{ runner.os != 'Linux' }}
-      shell: bash
-      run: ccache -z
diff --git a/.github/actions/cross-compile-protoc/action.yml b/.github/actions/cross-compile-protoc/action.yml
deleted file mode 100644
index 23660af..0000000
--- a/.github/actions/cross-compile-protoc/action.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-name: 'Cross-compile protoc'
-description: 'Produces a cross-compiled protoc binary for a target architecture'
-inputs:
-  credentials:
-    required: true
-    description: The GCP credentials to use for reading the docker image
-    type: string
-  architecture:
-    required: true
-    description: The target architecture to build for
-    type: string
-outputs:
-  protoc:
-    description: "Cross-compiled protoc location.  Also output to $PROTOC"
-    value: ${{ steps.output.outputs.protoc }}
-
-runs:
-  using: 'composite'
-  steps:
-    - name: Cross compile protoc for ${{ inputs.architecture }}
-      uses: ./.github/actions/bazel-docker
-      with:
-        credentials: ${{ inputs.credentials }}
-        bazel-cache: xcompile-protoc/${{ inputs.architecture }}
-        bash: |
-          bazel build //:protoc --config=${{ inputs.architecture }} $BAZEL_FLAGS
-          cp bazel-bin/protoc .
-
-    - name: Set protoc environment variable
-      shell: bash
-      run: echo "PROTOC=protoc-${{ inputs.architecture }}" >> $GITHUB_ENV
-
-    - name: Extract binary
-      id: output
-      shell: bash
-      run: |
-        mv protoc $PROTOC
-        echo "protoc=$PROTOC" >> $GITHUB_OUTPUT
diff --git a/.github/actions/docker/action.yml b/.github/actions/docker/action.yml
deleted file mode 100644
index 54de3f9..0000000
--- a/.github/actions/docker/action.yml
+++ /dev/null
@@ -1,50 +0,0 @@
-name: 'Docker Non-Bazel Run'
-description: 'Run a docker image for Protobuf CI testing with a non-Bazel build system'
-inputs:
-  credentials:
-    required: true
-    description: "The GCP credentials to use for reading the docker image"
-    type: string
-  command:
-    required: true
-    description: A command to run in the docker image
-  image:
-    required: false
-    default: us-docker.pkg.dev/protobuf-build/containers/common/linux/bazel:5.1.1-aec4d74f2eb6938fc53ef7d9a79a4bf2da24abc1
-    description: "The docker image to use"
-    type: string
-  platform:
-    required: false
-    description: "The platform to use for the image"
-    type: string
-  skip-staleness-check:
-    required: false
-    description: "Skip staleness checks"
-    type: boolean
-
-runs:
-  using: 'composite'
-  steps:
-    - name: Setup Runner
-      uses: ./.github/actions/internal/setup-runner
-
-    - name: Update stale files using Bazel
-      if: ${{ !inputs.skip-staleness-check }}
-      uses: ./.github/actions/bazel-docker
-      with:
-        image: us-docker.pkg.dev/protobuf-build/containers/common/linux/bazel:5.1.1-aec4d74f2eb6938fc53ef7d9a79a4bf2da24abc1
-        credentials: ${{ inputs.credentials }}
-        bazel-cache: regenerate-stale-files
-        bash: ./regenerate_stale_files.sh $BAZEL_FLAGS
-
-    - name: Generate docker flags
-      if: inputs.platform
-      shell: bash
-      run: echo "DOCKER_RUN_FLAGS=--platform ${{inputs.platform}}" >> $GITHUB_ENV
-
-    - name: Run Docker
-      uses: ./.github/actions/internal/docker-run
-      with:
-        image: ${{ inputs.image }}
-        command: ${{ inputs.command }}
-        run-flags: ${{ env.DOCKER_RUN_FLAGS }}
diff --git a/.github/actions/internal/bazel-setup/action.yml b/.github/actions/internal/bazel-setup/action.yml
deleted file mode 100644
index 0ef5d5a..0000000
--- a/.github/actions/internal/bazel-setup/action.yml
+++ /dev/null
@@ -1,75 +0,0 @@
-name: Setup Bazel
-description: Setup a Bazel environment for Protobuf CI testing
-inputs:
-  credentials-file:
-    required: true
-    description: The GCP credentials file to use for caching
-    type: string
-  bazel-cache:
-    required: true
-    description: A unique path for the Bazel cache.
-    type: string
-
-outputs:
-  bazel-flags:
-    description: Bazel flags that should be sent to all Bazel invocations
-    value: ${{ steps.output.outputs.bazel-flags }}
-  bazel-startup-flags:
-    description: Bazel startup flags that should be sent to all Bazel invocations
-    value: ${{ steps.output.outputs.bazel-startup-flags }}
-
-runs:
-  using: 'composite'
-  steps:
-    - name: Initialize BAZEL environment variable
-      shell: bash
-      run: echo "BAZEL=bazelisk" >> $GITHUB_ENV
-
-    - name: Initialize Windows startup flags
-      if: runner.os == 'Windows'
-      shell: bash
-      run: echo "BAZEL_STARTUP_FLAGS=--output_user_root=C:/tmp --windows_enable_symlinks" >> $GITHUB_ENV
-
-    - name: Initialize Bazel flags
-      shell: bash
-      run: echo "BAZEL_FLAGS=--keep_going --test_output=errors --test_timeout=600" >> $GITHUB_ENV
-
-    - name: Initialize Windows-specific Bazel flags
-      if: runner.os == 'Windows'
-      shell: bash
-      run: echo "BAZEL_FLAGS=$BAZEL_FLAGS --enable_runfiles" >> $GITHUB_ENV
-
-    - name: Initialize MacOS-specific Bazel flags
-      if: runner.os == 'macOS'
-      shell: bash
-      run: |
-        echo "BAZEL_FLAGS=$BAZEL_FLAGS --xcode_version_config=//.github:host_xcodes" >> $GITHUB_ENV
-        echo "DEVELOPER_DIR=${{ env.DEVELOPER_DIR || '/Applications/Xcode_14.1.app/Contents/Developer' }}" >> $GITHUB_ENV
-
-    - name: Configure Bazel caching
-      # Skip bazel cache for local act runs due to issue with credential files
-      # and nested docker images
-      if: ${{ inputs.bazel-cache && !github.event.act_local_test }}
-      shell: bash
-      run: >-
-        echo "BAZEL_FLAGS=$BAZEL_FLAGS
-        --google_credentials=${{ inputs.credentials-file }}
-        --remote_cache=https://storage.googleapis.com/protobuf-bazel-cache/protobuf/gha/${{ inputs.bazel-cache }}" >> $GITHUB_ENV
-
-    - name: Configure Bazel cache writing
-      # External runs should never write to our caches.
-      if: ${{ github.event_name != 'pull_request_target' && inputs.bazel-cache && !github.event.act_local_test }}
-      shell: bash
-      run: echo "BAZEL_FLAGS=$BAZEL_FLAGS --remote_upload_local_results" >> $GITHUB_ENV
-
-    - name: Output Bazel flags
-      id: output
-      shell: bash
-      run: |
-        echo "bazel-flags=$BAZEL_FLAGS" >> $GITHUB_OUTPUT
-        echo "bazel-startup-flags=$BAZEL_STARTUP_FLAGS" >> $GITHUB_OUTPUT
-
-    - name: Restore Bazel repository cache
-      uses: ./.github/actions/internal/repository-cache-restore
-      with:
-        bazel-cache: ${{ inputs.bazel-cache }}
diff --git a/.github/actions/internal/ccache-setup-windows/action.yml b/.github/actions/internal/ccache-setup-windows/action.yml
deleted file mode 100644
index 7b9f992..0000000
--- a/.github/actions/internal/ccache-setup-windows/action.yml
+++ /dev/null
@@ -1,68 +0,0 @@
-name: 'CCache Setup'
-description: 'Setup ccache for us in Windows CI'
-inputs:
-  ccache-version:
-    required: false
-    default: '4.7.4'
-    description: A pinned version of ccache
-    type: string
-
-runs:
-  using: 'composite'
-  steps:
-    - name: Setup MSVC
-      uses: ilammy/msvc-dev-cmd@cec98b9d092141f74527d0afa6feb2af698cfe89 # v1.12.1
-      with:
-        arch: x64
-        vsversion: '2019'
-
-    - name: Setup ccache path
-      shell: bash
-      run: |
-        echo "CCACHE_EXE_PATH=$LOCALAPPDATA\ccache-${{ inputs.ccache-version }}-windows-x86_64" >> $GITHUB_ENV
-        echo "$LOCALAPPDATA\ccache-${{ inputs.ccache-version }}-windows-x86_64" >> $GITHUB_PATH
-
-    - name: Add ccache to Powershell path
-      shell: pwsh
-      run: echo "%LOCALAPPDATA%\ccache-${{ inputs.ccache-version }}-windows-x86_64" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
-
-    - name: Setup caching of ccache download
-      if: ${{ github.event_name != 'pull_request' && github.event_name != 'pull_request_target' }}
-      id: ccache-cache
-      uses: actions/cache@627f0f41f6904a5b1efbaed9f96d9eb58e92e920 # v3.2.4
-      with:
-        path: ${{ env.CCACHE_EXE_PATH }}
-        key: ccache-exe-${{ inputs.ccache-version }}
-
-    - name: Restore ccache download
-      if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
-      id: ccache-restore
-      uses: actions/cache/restore@627f0f41f6904a5b1efbaed9f96d9eb58e92e920 # v3.2.4
-      with:
-        path: ${{ env.CCACHE_EXE_PATH }}
-        key: ccache-exe-${{ inputs.ccache-version }}
-
-    - name: Download ccache
-      shell: bash
-      if: ${{ steps.ccache-cache.outputs.cache-hit != 'true' && steps.ccache-restore.outputs.cache-hit != 'true'}}
-      run: |
-        cd $LOCALAPPDATA
-        curl -kLSs "https://github.com/ccache/ccache/releases/download/v${{ inputs.ccache-version }}/ccache-${{ inputs.ccache-version }}-windows-x86_64.zip" -o ccache.zip
-        unzip ccache.zip
-        rm ccache.zip
-        ccache --version
-
-    - name: Configure ccache environment variables
-      shell: pwsh
-      run: |
-        Write-Host $Env:GITHUB_REF
-        $cllocation = (Get-Command cl.exe).Path
-        echo "CCACHE_COMPILER=$cllocation" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
-        echo "CCACHE_COMPILERTYPE=msvc" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
-
-    - name: Configure Windows-specific ccache environment variables
-      shell: bash
-      # Windows caches are about 2x larger than other platforms.
-      run: |
-        echo "CCACHE_COMPRESSLEVEL=10" >> $GITHUB_ENV
-        echo "CCACHE_MAXSIZE=200M" >> $GITHUB_ENV
diff --git a/.github/actions/internal/docker-run/action.yml b/.github/actions/internal/docker-run/action.yml
deleted file mode 100644
index 1cc1489..0000000
--- a/.github/actions/internal/docker-run/action.yml
+++ /dev/null
@@ -1,62 +0,0 @@
-name: 'Run Docker'
-description: 'Run a docker image for Protobuf CI testing'
-inputs:
-  image:
-    required: true
-    description: "The docker image to use"
-    type: string
-  command:
-    required: true
-    description: "A raw docker command to run"
-    type: string
-  run-flags:
-    required: false
-    description: "Additional flags to pass to docker run"
-    type: string
-
-  # WARNING: loading from cache appears to be slower than pull!
-  docker-cache:
-    required: false
-    description: "Enabled caching of pulled docker images."
-
-runs:
-  using: 'composite'
-  steps:
-    - name: Authenticate for GAR use
-      shell: bash
-      run: gcloud auth configure-docker -q us-docker.pkg.dev
-
-    - name: Setup QEMU for possible emulation
-      uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0
-
-    - name: Check docker cache
-      if: ${{ inputs.docker-cache }}
-      id: check-docker-cache
-      uses: actions/cache@627f0f41f6904a5b1efbaed9f96d9eb58e92e920 # v3.2.4
-      with:
-        path: ci/docker/
-        key: ${{ inputs.image }}
-
-    - name: Pull and store if cache miss
-      shell: bash
-      if: ${{ inputs.docker-cache && steps.check-docker-cache.outputs.cache-hit != 'true' }}
-      run: >
-        time docker pull -q ${{ inputs.image }} &&
-        mkdir -p ci/docker/$(dirname ${{ inputs.image }}) &&
-        time docker image save ${{ inputs.image }} --output ./ci/docker/${{ inputs.image }}.tar
-
-    - name: Use the cached image on cache hit
-      shell: bash
-      if: ${{ inputs.docker-cache && steps.check-docker-cache.outputs.cache-hit == 'true' }}
-      run: time docker image load --input ./ci/docker/${{ inputs.image }}.tar
-
-    - name: Pull fresh docker image
-      shell: bash
-      if: ${{ !inputs.docker-cache }}
-      run: time docker pull -q ${{ inputs.image }}
-
-    - name: Run docker
-      shell: bash
-      run: >
-        time docker run ${{ inputs.run-flags}} -v${{ github.workspace }}:/workspace
-        ${{ inputs.image }} ${{ inputs.command }}
diff --git a/.github/actions/internal/gcloud-auth/action.yml b/.github/actions/internal/gcloud-auth/action.yml
deleted file mode 100644
index 189619d..0000000
--- a/.github/actions/internal/gcloud-auth/action.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-name: 'Authenticate for GCP'
-description: 'Authenticate a workflow for Protobuf CI testing'
-inputs:
-  credentials:
-    required: true
-    description: "The GCP credentials to use for GCP"
-    type: string
-
-outputs:
-  credentials-file:
-    description: "Credentials file generated for GCP"
-    value: ${{ steps.output.outputs.credentials-file }}
-
-runs:
-  using: 'composite'
-  steps:
-    - name: Authenticate to Google Cloud
-      id: auth
-      uses: google-github-actions/auth@ef5d53e30bbcd8d0836f4288f5e50ff3e086997d # v1.0.0
-      with:
-        credentials_json: ${{ inputs.credentials }}
-    - name: Set up Cloud SDK
-      uses: google-github-actions/setup-gcloud@d51b5346f85640ec2aa2fa057354d2b82c2fcbce # v1.0.1
-    - name: Use gcloud CLI
-      shell: bash
-      run: gcloud info
-
-    - name: Store credentials path
-      shell: bash
-      run: echo "CREDENTIALS_FILE=${{ steps.auth.outputs.credentials_file_path }}" >> $GITHUB_ENV
-
-    - name: Fix credentials path (Windows)
-      if: ${{ runner.os == 'Windows' }}
-      # Bash commands in windows don't like the backslash in the file path.
-      # Assume we're running in the root directory and grab the base name.
-      shell: bash
-      run: echo "CREDENTIALS_FILE="$(basename ${CREDENTIALS_FILE//\\//}) >> $GITHUB_ENV
-
-    - name: Output credentials file
-      id: output
-      shell: bash
-      run: echo "credentials-file=${{ env.CREDENTIALS_FILE }}" >> $GITHUB_OUTPUT
diff --git a/.github/actions/internal/repository-cache-restore/action.yml b/.github/actions/internal/repository-cache-restore/action.yml
deleted file mode 100644
index a5b4a0a..0000000
--- a/.github/actions/internal/repository-cache-restore/action.yml
+++ /dev/null
@@ -1,41 +0,0 @@
-name: Restore Repository Cache
-description: Restore the Bazel repository cache from our github action cache
-inputs:
-  bazel-cache:
-    required: true
-    description: A unique path for the Bazel cache.
-    type: string
-
-# By design, these actions will restore the latest cache for this branch/os,
-# and only save a new version if something has changed.  Initially this will
-# cause a lot of churn, since each test has a slightly different set of
-# repositories to download.  Over time though, since we don't upload no-op
-# changes, this should converge to a stable set of 3 caches per branch.  Every
-# run will update the current cache with a new test's repositories, until there
-# are no unique ones left.
-#
-# This saves asymptotic space, since each one of these can get up to ~500 MB
-# and Github prunes the cache after 10 GB.
-runs:
-  using: 'composite'
-  steps:
-    - name: Setup Bazel repository cache variables
-      shell: bash
-      run: |
-        REPOSITORY_CACHE_BASE=repository-cache-${{ github.base_ref || github.ref_name }}-${{ runner.os }}
-        echo "REPOSITORY_CACHE_BASE=$REPOSITORY_CACHE_BASE" >> $GITHUB_ENV
-        echo "REPOSITORY_CACHE_NAME=$REPOSITORY_CACHE_BASE-${{ inputs.bazel-cache}}-${{ github.sha }}" >> $GITHUB_ENV
-        echo "REPOSITORY_CACHE_PATH=.repository-cache" >> $GITHUB_ENV
-
-    - name: Restore Bazel repository cache
-      id: restore-cache
-      uses: actions/cache/restore@627f0f41f6904a5b1efbaed9f96d9eb58e92e920 # v3.2.4
-      with:
-        path: ${{ github.workspace }}/${{ env.REPOSITORY_CACHE_PATH }}
-        key: ${{ env.REPOSITORY_CACHE_NAME }}
-        restore-keys: ${{ env.REPOSITORY_CACHE_BASE }}
-
-    - name: Initialize BAZEL environment variable
-      if: ${{ steps.restore-cache.cache-hit }}
-      shell: bash
-      run: echo "REPOSITORY_CACHE_HASH=${{ hashFiles(format('{0}/**', env.REPOSITORY_CACHE_PATH)) }}" >> $GITHUB_ENV
diff --git a/.github/actions/internal/repository-cache-save/action.yml b/.github/actions/internal/repository-cache-save/action.yml
deleted file mode 100644
index 1324b2b..0000000
--- a/.github/actions/internal/repository-cache-save/action.yml
+++ /dev/null
@@ -1,19 +0,0 @@
-name: Restore Repository Cache
-description: Restore the Bazel repository cache from our github action cache
-
-# Note: this action will only work if repository-cache-restore has already
-# been called.  All bazel actions should specify the repository_cache parameter
-# using REPOSITORY_CACHE_PATH.
-#
-# We intentionally upload to REPOSITORY_CACHE_BASE to prevent a flood of new
-# caches on any change.  Only 1 job per os in each test run will be allowed to
-# update the cache because they're all trying to write to the same location.
-runs:
-  using: 'composite'
-  steps:
-    - name: Save modified Bazel repository cache
-      if: ${{ env.REPOSITORY_CACHE_HASH != hashFiles(format('{0}/**', env.REPOSITORY_CACHE_PATH)) }}
-      uses: actions/cache/save@627f0f41f6904a5b1efbaed9f96d9eb58e92e920 # v3.2.4
-      with:
-        path: ${{ github.workspace }}/${{ env.REPOSITORY_CACHE_PATH }}
-        key: ${{ env.REPOSITORY_CACHE_BASE }}-${{ github.sha }}
diff --git a/.github/actions/internal/setup-runner/action.yml b/.github/actions/internal/setup-runner/action.yml
deleted file mode 100644
index 64afdc3..0000000
--- a/.github/actions/internal/setup-runner/action.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-name: Setup CI Runner
-# TODO(b/267357823) Consider moving this to it's own repository so we can
-# include the call to actions/checkout.
-description: Setup any platform-specific adjustments we need to make for CI
-runs:
-  using: 'composite'
-  steps:
-    - name: Fix Windows line breaks
-      if: runner.os == 'Windows'
-      shell: bash
-      run: find . -type f -print0 | xargs -0 d2u 2>/dev/null
-
-    - name: Install bazelrc files
-      shell: bash
-      run: |
-        cp ci/*.bazelrc .
-        cp -f ${{ runner.os }}.bazelrc .bazelrc
diff --git a/.github/workflows/clear_caches.yml b/.github/workflows/clear_caches.yml
index 3ea213d..f7d7bce 100644
--- a/.github/workflows/clear_caches.yml
+++ b/.github/workflows/clear_caches.yml
@@ -2,8 +2,8 @@
 
 on:
   schedule:
-    # Run every 1st of the month at 10 AM UTC (2 AM PDT)
-    - cron: 0 10 1 * *
+    # Run every 4 months at 10 AM UTC (2 AM PDT)
+    - cron: 0 10 1 */4 *
 
   # manual
   workflow_dispatch:
diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml
index 26b4a32..84b0706 100644
--- a/.github/workflows/codespell.yml
+++ b/.github/workflows/codespell.yml
@@ -24,4 +24,4 @@
         with:
           check_filenames: true
           skip: ./.git,./third_party,./conformance/third_party,*.snk,*.pb,*.pb.cc,*.pb.h,./src/google/protobuf/testdata,./objectivec/Tests,./python/compatibility_tests/v2.5.0/tests/google/protobuf/internal,./.github/workflows/codespell.yml
-          ignore_words_list: "alow,alse,atleast,ba,chec,cleare,copyable,cloneable,dedup,dur,errorprone,falsy,files',fo,fundementals,hel,importd,inout,leapyear,nd,nin,ois,ons,parseable,process',ro,te,testof,ue,unparseable,wasn,wee,gae,keyserver,objext,od,optin,streem,sur,falsy"
+          ignore_words_list: "alow,alse,atleast,ba,chec,cleare,copyable,cloneable,crate,dedup,dur,errorprone,falsy,files',fo,fundementals,hel,importd,inout,leapyear,nd,nin,ois,ons,parseable,process',ro,te,testof,ue,unparseable,wasn,wee,gae,keyserver,objext,od,optin,streem,sur,falsy,cleary"
diff --git a/.github/workflows/staleness_check.yml b/.github/workflows/staleness_check.yml
index 022d64e..f04621e 100644
--- a/.github/workflows/staleness_check.yml
+++ b/.github/workflows/staleness_check.yml
@@ -4,6 +4,7 @@
   schedule:
     # Run daily at 10 AM UTC (2 AM PDT)
     - cron: 0 10 * * *
+  workflow_dispatch:
 
 permissions: {}
 jobs:
@@ -16,6 +17,7 @@
 
     name: Test staleness ${{ matrix.os.name }} ${{ matrix.branch}}
     runs-on: ${{ matrix.os.value }}
+    if: ${{ github.event.repository.full_name == 'protocolbuffers/protobuf' || matrix.branch == 'main '}}
     steps:
       - name: Checkout ${{ matrix.branch }}
         uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
@@ -23,7 +25,7 @@
           ref: ${{ matrix.branch}}
 
       - name: Run all staleness tests
-        uses: ./.github/actions/bazel
+        uses: protocolbuffers/protobuf-ci/bazel@v1
         with:
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
           bazel-cache: staleness_check/${{ matrix.branch}}_${{ matrix.os.value }}
diff --git a/.github/workflows/staleness_refresh.yml b/.github/workflows/staleness_refresh.yml
index 8f083fd..c53e4ed 100644
--- a/.github/workflows/staleness_refresh.yml
+++ b/.github/workflows/staleness_refresh.yml
@@ -8,6 +8,7 @@
       # The 21.x branch predates support for auto-generation, so we make sure
       # to exclude it.
       - '!21.x'
+  workflow_dispatch:
 
 permissions: {}
 jobs:
@@ -31,4 +32,4 @@
       - name: Configure name and email address in Git
         run: cd ${{ github.workspace }} && git config user.name "Protobuf Team Bot" && git config user.email "protobuf-team-bot@google.com"
       - name: Commit and push update
-        run: cd ${{ github.workspace }} && ./push_auto_update.sh
+        run: cd ${{ github.workspace }} && ./ci/push_auto_update.sh
diff --git a/.github/workflows/test_cpp.yml b/.github/workflows/test_cpp.yml
index c46a723..9dc1f50 100644
--- a/.github/workflows/test_cpp.yml
+++ b/.github/workflows/test_cpp.yml
@@ -44,7 +44,7 @@
         with:
           ref: ${{ inputs.safe-checkout }}
       - name: Run tests
-        uses: ./.github/actions/bazel-docker
+        uses: protocolbuffers/protobuf-ci/bazel-docker@v1
         with:
           image: ${{ matrix.image }}
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
@@ -80,7 +80,6 @@
         with:
           image: us-docker.pkg.dev/protobuf-build/containers/test/linux/emulation:${{ matrix.arch }}-3af05275178e16af30961976af126eabbbb2c733
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
-          skip-staleness-check: true
           entrypoint: bash
           command: >
             -c "set -ex;
@@ -123,12 +122,12 @@
           ref: ${{ inputs.safe-checkout }}
 
       - name: Setup ccache
-        uses: ./.github/actions/ccache
+        uses: protocolbuffers/protobuf-ci/ccache@v1
         with:
           cache-prefix: linux-cmake-${{ matrix.name }}
 
       - name: Run tests
-        uses: ./.github/actions/docker
+        uses: protocolbuffers/protobuf-ci/docker@v1
         with:
           image: us-docker.pkg.dev/protobuf-build/containers/test/linux/cmake@sha256:e0eb6c69b7551d89f0dbdbe11906077a1d501229c28db39623b945e0c5d7029a
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
@@ -145,12 +144,12 @@
           ref: ${{ inputs.safe-checkout }}
 
       - name: Setup ccache
-        uses: ./.github/actions/ccache
+        uses: protocolbuffers/protobuf-ci/ccache@v1
         with:
           cache-prefix: linux-cmake-install
 
       - name: Run tests
-        uses: ./.github/actions/docker
+        uses: protocolbuffers/protobuf-ci/docker@v1
         with:
           image: us-docker.pkg.dev/protobuf-build/containers/test/linux/cmake@sha256:e0eb6c69b7551d89f0dbdbe11906077a1d501229c28db39623b945e0c5d7029a
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
@@ -174,12 +173,12 @@
           ref: ${{ inputs.safe-checkout }}
 
       - name: Setup ccache
-        uses: ./.github/actions/ccache
+        uses: protocolbuffers/protobuf-ci/ccache@v1
         with:
           cache-prefix: linux-cmake-32-bit
 
       - name: Run tests
-        uses: ./.github/actions/docker
+        uses: protocolbuffers/protobuf-ci/docker@v1
         with:
           image: us-docker.pkg.dev/protobuf-build/containers/test/linux/32bit@sha256:f99f051daa8b12f4ebad5927f389bc71372f771ab080290ab451cbaf1648f9ea
           platform: linux/386
@@ -212,7 +211,7 @@
         with:
           ref: ${{ inputs.safe-checkout }}
       - name: Run tests
-        uses: ./.github/actions/bazel
+        uses: protocolbuffers/protobuf-ci/bazel@v1
         with:
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
           bazel: test ${{ matrix.bazel }}
@@ -254,14 +253,14 @@
           ref: ${{ inputs.safe-checkout }}
 
       - name: Setup ccache
-        uses: ./.github/actions/ccache
+        uses: protocolbuffers/protobuf-ci/ccache@v1
         with:
           cache-prefix: ${{ matrix.name }}
 
       # Install phase.
       - name: Configure CMake for install
         if: matrix.install-flags
-        uses: ./.github/actions/bash
+        uses: protocolbuffers/protobuf-ci/bash@v1
         with:
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
           command: cmake . ${{ matrix.install-flags }} ${{ env.CCACHE_CMAKE_FLAGS }}
@@ -283,7 +282,7 @@
         run: cmake --build . --target clean && rm CMakeCache.txt
 
       - name: Configure CMake
-        uses: ./.github/actions/bash
+        uses: protocolbuffers/protobuf-ci/bash@v1
         with:
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
           command: cmake . ${{ matrix.flags }} ${{ env.CCACHE_CMAKE_FLAGS }}
diff --git a/.github/workflows/test_csharp.yml b/.github/workflows/test_csharp.yml
index 1fe79d4..c9e651e 100644
--- a/.github/workflows/test_csharp.yml
+++ b/.github/workflows/test_csharp.yml
@@ -18,7 +18,7 @@
         with:
           ref: ${{ inputs.safe-checkout }}
       - name: Run tests
-        uses: ./.github/actions/bazel-docker
+        uses: protocolbuffers/protobuf-ci/bazel-docker@v1
         with:
           image: us-docker.pkg.dev/protobuf-build/containers/test/linux/csharp:3.1.415-6.0.100-508417e5215994ade7585d28ba3aad681a25fa5d
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
@@ -57,7 +57,7 @@
       - name: Build protobuf C# tests under x86_64 docker image
         # Tests are built "dotnet publish" because we want all the dependencies to the copied to the destination directory
         # (we want to avoid references to ~/.nuget that won't be available in the subsequent docker run)
-        uses: ./.github/actions/docker
+        uses: protocolbuffers/protobuf-ci/docker@v1
         with:
           image: mcr.microsoft.com/dotnet/sdk:6.0.100-bullseye-slim
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
@@ -74,7 +74,7 @@
         #   running under current user's UID and GID. To be able to do that, we need to provide a home directory for the user
         #   otherwise the UID would be homeless under the docker container and pip install wouldn't work. For simplicity,
         #   we just run map the user's home to a throwaway temporary directory
-        uses: ./.github/actions/docker
+        uses: protocolbuffers/protobuf-ci/docker@v1
         with:
           image: mcr.microsoft.com/dotnet/sdk:6.0.100-bullseye-slim-arm64v8
           skip-staleness-check: true
diff --git a/.github/workflows/test_java.yml b/.github/workflows/test_java.yml
index cea3223..f169794 100644
--- a/.github/workflows/test_java.yml
+++ b/.github/workflows/test_java.yml
@@ -39,7 +39,7 @@
         with:
           ref: ${{ inputs.safe-checkout }}
       - name: Run tests
-        uses: ./.github/actions/bazel-docker
+        uses: protocolbuffers/protobuf-ci/bazel-docker@v1
         with:
           image: ${{ matrix.image }}
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
@@ -55,7 +55,7 @@
         with:
           ref: ${{ inputs.safe-checkout }}
       - name: Build protoc
-        uses: ./.github/actions/bazel
+        uses: protocolbuffers/protobuf-ci/bazel@v1
         with:
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
           bazel-cache: java_linux_linkage
diff --git a/.github/workflows/test_objectivec.yml b/.github/workflows/test_objectivec.yml
index 4093b18..019bd4c 100644
--- a/.github/workflows/test_objectivec.yml
+++ b/.github/workflows/test_objectivec.yml
@@ -36,13 +36,13 @@
           ref: ${{ inputs.safe-checkout }}
 
       - name: Setup ccache
-        uses: ./.github/actions/ccache
+        uses: protocolbuffers/protobuf-ci/ccache@v1
         with:
           cache-prefix: objectivec_${{ matrix.platform }}_${{ matrix.xc_config }}
           support-modules: true
 
       - name: Run tests
-        uses: ./.github/actions/bash
+        uses: protocolbuffers/protobuf-ci/bash@v1
         env:
           CC: ${{ github.workspace }}/ci/clang_wrapper
           CXX: ${{ github.workspace }}/ci/clang_wrapper++
@@ -78,7 +78,7 @@
         with:
           ref: ${{ inputs.safe-checkout }}
       - name: Pod lib lint
-        uses: ./.github/actions/bash
+        uses: protocolbuffers/protobuf-ci/bash@v1
         with:
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
           command: |
@@ -107,7 +107,7 @@
         with:
           ref: ${{ inputs.safe-checkout }}
       - name: Run tests
-        uses: ./.github/actions/bazel
+        uses: protocolbuffers/protobuf-ci/bazel@v1
         with:
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
           bazel: test ${{ matrix.config.flags }} ${{ matrix.bazel_targets }}
diff --git a/.github/workflows/test_php.yml b/.github/workflows/test_php.yml
index fda266f..e3dbe4f 100644
--- a/.github/workflows/test_php.yml
+++ b/.github/workflows/test_php.yml
@@ -43,7 +43,7 @@
           submodules: recursive
           ref: ${{ inputs.safe-checkout }}
       - name: Run tests
-        uses: ./.github/actions/docker
+        uses: protocolbuffers/protobuf-ci/docker@v1
         with:
           image: us-docker.pkg.dev/protobuf-build/containers/test/linux/php:${{ matrix.version }}-508417e5215994ade7585d28ba3aad681a25fa5d
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
@@ -78,14 +78,14 @@
 
       - name: Cross compile protoc for i386
         id: cross-compile
-        uses: ./.github/actions/cross-compile-protoc
+        uses: protocolbuffers/protobuf-ci/cross-compile-protoc@v1
         with:
           image: us-docker.pkg.dev/protobuf-build/containers/common/linux/bazel:5.1.1-6361b3a6e5c97e9951d03a4de28542fc45f1adab
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
           architecture: linux-i386
 
       - name: Run tests
-        uses: ./.github/actions/docker
+        uses: protocolbuffers/protobuf-ci/docker@v1
         with:
           image: ${{ env.image }}
           skip-staleness-check: true
@@ -110,14 +110,14 @@
 
       - name: Cross compile protoc for aarch64
         id: cross-compile
-        uses: ./.github/actions/cross-compile-protoc
+        uses: protocolbuffers/protobuf-ci/cross-compile-protoc@v1
         with:
           image: us-docker.pkg.dev/protobuf-build/containers/common/linux/bazel:5.1.1-6361b3a6e5c97e9951d03a4de28542fc45f1adab
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
           architecture: linux-aarch64
 
       - name: Run tests
-        uses: ./.github/actions/docker
+        uses: protocolbuffers/protobuf-ci/docker@v1
         with:
           image: us-docker.pkg.dev/protobuf-build/containers/test/linux/php-aarch64:0cc100b6e03d14c1e8f71ae794dc162ed122fe31@sha256:77b70feba68dced1f0fd21b52a08d3d2e0c5c797bfe68435a0038ce87ecfd310
           platform: linux/arm64
@@ -159,7 +159,7 @@
         run: php --version | grep ${{ matrix.version }} || (echo "Invalid PHP version - $(php --version)" && exit 1)
 
       - name: Run tests
-        uses: ./.github/actions/bazel
+        uses: protocolbuffers/protobuf-ci/bazel@v1
         with:
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
           bazel-cache: php_macos/${{ matrix.version }}
@@ -173,7 +173,7 @@
             popd
 
       - name: Run conformance tests
-        uses: ./.github/actions/bazel
+        uses: protocolbuffers/protobuf-ci/bazel@v1
         with:
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
           bazel-cache: php_macos/${{ matrix.version }}
diff --git a/.github/workflows/test_php_ext.yml b/.github/workflows/test_php_ext.yml
index 901512c..993e0da 100644
--- a/.github/workflows/test_php_ext.yml
+++ b/.github/workflows/test_php_ext.yml
@@ -12,45 +12,50 @@
   contents: read  #  to fetch code (actions/checkout)
 
 jobs:
-  build-php:
-    name: Build
+  package:
+    name: Package
     runs-on: ubuntu-latest
-    container: ${{ matrix.php-image }}
-    strategy:
-      matrix:
-        php-image:
-          - php:7.4-cli
-          - php:8.1-cli
-    # TODO(b/266868629) Dockerize these instead of installing all the
-    # dependencies on each run.
     steps:
-      - name: Install python3
-        run: |
-          apt-get update -q
-          apt-get install -qy python3
-      - name: Install bazel
-        run: |
-          apt-get install -qy wget
-          mkdir $HOME/bin
-          wget -O $HOME/bin/bazel https://github.com/bazelbuild/bazel/releases/download/5.3.2/bazel-5.3.2-linux-x86_64
-          chmod a+x $HOME/bin/bazel
-      - name: Install git
-        run: |
-          apt-get install -qy --no-install-recommends git
       - name: Checkout
         uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
         with:
           ref: ${{ inputs.safe-checkout }}
-          submodules: recursive
-      - name: Create package
-        run: |
-          cd $GITHUB_WORKSPACE
-          rm -rf bazel-bin/php/protobuf-*.tgz
-          $HOME/bin/bazel build php:release
+
+      - name: Package extension
+        uses: protocolbuffers/protobuf-ci/bazel@v1
+        with:
+          credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
+          bazel-cache: php_ext/${{ matrix.version }}
+          bash: >
+            bazel build //php:release $BAZEL_FLAGS;
+            cp bazel-bin/php/protobuf-*.tgz .
+
+      - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
+        with:
+          name: protobuf-php-release
+          path: protobuf-*.tgz
+
+  build:
+    needs: [package]
+    strategy:
+      fail-fast: false   # Don't cancel all jobs if one fails.
+      matrix:
+        include:
+          - php-image: php:7.4-cli
+            version: "7.4.18-dbg"
+          - php-image: php:8.1-cli
+            version: "8.1.14"
+    name: Build ${{ matrix.version }}
+    runs-on: ubuntu-latest
+    container: ${{ matrix.php-image }}
+    steps:
+      - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a
+        with:
+          name: protobuf-php-release
       - name: Compile extension
         run: |
           cd /tmp
-          MAKE="make -j$(nproc)" pecl install $GITHUB_WORKSPACE/bazel-bin/php/protobuf-*.tgz
+          MAKE="make -j$(nproc)" pecl install $GITHUB_WORKSPACE/protobuf-*.tgz
       - name: Enable extension
         run: docker-php-ext-enable protobuf
       - name: Inspect extension
diff --git a/.github/workflows/test_python.yml b/.github/workflows/test_python.yml
index 3547635..36f4e5a 100644
--- a/.github/workflows/test_python.yml
+++ b/.github/workflows/test_python.yml
@@ -37,7 +37,7 @@
         with:
           ref: ${{ inputs.safe-checkout }}
       - name: Run tests
-        uses: ./.github/actions/bazel-docker
+        uses: protocolbuffers/protobuf-ci/bazel-docker@v1
         with:
           image: ${{ matrix.image || format('us-docker.pkg.dev/protobuf-build/containers/test/linux/python:{0}-508417e5215994ade7585d28ba3aad681a25fa5d', matrix.version) }}
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
@@ -65,7 +65,6 @@
       - name: Checkout pending changes
         uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
         with:
-          submodules: recursive
           ref: ${{ inputs.safe-checkout }}
 
       - name: Pin Python version
@@ -83,7 +82,7 @@
           source venv/bin/activate
 
       - name: Run tests
-        uses: ./.github/actions/bazel
+        uses: protocolbuffers/protobuf-ci/bazel@v1
         env:
           KOKORO_PYTHON_VERSION: ${{ matrix.version }}
         with:
diff --git a/.github/workflows/test_ruby.yml b/.github/workflows/test_ruby.yml
index 31b8f79..33906f5 100644
--- a/.github/workflows/test_ruby.yml
+++ b/.github/workflows/test_ruby.yml
@@ -32,7 +32,7 @@
         with:
           ref: ${{ inputs.safe-checkout }}
       - name: Run tests
-        uses: ./.github/actions/bazel-docker
+        uses: protocolbuffers/protobuf-ci/bazel-docker@v1
         with:
           image: ${{ matrix.image || format('us-docker.pkg.dev/protobuf-build/containers/test/linux/ruby:{0}-{1}-508417e5215994ade7585d28ba3aad681a25fa5d', matrix.ruby, matrix.bazel) }}
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
@@ -50,14 +50,14 @@
 
       - name: Cross compile protoc for aarch64
         id: cross-compile
-        uses: ./.github/actions/cross-compile-protoc
+        uses: protocolbuffers/protobuf-ci/cross-compile-protoc@v1
         with:
           image: us-docker.pkg.dev/protobuf-build/containers/common/linux/bazel:5.1.1-6361b3a6e5c97e9951d03a4de28542fc45f1adab
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
           architecture: linux-aarch64
 
       - name: Run tests
-        uses: ./.github/actions/docker
+        uses: protocolbuffers/protobuf-ci/docker@v1
         with:
           image: arm64v8/ruby:2.7.3-buster
           skip-staleness-check: true
@@ -84,7 +84,6 @@
       - name: Checkout pending changes
         uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
         with:
-          submodules: recursive
           ref: ${{ inputs.safe-checkout }}
 
       - name: Pin Ruby version
@@ -96,7 +95,7 @@
         run: ruby --version | grep ${{ matrix.version }} || (echo "Invalid Ruby version - $(ruby --version)" && exit 1)
 
       - name: Run tests
-        uses: ./.github/actions/bazel
+        uses: protocolbuffers/protobuf-ci/bazel@v1
         with:
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
           bazel-cache: ruby_macos/${{ matrix.version }}
@@ -124,7 +123,7 @@
         with:
           ref: ${{ inputs.safe-checkout }}
       - name: Run tests
-        uses: ./.github/actions/bazel-docker
+        uses: protocolbuffers/protobuf-ci/bazel-docker@v1
         with:
           image: us-docker.pkg.dev/protobuf-build/containers/test/linux/ruby:${{ matrix.ruby }}-${{ matrix.bazel }}-508417e5215994ade7585d28ba3aad681a25fa5d
           credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
diff --git a/.github/workflows/test_runner.yml b/.github/workflows/test_runner.yml
index a99d090..ce3268a 100644
--- a/.github/workflows/test_runner.yml
+++ b/.github/workflows/test_runner.yml
@@ -21,8 +21,8 @@
     branches:
       - main
       - '[0-9]+.x'
-      # The 21.x branch still uses Kokoro
-      - '!21.x'
+      # The 21.x and 22.x branches still use Kokoro
+      - '!2[12].x'
       # For testing purposes so we can stage this on the `gha` branch.
       - gha
 
@@ -31,8 +31,8 @@
     branches:
       - main
       - '[0-9]+.x'
-      # The 21.x branch still uses Kokoro
-      - '!21.x'
+      # The 21.x and 22.x branches still use Kokoro
+      - '!2[12].x'
       # For testing purposes so we can stage this on the `gha` branch.
       - gha
 
@@ -41,7 +41,7 @@
     branches:
       - main
       - '[0-9]+.x'
-      # The 21.x branch still uses Kokoro
+      # The 21.x branch still use Kokoro
       - '!21.x'
       # For testing purposes so we can stage this on the `gha` branch.
       - gha
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 544ce94..c042073 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,29 +1,18 @@
 # Minimum CMake required
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.10)
 
 if(protobuf_VERBOSE)
   message(STATUS "Protocol Buffers Configuring...")
 endif()
 
-# CMake policies
-cmake_policy(SET CMP0022 NEW)
-# On MacOS use @rpath/ for target's install name prefix path
-if (POLICY CMP0042)
-  cmake_policy(SET CMP0042 NEW)
-endif ()
-# Clear VERSION variables when no VERSION is given to project()
-if(POLICY CMP0048)
-  cmake_policy(SET CMP0048 NEW)
-endif()
 # MSVC runtime library flags are selected by an abstraction.
+# New in CMake 3.15: https://cmake.org/cmake/help/latest/policy/CMP0091.html
 if(POLICY CMP0091)
   cmake_policy(SET CMP0091 NEW)
 endif()
-# Honor visibility properties for all target types.
-if(POLICY CMP0063)
-  cmake_policy(SET CMP0063 NEW)
-endif()
-# option() honor variables
+
+# option() honors normal variables
+# New in CMake 3.13: https://cmake.org/cmake/help/latest/policy/CMP0077.html
 if (POLICY CMP0077)
   cmake_policy(SET CMP0077 NEW)
 endif (POLICY CMP0077)
diff --git a/README.md b/README.md
index 3bfeb0f..c84ab2b 100644
--- a/README.md
+++ b/README.md
@@ -3,14 +3,12 @@
 
 Copyright 2008 Google Inc.
 
-[Protocol Buffers documentation](https://developers.google.com/protocol-buffers/)
-
 Overview
 --------
 
 Protocol Buffers (a.k.a., protobuf) are Google's language-neutral,
 platform-neutral, extensible mechanism for serializing structured data. You
-can find [protobuf's documentation on the Google Developers site](https://developers.google.com/protocol-buffers/).
+can learn more about it in [protobuf's documentation](https://protobuf.dev).
 
 This README file contains protobuf installation instructions. To install
 protobuf, you need to install the protocol compiler (used to compile .proto
@@ -64,7 +62,7 @@
 -----------
 
 The best way to learn how to use protobuf is to follow the [tutorials in our
-developer guide](https://developers.google.com/protocol-buffers/docs/tutorials).
+developer guide](https://protobuf.dev/getting-started).
 
 If you want to learn from code examples, take a look at the examples in the
 [examples](examples) directory.
@@ -72,11 +70,16 @@
 Documentation
 -------------
 
-The complete documentation is available via the [Protocol Buffers documentation](https://developers.google.com/protocol-buffers/).
+The complete documentation is available at the [Protocol Buffers doc site](https://protobuf.dev).
+
+Support Policy
+--------------
+
+Read about our [version support policy](https://protobuf.dev/version-support/)
+to stay current on support timeframes for the language libraries.
 
 Developer Community
 -------------------
 
 To be alerted to upcoming changes in Protocol Buffers and connect with protobuf developers and users,
 [join the Google Group](https://groups.google.com/g/protobuf).
-
diff --git a/WORKSPACE b/WORKSPACE
index 2301093..af7d63a 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -136,4 +136,4 @@
 
 load("@rules_rust//rust:repositories.bzl", "rules_rust_dependencies", "rust_register_toolchains")
 rules_rust_dependencies()
-rust_register_toolchains()
+rust_register_toolchains(edition = "2021")
diff --git a/build_defs/upb.patch b/build_defs/upb.patch
index 809fb70..ec1f28b 100644
--- a/build_defs/upb.patch
+++ b/build_defs/upb.patch
@@ -9,4 +9,22 @@
  # end:github_only
  
  def _upbc(stage):
- 
\ No newline at end of file
+ 
+--- cmake/build_defs.bzl
++++ cmake/build_defs.bzl
+@@ -25,7 +25,7 @@
+ 
+ """Bazel support functions related to CMake support."""
+ 
+-def staleness_test(name, outs, generated_pattern, target_files = None, **kwargs):
++def staleness_test(name, outs, generated_pattern, target_files = None, tags = [], **kwargs):
+     """Tests that checked-in file(s) match the contents of generated file(s).
+ 
+     The resulting test will verify that all output files exist and have the
+@@ -72,5 +72,6 @@ def staleness_test(name, outs, generated_pattern, target_files = None, **kwargs)
+         deps = [
+             Label("//cmake:staleness_test_lib"),
+         ],
++        tags = ["staleness_test"] + tags,
+         **kwargs
+     )
diff --git a/push_auto_update.sh b/ci/push_auto_update.sh
similarity index 94%
rename from push_auto_update.sh
rename to ci/push_auto_update.sh
index 9972da6..641525c 100755
--- a/push_auto_update.sh
+++ b/ci/push_auto_update.sh
@@ -8,7 +8,7 @@
 set -ex
 
 # Cd to the repo root.
-cd $(dirname -- "$0")
+cd $(dirname -- "$0")/..
 
 previous_commit_title=$(git log -1 --pretty='%s')
 
@@ -42,4 +42,4 @@
 git pull --rebase
 git add -A
 git diff --staged --quiet || git commit -am "$commit_message"
-git push
+git push || echo "Conflicting commit hit, retrying in next job..."
diff --git a/cmake/tests.cmake b/cmake/tests.cmake
index 27f2eac..2608060 100644
--- a/cmake/tests.cmake
+++ b/cmake/tests.cmake
@@ -131,7 +131,8 @@
 )
 
 add_test(NAME lite-test
-  COMMAND lite-test ${protobuf_GTEST_ARGS})
+  COMMAND lite-test ${protobuf_GTEST_ARGS}
+  WORKING_DIRECTORY ${protobuf_SOURCE_DIR})
 
 add_custom_target(full-test
   COMMAND tests
@@ -139,7 +140,8 @@
   WORKING_DIRECTORY ${protobuf_SOURCE_DIR})
 
 add_test(NAME full-test
-  COMMAND tests ${protobuf_GTEST_ARGS})
+  COMMAND tests ${protobuf_GTEST_ARGS}
+  WORKING_DIRECTORY ${protobuf_SOURCE_DIR})
 
 # For test purposes, remove headers that should already be installed.  This
 # prevents accidental conflicts and also version skew (since local headers take
diff --git a/conformance/binary_json_conformance_suite.cc b/conformance/binary_json_conformance_suite.cc
index 90991f1..21b6592 100644
--- a/conformance/binary_json_conformance_suite.cc
+++ b/conformance/binary_json_conformance_suite.cc
@@ -1431,12 +1431,12 @@
 
     TestIllegalTags();
 
-    int64 kInt64Min = -9223372036854775808ULL;
-    int64 kInt64Max = 9223372036854775807ULL;
-    uint64 kUint64Max = 18446744073709551615ULL;
-    int32 kInt32Max = 2147483647;
-    int32 kInt32Min = -2147483648;
-    uint32 kUint32Max = 4294967295UL;
+    int64_t kInt64Min = -9223372036854775808ULL;
+    int64_t kInt64Max = 9223372036854775807ULL;
+    uint64_t kUint64Max = 18446744073709551615ULL;
+    int32_t kInt32Max = 2147483647;
+    int32_t kInt32Min = -2147483648;
+    uint32_t kUint32Max = 4294967295UL;
 
     TestValidDataForType(
         FieldDescriptor::TYPE_DOUBLE,
@@ -2318,11 +2318,11 @@
   {
     TestAllTypesProto3 message;
     message.set_optional_double(
-        WireFormatLite::DecodeDouble(int64{0x7FFA123456789ABC}));
+        WireFormatLite::DecodeDouble(int64_t{0x7FFA123456789ABC}));
     RunValidJsonTestWithProtobufInput("DoubleFieldNormalizeQuietNan", REQUIRED,
                                       message, "optional_double: nan");
     message.set_optional_double(
-        WireFormatLite::DecodeDouble(uint64{0xFFFBCBA987654321}));
+        WireFormatLite::DecodeDouble(uint64_t{0xFFFBCBA987654321}));
     RunValidJsonTestWithProtobufInput("DoubleFieldNormalizeSignalingNan",
                                       REQUIRED, message,
                                       "optional_double: nan");
diff --git a/conformance/conformance_test.cc b/conformance/conformance_test.cc
index e5d2907..aba07a0 100644
--- a/conformance/conformance_test.cc
+++ b/conformance/conformance_test.cc
@@ -162,12 +162,19 @@
 }
 
 string ConformanceTestSuite::ConformanceRequestSetting::GetTestName() const {
-  string rname = prototype_message_.GetDescriptor()->file()->syntax() ==
-                         FileDescriptor::SYNTAX_PROTO3
-                     ? "Proto3"
-                     : "Proto2";
+  string rname;
+  switch (prototype_message_.GetDescriptor()->file()->syntax()) {
+    case FileDescriptor::SYNTAX_PROTO3:
+      rname = ".Proto3.";
+      break;
+    case FileDescriptor::SYNTAX_PROTO2:
+      rname = ".Proto2.";
+      break;
+    default:
+      break;
+  }
 
-  return absl::StrCat(ConformanceLevelToString(level_), ".", rname, ".",
+  return absl::StrCat(ConformanceLevelToString(level_), rname,
                       InputFormatString(input_format_), ".", test_name_, ".",
                       OutputFormatString(output_format_));
 }
diff --git a/csharp/BUILD.bazel b/csharp/BUILD.bazel
index 23fbe41..2af7d06 100644
--- a/csharp/BUILD.bazel
+++ b/csharp/BUILD.bazel
@@ -65,9 +65,11 @@
     srcs = [
         ":srcs",
         "src/Google.Protobuf.sln",
+        "//conformance:conformance_csharp_proto",
         "//csharp/src/Google.Protobuf.Conformance:srcs",
     ],
     cmd = """
+        cp $(rootpath //conformance:conformance_csharp_proto) `dirname $(location src/Google.Protobuf.sln)`/Google.Protobuf.Conformance/
         pushd `dirname $(location src/Google.Protobuf.sln)`/..
         dotnet restore src/Google.Protobuf.sln
         dotnet build -c Release src/Google.Protobuf.sln
diff --git a/csharp/generate_protos.sh b/csharp/generate_protos.sh
index cd682c2..17b1640 100755
--- a/csharp/generate_protos.sh
+++ b/csharp/generate_protos.sh
@@ -79,3 +79,8 @@
 $PROTOC -Iexamples -Isrc --csharp_out=csharp/src/AddressBook \
     --csharp_opt=file_extension=.pb.cs \
     examples/addressbook.proto
+
+# Conformance tests
+$PROTOC -I. --csharp_out=csharp/src/Google.Protobuf.Conformance \
+    --csharp_opt=file_extension=.pb.cs \
+    conformance/conformance.proto
diff --git a/csharp/src/Google.Protobuf.Conformance/BUILD.bazel b/csharp/src/Google.Protobuf.Conformance/BUILD.bazel
index 12ad0f7..0b5e2c8 100644
--- a/csharp/src/Google.Protobuf.Conformance/BUILD.bazel
+++ b/csharp/src/Google.Protobuf.Conformance/BUILD.bazel
@@ -1,3 +1,4 @@
+load("//:protobuf.bzl", "internal_csharp_proto_library")
 load("//build_defs:internal_shell.bzl", "inline_sh_binary")
 load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix")
 
@@ -37,7 +38,6 @@
 filegroup(
     name = "srcs",
     srcs = [
-        "Conformance.cs",
         "Program.cs",
         "Google.Protobuf.Conformance.csproj",
     ],
@@ -88,7 +88,6 @@
     srcs = [
         "BUILD.bazel",
         "Google.Protobuf.Conformance.csproj",
-        "Conformance.cs",
         "Program.cs",
     ],
     strip_prefix = strip_prefix.from_root(""),
diff --git a/csharp/src/Google.Protobuf.Conformance/Conformance.cs b/csharp/src/Google.Protobuf.Conformance/Conformance.pb.cs
similarity index 78%
rename from csharp/src/Google.Protobuf.Conformance/Conformance.cs
rename to csharp/src/Google.Protobuf.Conformance/Conformance.pb.cs
index 77fd9c9..3b4ff53 100644
--- a/csharp/src/Google.Protobuf.Conformance/Conformance.cs
+++ b/csharp/src/Google.Protobuf.Conformance/Conformance.pb.cs
@@ -1,6 +1,6 @@
 // <auto-generated>
 //     Generated by the protocol buffer compiler.  DO NOT EDIT!
-//     source: conformance.proto
+//     source: conformance/conformance.proto
 // </auto-generated>
 #pragma warning disable 1591, 0612, 3021, 8981
 #region Designer generated code
@@ -11,11 +11,11 @@
 using scg = global::System.Collections.Generic;
 namespace Conformance {
 
-  /// <summary>Holder for reflection information generated from conformance.proto</summary>
+  /// <summary>Holder for reflection information generated from conformance/conformance.proto</summary>
   public static partial class ConformanceReflection {
 
     #region Descriptor
-    /// <summary>File descriptor for conformance.proto</summary>
+    /// <summary>File descriptor for conformance/conformance.proto</summary>
     public static pbr::FileDescriptor Descriptor {
       get { return descriptor; }
     }
@@ -24,34 +24,35 @@
     static ConformanceReflection() {
       byte[] descriptorData = global::System.Convert.FromBase64String(
           string.Concat(
-            "ChFjb25mb3JtYW5jZS5wcm90bxILY29uZm9ybWFuY2UiHQoKRmFpbHVyZVNl",
-            "dBIPCgdmYWlsdXJlGAEgAygJIuMCChJDb25mb3JtYW5jZVJlcXVlc3QSGgoQ",
-            "cHJvdG9idWZfcGF5bG9hZBgBIAEoDEgAEhYKDGpzb25fcGF5bG9hZBgCIAEo",
-            "CUgAEhYKDGpzcGJfcGF5bG9hZBgHIAEoCUgAEhYKDHRleHRfcGF5bG9hZBgI",
-            "IAEoCUgAEjgKF3JlcXVlc3RlZF9vdXRwdXRfZm9ybWF0GAMgASgOMhcuY29u",
-            "Zm9ybWFuY2UuV2lyZUZvcm1hdBIUCgxtZXNzYWdlX3R5cGUYBCABKAkSMAoN",
-            "dGVzdF9jYXRlZ29yeRgFIAEoDjIZLmNvbmZvcm1hbmNlLlRlc3RDYXRlZ29y",
-            "eRI+ChVqc3BiX2VuY29kaW5nX29wdGlvbnMYBiABKAsyHy5jb25mb3JtYW5j",
-            "ZS5Kc3BiRW5jb2RpbmdDb25maWcSHAoUcHJpbnRfdW5rbm93bl9maWVsZHMY",
-            "CSABKAhCCQoHcGF5bG9hZCLhAQoTQ29uZm9ybWFuY2VSZXNwb25zZRIVCgtw",
-            "YXJzZV9lcnJvchgBIAEoCUgAEhkKD3NlcmlhbGl6ZV9lcnJvchgGIAEoCUgA",
-            "EhcKDXJ1bnRpbWVfZXJyb3IYAiABKAlIABIaChBwcm90b2J1Zl9wYXlsb2Fk",
-            "GAMgASgMSAASFgoManNvbl9wYXlsb2FkGAQgASgJSAASEQoHc2tpcHBlZBgF",
-            "IAEoCUgAEhYKDGpzcGJfcGF5bG9hZBgHIAEoCUgAEhYKDHRleHRfcGF5bG9h",
-            "ZBgIIAEoCUgAQggKBnJlc3VsdCI3ChJKc3BiRW5jb2RpbmdDb25maWcSIQoZ",
-            "dXNlX2pzcGJfYXJyYXlfYW55X2Zvcm1hdBgBIAEoCCpQCgpXaXJlRm9ybWF0",
-            "Eg8KC1VOU1BFQ0lGSUVEEAASDAoIUFJPVE9CVUYQARIICgRKU09OEAISCAoE",
-            "SlNQQhADEg8KC1RFWFRfRk9STUFUEAQqjwEKDFRlc3RDYXRlZ29yeRIUChBV",
-            "TlNQRUNJRklFRF9URVNUEAASDwoLQklOQVJZX1RFU1QQARINCglKU09OX1RF",
-            "U1QQAhIkCiBKU09OX0lHTk9SRV9VTktOT1dOX1BBUlNJTkdfVEVTVBADEg0K",
-            "CUpTUEJfVEVTVBAEEhQKEFRFWFRfRk9STUFUX1RFU1QQBUIhCh9jb20uZ29v",
-            "Z2xlLnByb3RvYnVmLmNvbmZvcm1hbmNlYgZwcm90bzM="));
+            "Ch1jb25mb3JtYW5jZS9jb25mb3JtYW5jZS5wcm90bxILY29uZm9ybWFuY2Ui",
+            "HQoKRmFpbHVyZVNldBIPCgdmYWlsdXJlGAEgAygJIuMCChJDb25mb3JtYW5j",
+            "ZVJlcXVlc3QSGgoQcHJvdG9idWZfcGF5bG9hZBgBIAEoDEgAEhYKDGpzb25f",
+            "cGF5bG9hZBgCIAEoCUgAEhYKDGpzcGJfcGF5bG9hZBgHIAEoCUgAEhYKDHRl",
+            "eHRfcGF5bG9hZBgIIAEoCUgAEjgKF3JlcXVlc3RlZF9vdXRwdXRfZm9ybWF0",
+            "GAMgASgOMhcuY29uZm9ybWFuY2UuV2lyZUZvcm1hdBIUCgxtZXNzYWdlX3R5",
+            "cGUYBCABKAkSMAoNdGVzdF9jYXRlZ29yeRgFIAEoDjIZLmNvbmZvcm1hbmNl",
+            "LlRlc3RDYXRlZ29yeRI+ChVqc3BiX2VuY29kaW5nX29wdGlvbnMYBiABKAsy",
+            "Hy5jb25mb3JtYW5jZS5Kc3BiRW5jb2RpbmdDb25maWcSHAoUcHJpbnRfdW5r",
+            "bm93bl9maWVsZHMYCSABKAhCCQoHcGF5bG9hZCL6AQoTQ29uZm9ybWFuY2VS",
+            "ZXNwb25zZRIVCgtwYXJzZV9lcnJvchgBIAEoCUgAEhkKD3NlcmlhbGl6ZV9l",
+            "cnJvchgGIAEoCUgAEhcKDXRpbWVvdXRfZXJyb3IYCSABKAlIABIXCg1ydW50",
+            "aW1lX2Vycm9yGAIgASgJSAASGgoQcHJvdG9idWZfcGF5bG9hZBgDIAEoDEgA",
+            "EhYKDGpzb25fcGF5bG9hZBgEIAEoCUgAEhEKB3NraXBwZWQYBSABKAlIABIW",
+            "Cgxqc3BiX3BheWxvYWQYByABKAlIABIWCgx0ZXh0X3BheWxvYWQYCCABKAlI",
+            "AEIICgZyZXN1bHQiNwoSSnNwYkVuY29kaW5nQ29uZmlnEiEKGXVzZV9qc3Bi",
+            "X2FycmF5X2FueV9mb3JtYXQYASABKAgqUAoKV2lyZUZvcm1hdBIPCgtVTlNQ",
+            "RUNJRklFRBAAEgwKCFBST1RPQlVGEAESCAoESlNPThACEggKBEpTUEIQAxIP",
+            "CgtURVhUX0ZPUk1BVBAEKo8BCgxUZXN0Q2F0ZWdvcnkSFAoQVU5TUEVDSUZJ",
+            "RURfVEVTVBAAEg8KC0JJTkFSWV9URVNUEAESDQoJSlNPTl9URVNUEAISJAog",
+            "SlNPTl9JR05PUkVfVU5LTk9XTl9QQVJTSU5HX1RFU1QQAxINCglKU1BCX1RF",
+            "U1QQBBIUChBURVhUX0ZPUk1BVF9URVNUEAVCLwofY29tLmdvb2dsZS5wcm90",
+            "b2J1Zi5jb25mb3JtYW5jZaICC0NvbmZvcm1hbmNlYgZwcm90bzM="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
           new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Conformance.WireFormat), typeof(global::Conformance.TestCategory), }, null, new pbr::GeneratedClrTypeInfo[] {
             new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.FailureSet), global::Conformance.FailureSet.Parser, new[]{ "Failure" }, null, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.ConformanceRequest), global::Conformance.ConformanceRequest.Parser, new[]{ "ProtobufPayload", "JsonPayload", "JspbPayload", "TextPayload", "RequestedOutputFormat", "MessageType", "TestCategory", "JspbEncodingOptions", "PrintUnknownFields" }, new[]{ "Payload" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.ConformanceResponse), global::Conformance.ConformanceResponse.Parser, new[]{ "ParseError", "SerializeError", "RuntimeError", "ProtobufPayload", "JsonPayload", "Skipped", "JspbPayload", "TextPayload" }, new[]{ "Result" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.ConformanceResponse), global::Conformance.ConformanceResponse.Parser, new[]{ "ParseError", "SerializeError", "TimeoutError", "RuntimeError", "ProtobufPayload", "JsonPayload", "Skipped", "JspbPayload", "TextPayload" }, new[]{ "Result" }, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Conformance.JspbEncodingConfig), global::Conformance.JspbEncodingConfig.Parser, new[]{ "UseJspbArrayAnyFormat" }, null, null, null, null)
           }));
     }
@@ -89,7 +90,8 @@
     /// </summary>
     [pbr::OriginalName("JSON_IGNORE_UNKNOWN_PARSING_TEST")] JsonIgnoreUnknownParsingTest = 3,
     /// <summary>
-    /// Test jspb wire format. Only used inside Google. Opensource testees just skip it.
+    /// Test jspb wire format. Only used inside Google. Opensource testees just
+    /// skip it.
     /// </summary>
     [pbr::OriginalName("JSPB_TEST")] JspbTest = 4,
     /// <summary>
@@ -360,51 +362,107 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public pb::ByteString ProtobufPayload {
-      get { return payloadCase_ == PayloadOneofCase.ProtobufPayload ? (pb::ByteString) payload_ : pb::ByteString.Empty; }
+      get { return HasProtobufPayload ? (pb::ByteString) payload_ : pb::ByteString.Empty; }
       set {
         payload_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         payloadCase_ = PayloadOneofCase.ProtobufPayload;
       }
     }
+    /// <summary>Gets whether the "protobuf_payload" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasProtobufPayload {
+      get { return payloadCase_ == PayloadOneofCase.ProtobufPayload; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "protobuf_payload" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearProtobufPayload() {
+      if (HasProtobufPayload) {
+        ClearPayload();
+      }
+    }
 
     /// <summary>Field number for the "json_payload" field.</summary>
     public const int JsonPayloadFieldNumber = 2;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string JsonPayload {
-      get { return payloadCase_ == PayloadOneofCase.JsonPayload ? (string) payload_ : ""; }
+      get { return HasJsonPayload ? (string) payload_ : ""; }
       set {
         payload_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         payloadCase_ = PayloadOneofCase.JsonPayload;
       }
     }
+    /// <summary>Gets whether the "json_payload" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasJsonPayload {
+      get { return payloadCase_ == PayloadOneofCase.JsonPayload; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "json_payload" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearJsonPayload() {
+      if (HasJsonPayload) {
+        ClearPayload();
+      }
+    }
 
     /// <summary>Field number for the "jspb_payload" field.</summary>
     public const int JspbPayloadFieldNumber = 7;
     /// <summary>
-    /// Only used inside google.  Opensource testees just skip it.
+    /// Only used inside Google.  Opensource testees just skip it.
     /// </summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string JspbPayload {
-      get { return payloadCase_ == PayloadOneofCase.JspbPayload ? (string) payload_ : ""; }
+      get { return HasJspbPayload ? (string) payload_ : ""; }
       set {
         payload_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         payloadCase_ = PayloadOneofCase.JspbPayload;
       }
     }
+    /// <summary>Gets whether the "jspb_payload" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasJspbPayload {
+      get { return payloadCase_ == PayloadOneofCase.JspbPayload; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "jspb_payload" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearJspbPayload() {
+      if (HasJspbPayload) {
+        ClearPayload();
+      }
+    }
 
     /// <summary>Field number for the "text_payload" field.</summary>
     public const int TextPayloadFieldNumber = 8;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string TextPayload {
-      get { return payloadCase_ == PayloadOneofCase.TextPayload ? (string) payload_ : ""; }
+      get { return HasTextPayload ? (string) payload_ : ""; }
       set {
         payload_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         payloadCase_ = PayloadOneofCase.TextPayload;
       }
     }
+    /// <summary>Gets whether the "text_payload" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasTextPayload {
+      get { return payloadCase_ == PayloadOneofCase.TextPayload; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "text_payload" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearTextPayload() {
+      if (HasTextPayload) {
+        ClearPayload();
+      }
+    }
 
     /// <summary>Field number for the "requested_output_format" field.</summary>
     public const int RequestedOutputFormatFieldNumber = 3;
@@ -443,8 +501,8 @@
     private global::Conformance.TestCategory testCategory_ = global::Conformance.TestCategory.UnspecifiedTest;
     /// <summary>
     /// Each test is given a specific test category. Some category may need
-    /// specific support in testee programs. Refer to the definition of TestCategory
-    /// for more information.
+    /// specific support in testee programs. Refer to the definition of
+    /// TestCategory for more information.
     /// </summary>
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
@@ -541,10 +599,10 @@
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public override int GetHashCode() {
       int hash = 1;
-      if (payloadCase_ == PayloadOneofCase.ProtobufPayload) hash ^= ProtobufPayload.GetHashCode();
-      if (payloadCase_ == PayloadOneofCase.JsonPayload) hash ^= JsonPayload.GetHashCode();
-      if (payloadCase_ == PayloadOneofCase.JspbPayload) hash ^= JspbPayload.GetHashCode();
-      if (payloadCase_ == PayloadOneofCase.TextPayload) hash ^= TextPayload.GetHashCode();
+      if (HasProtobufPayload) hash ^= ProtobufPayload.GetHashCode();
+      if (HasJsonPayload) hash ^= JsonPayload.GetHashCode();
+      if (HasJspbPayload) hash ^= JspbPayload.GetHashCode();
+      if (HasTextPayload) hash ^= TextPayload.GetHashCode();
       if (RequestedOutputFormat != global::Conformance.WireFormat.Unspecified) hash ^= RequestedOutputFormat.GetHashCode();
       if (MessageType.Length != 0) hash ^= MessageType.GetHashCode();
       if (TestCategory != global::Conformance.TestCategory.UnspecifiedTest) hash ^= TestCategory.GetHashCode();
@@ -569,11 +627,11 @@
     #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
       output.WriteRawMessage(this);
     #else
-      if (payloadCase_ == PayloadOneofCase.ProtobufPayload) {
+      if (HasProtobufPayload) {
         output.WriteRawTag(10);
         output.WriteBytes(ProtobufPayload);
       }
-      if (payloadCase_ == PayloadOneofCase.JsonPayload) {
+      if (HasJsonPayload) {
         output.WriteRawTag(18);
         output.WriteString(JsonPayload);
       }
@@ -593,11 +651,11 @@
         output.WriteRawTag(50);
         output.WriteMessage(JspbEncodingOptions);
       }
-      if (payloadCase_ == PayloadOneofCase.JspbPayload) {
+      if (HasJspbPayload) {
         output.WriteRawTag(58);
         output.WriteString(JspbPayload);
       }
-      if (payloadCase_ == PayloadOneofCase.TextPayload) {
+      if (HasTextPayload) {
         output.WriteRawTag(66);
         output.WriteString(TextPayload);
       }
@@ -615,11 +673,11 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
-      if (payloadCase_ == PayloadOneofCase.ProtobufPayload) {
+      if (HasProtobufPayload) {
         output.WriteRawTag(10);
         output.WriteBytes(ProtobufPayload);
       }
-      if (payloadCase_ == PayloadOneofCase.JsonPayload) {
+      if (HasJsonPayload) {
         output.WriteRawTag(18);
         output.WriteString(JsonPayload);
       }
@@ -639,11 +697,11 @@
         output.WriteRawTag(50);
         output.WriteMessage(JspbEncodingOptions);
       }
-      if (payloadCase_ == PayloadOneofCase.JspbPayload) {
+      if (HasJspbPayload) {
         output.WriteRawTag(58);
         output.WriteString(JspbPayload);
       }
-      if (payloadCase_ == PayloadOneofCase.TextPayload) {
+      if (HasTextPayload) {
         output.WriteRawTag(66);
         output.WriteString(TextPayload);
       }
@@ -661,16 +719,16 @@
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public int CalculateSize() {
       int size = 0;
-      if (payloadCase_ == PayloadOneofCase.ProtobufPayload) {
+      if (HasProtobufPayload) {
         size += 1 + pb::CodedOutputStream.ComputeBytesSize(ProtobufPayload);
       }
-      if (payloadCase_ == PayloadOneofCase.JsonPayload) {
+      if (HasJsonPayload) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(JsonPayload);
       }
-      if (payloadCase_ == PayloadOneofCase.JspbPayload) {
+      if (HasJspbPayload) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(JspbPayload);
       }
-      if (payloadCase_ == PayloadOneofCase.TextPayload) {
+      if (HasTextPayload) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(TextPayload);
       }
       if (RequestedOutputFormat != global::Conformance.WireFormat.Unspecified) {
@@ -892,6 +950,9 @@
         case ResultOneofCase.SerializeError:
           SerializeError = other.SerializeError;
           break;
+        case ResultOneofCase.TimeoutError:
+          TimeoutError = other.TimeoutError;
+          break;
         case ResultOneofCase.RuntimeError:
           RuntimeError = other.RuntimeError;
           break;
@@ -933,12 +994,26 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string ParseError {
-      get { return resultCase_ == ResultOneofCase.ParseError ? (string) result_ : ""; }
+      get { return HasParseError ? (string) result_ : ""; }
       set {
         result_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         resultCase_ = ResultOneofCase.ParseError;
       }
     }
+    /// <summary>Gets whether the "parse_error" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasParseError {
+      get { return resultCase_ == ResultOneofCase.ParseError; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "parse_error" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearParseError() {
+      if (HasParseError) {
+        ClearResult();
+      }
+    }
 
     /// <summary>Field number for the "serialize_error" field.</summary>
     public const int SerializeErrorFieldNumber = 6;
@@ -950,12 +1025,57 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string SerializeError {
-      get { return resultCase_ == ResultOneofCase.SerializeError ? (string) result_ : ""; }
+      get { return HasSerializeError ? (string) result_ : ""; }
       set {
         result_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         resultCase_ = ResultOneofCase.SerializeError;
       }
     }
+    /// <summary>Gets whether the "serialize_error" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasSerializeError {
+      get { return resultCase_ == ResultOneofCase.SerializeError; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "serialize_error" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearSerializeError() {
+      if (HasSerializeError) {
+        ClearResult();
+      }
+    }
+
+    /// <summary>Field number for the "timeout_error" field.</summary>
+    public const int TimeoutErrorFieldNumber = 9;
+    /// <summary>
+    /// This should be set if the test program timed out.  The string should
+    /// provide more information about what the child process was doing when it
+    /// was killed.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string TimeoutError {
+      get { return HasTimeoutError ? (string) result_ : ""; }
+      set {
+        result_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+        resultCase_ = ResultOneofCase.TimeoutError;
+      }
+    }
+    /// <summary>Gets whether the "timeout_error" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasTimeoutError {
+      get { return resultCase_ == ResultOneofCase.TimeoutError; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "timeout_error" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearTimeoutError() {
+      if (HasTimeoutError) {
+        ClearResult();
+      }
+    }
 
     /// <summary>Field number for the "runtime_error" field.</summary>
     public const int RuntimeErrorFieldNumber = 2;
@@ -967,12 +1087,26 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string RuntimeError {
-      get { return resultCase_ == ResultOneofCase.RuntimeError ? (string) result_ : ""; }
+      get { return HasRuntimeError ? (string) result_ : ""; }
       set {
         result_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         resultCase_ = ResultOneofCase.RuntimeError;
       }
     }
+    /// <summary>Gets whether the "runtime_error" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRuntimeError {
+      get { return resultCase_ == ResultOneofCase.RuntimeError; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "runtime_error" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRuntimeError() {
+      if (HasRuntimeError) {
+        ClearResult();
+      }
+    }
 
     /// <summary>Field number for the "protobuf_payload" field.</summary>
     public const int ProtobufPayloadFieldNumber = 3;
@@ -983,12 +1117,26 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public pb::ByteString ProtobufPayload {
-      get { return resultCase_ == ResultOneofCase.ProtobufPayload ? (pb::ByteString) result_ : pb::ByteString.Empty; }
+      get { return HasProtobufPayload ? (pb::ByteString) result_ : pb::ByteString.Empty; }
       set {
         result_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         resultCase_ = ResultOneofCase.ProtobufPayload;
       }
     }
+    /// <summary>Gets whether the "protobuf_payload" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasProtobufPayload {
+      get { return resultCase_ == ResultOneofCase.ProtobufPayload; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "protobuf_payload" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearProtobufPayload() {
+      if (HasProtobufPayload) {
+        ClearResult();
+      }
+    }
 
     /// <summary>Field number for the "json_payload" field.</summary>
     public const int JsonPayloadFieldNumber = 4;
@@ -999,12 +1147,26 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string JsonPayload {
-      get { return resultCase_ == ResultOneofCase.JsonPayload ? (string) result_ : ""; }
+      get { return HasJsonPayload ? (string) result_ : ""; }
       set {
         result_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         resultCase_ = ResultOneofCase.JsonPayload;
       }
     }
+    /// <summary>Gets whether the "json_payload" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasJsonPayload {
+      get { return resultCase_ == ResultOneofCase.JsonPayload; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "json_payload" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearJsonPayload() {
+      if (HasJsonPayload) {
+        ClearResult();
+      }
+    }
 
     /// <summary>Field number for the "skipped" field.</summary>
     public const int SkippedFieldNumber = 5;
@@ -1015,12 +1177,26 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string Skipped {
-      get { return resultCase_ == ResultOneofCase.Skipped ? (string) result_ : ""; }
+      get { return HasSkipped ? (string) result_ : ""; }
       set {
         result_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         resultCase_ = ResultOneofCase.Skipped;
       }
     }
+    /// <summary>Gets whether the "skipped" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasSkipped {
+      get { return resultCase_ == ResultOneofCase.Skipped; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "skipped" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearSkipped() {
+      if (HasSkipped) {
+        ClearResult();
+      }
+    }
 
     /// <summary>Field number for the "jspb_payload" field.</summary>
     public const int JspbPayloadFieldNumber = 7;
@@ -1032,12 +1208,26 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string JspbPayload {
-      get { return resultCase_ == ResultOneofCase.JspbPayload ? (string) result_ : ""; }
+      get { return HasJspbPayload ? (string) result_ : ""; }
       set {
         result_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         resultCase_ = ResultOneofCase.JspbPayload;
       }
     }
+    /// <summary>Gets whether the "jspb_payload" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasJspbPayload {
+      get { return resultCase_ == ResultOneofCase.JspbPayload; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "jspb_payload" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearJspbPayload() {
+      if (HasJspbPayload) {
+        ClearResult();
+      }
+    }
 
     /// <summary>Field number for the "text_payload" field.</summary>
     public const int TextPayloadFieldNumber = 8;
@@ -1048,12 +1238,26 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string TextPayload {
-      get { return resultCase_ == ResultOneofCase.TextPayload ? (string) result_ : ""; }
+      get { return HasTextPayload ? (string) result_ : ""; }
       set {
         result_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         resultCase_ = ResultOneofCase.TextPayload;
       }
     }
+    /// <summary>Gets whether the "text_payload" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasTextPayload {
+      get { return resultCase_ == ResultOneofCase.TextPayload; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "text_payload" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearTextPayload() {
+      if (HasTextPayload) {
+        ClearResult();
+      }
+    }
 
     private object result_;
     /// <summary>Enum of possible cases for the "result" oneof.</summary>
@@ -1061,6 +1265,7 @@
       None = 0,
       ParseError = 1,
       SerializeError = 6,
+      TimeoutError = 9,
       RuntimeError = 2,
       ProtobufPayload = 3,
       JsonPayload = 4,
@@ -1099,6 +1304,7 @@
       }
       if (ParseError != other.ParseError) return false;
       if (SerializeError != other.SerializeError) return false;
+      if (TimeoutError != other.TimeoutError) return false;
       if (RuntimeError != other.RuntimeError) return false;
       if (ProtobufPayload != other.ProtobufPayload) return false;
       if (JsonPayload != other.JsonPayload) return false;
@@ -1113,14 +1319,15 @@
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public override int GetHashCode() {
       int hash = 1;
-      if (resultCase_ == ResultOneofCase.ParseError) hash ^= ParseError.GetHashCode();
-      if (resultCase_ == ResultOneofCase.SerializeError) hash ^= SerializeError.GetHashCode();
-      if (resultCase_ == ResultOneofCase.RuntimeError) hash ^= RuntimeError.GetHashCode();
-      if (resultCase_ == ResultOneofCase.ProtobufPayload) hash ^= ProtobufPayload.GetHashCode();
-      if (resultCase_ == ResultOneofCase.JsonPayload) hash ^= JsonPayload.GetHashCode();
-      if (resultCase_ == ResultOneofCase.Skipped) hash ^= Skipped.GetHashCode();
-      if (resultCase_ == ResultOneofCase.JspbPayload) hash ^= JspbPayload.GetHashCode();
-      if (resultCase_ == ResultOneofCase.TextPayload) hash ^= TextPayload.GetHashCode();
+      if (HasParseError) hash ^= ParseError.GetHashCode();
+      if (HasSerializeError) hash ^= SerializeError.GetHashCode();
+      if (HasTimeoutError) hash ^= TimeoutError.GetHashCode();
+      if (HasRuntimeError) hash ^= RuntimeError.GetHashCode();
+      if (HasProtobufPayload) hash ^= ProtobufPayload.GetHashCode();
+      if (HasJsonPayload) hash ^= JsonPayload.GetHashCode();
+      if (HasSkipped) hash ^= Skipped.GetHashCode();
+      if (HasJspbPayload) hash ^= JspbPayload.GetHashCode();
+      if (HasTextPayload) hash ^= TextPayload.GetHashCode();
       hash ^= (int) resultCase_;
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
@@ -1140,38 +1347,42 @@
     #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
       output.WriteRawMessage(this);
     #else
-      if (resultCase_ == ResultOneofCase.ParseError) {
+      if (HasParseError) {
         output.WriteRawTag(10);
         output.WriteString(ParseError);
       }
-      if (resultCase_ == ResultOneofCase.RuntimeError) {
+      if (HasRuntimeError) {
         output.WriteRawTag(18);
         output.WriteString(RuntimeError);
       }
-      if (resultCase_ == ResultOneofCase.ProtobufPayload) {
+      if (HasProtobufPayload) {
         output.WriteRawTag(26);
         output.WriteBytes(ProtobufPayload);
       }
-      if (resultCase_ == ResultOneofCase.JsonPayload) {
+      if (HasJsonPayload) {
         output.WriteRawTag(34);
         output.WriteString(JsonPayload);
       }
-      if (resultCase_ == ResultOneofCase.Skipped) {
+      if (HasSkipped) {
         output.WriteRawTag(42);
         output.WriteString(Skipped);
       }
-      if (resultCase_ == ResultOneofCase.SerializeError) {
+      if (HasSerializeError) {
         output.WriteRawTag(50);
         output.WriteString(SerializeError);
       }
-      if (resultCase_ == ResultOneofCase.JspbPayload) {
+      if (HasJspbPayload) {
         output.WriteRawTag(58);
         output.WriteString(JspbPayload);
       }
-      if (resultCase_ == ResultOneofCase.TextPayload) {
+      if (HasTextPayload) {
         output.WriteRawTag(66);
         output.WriteString(TextPayload);
       }
+      if (HasTimeoutError) {
+        output.WriteRawTag(74);
+        output.WriteString(TimeoutError);
+      }
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
@@ -1182,38 +1393,42 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
-      if (resultCase_ == ResultOneofCase.ParseError) {
+      if (HasParseError) {
         output.WriteRawTag(10);
         output.WriteString(ParseError);
       }
-      if (resultCase_ == ResultOneofCase.RuntimeError) {
+      if (HasRuntimeError) {
         output.WriteRawTag(18);
         output.WriteString(RuntimeError);
       }
-      if (resultCase_ == ResultOneofCase.ProtobufPayload) {
+      if (HasProtobufPayload) {
         output.WriteRawTag(26);
         output.WriteBytes(ProtobufPayload);
       }
-      if (resultCase_ == ResultOneofCase.JsonPayload) {
+      if (HasJsonPayload) {
         output.WriteRawTag(34);
         output.WriteString(JsonPayload);
       }
-      if (resultCase_ == ResultOneofCase.Skipped) {
+      if (HasSkipped) {
         output.WriteRawTag(42);
         output.WriteString(Skipped);
       }
-      if (resultCase_ == ResultOneofCase.SerializeError) {
+      if (HasSerializeError) {
         output.WriteRawTag(50);
         output.WriteString(SerializeError);
       }
-      if (resultCase_ == ResultOneofCase.JspbPayload) {
+      if (HasJspbPayload) {
         output.WriteRawTag(58);
         output.WriteString(JspbPayload);
       }
-      if (resultCase_ == ResultOneofCase.TextPayload) {
+      if (HasTextPayload) {
         output.WriteRawTag(66);
         output.WriteString(TextPayload);
       }
+      if (HasTimeoutError) {
+        output.WriteRawTag(74);
+        output.WriteString(TimeoutError);
+      }
       if (_unknownFields != null) {
         _unknownFields.WriteTo(ref output);
       }
@@ -1224,28 +1439,31 @@
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public int CalculateSize() {
       int size = 0;
-      if (resultCase_ == ResultOneofCase.ParseError) {
+      if (HasParseError) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(ParseError);
       }
-      if (resultCase_ == ResultOneofCase.SerializeError) {
+      if (HasSerializeError) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(SerializeError);
       }
-      if (resultCase_ == ResultOneofCase.RuntimeError) {
+      if (HasTimeoutError) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(TimeoutError);
+      }
+      if (HasRuntimeError) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(RuntimeError);
       }
-      if (resultCase_ == ResultOneofCase.ProtobufPayload) {
+      if (HasProtobufPayload) {
         size += 1 + pb::CodedOutputStream.ComputeBytesSize(ProtobufPayload);
       }
-      if (resultCase_ == ResultOneofCase.JsonPayload) {
+      if (HasJsonPayload) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(JsonPayload);
       }
-      if (resultCase_ == ResultOneofCase.Skipped) {
+      if (HasSkipped) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(Skipped);
       }
-      if (resultCase_ == ResultOneofCase.JspbPayload) {
+      if (HasJspbPayload) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(JspbPayload);
       }
-      if (resultCase_ == ResultOneofCase.TextPayload) {
+      if (HasTextPayload) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(TextPayload);
       }
       if (_unknownFields != null) {
@@ -1267,6 +1485,9 @@
         case ResultOneofCase.SerializeError:
           SerializeError = other.SerializeError;
           break;
+        case ResultOneofCase.TimeoutError:
+          TimeoutError = other.TimeoutError;
+          break;
         case ResultOneofCase.RuntimeError:
           RuntimeError = other.RuntimeError;
           break;
@@ -1334,6 +1555,10 @@
             TextPayload = input.ReadString();
             break;
           }
+          case 74: {
+            TimeoutError = input.ReadString();
+            break;
+          }
         }
       }
     #endif
@@ -1381,6 +1606,10 @@
             TextPayload = input.ReadString();
             break;
           }
+          case 74: {
+            TimeoutError = input.ReadString();
+            break;
+          }
         }
       }
     }
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto2.pb.cs b/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto2.pb.cs
index bbfe2e0..d48c50e 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto2.pb.cs
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto2.pb.cs
@@ -215,12 +215,68 @@
             "cm90bzIiLwoORW51bU9ubHlQcm90bzIiHQoEQm9vbBIKCgZrRmFsc2UQABIJ",
             "CgVrVHJ1ZRABIh8KD09uZVN0cmluZ1Byb3RvMhIMCgRkYXRhGAEgASgJIkYK",
             "EVByb3RvV2l0aEtleXdvcmRzEg4KBmlubGluZRgBIAEoBRIPCgdjb25jZXB0",
-            "GAIgASgJEhAKCHJlcXVpcmVzGAMgAygJKkYKEUZvcmVpZ25FbnVtUHJvdG8y",
-            "Eg8KC0ZPUkVJR05fRk9PEAASDwoLRk9SRUlHTl9CQVIQARIPCgtGT1JFSUdO",
-            "X0JBWhACOkoKD2V4dGVuc2lvbl9pbnQzMhIxLnByb3RvYnVmX3Rlc3RfbWVz",
-            "c2FnZXMucHJvdG8yLlRlc3RBbGxUeXBlc1Byb3RvMhh4IAEoBUI4Cihjb20u",
-            "Z29vZ2xlLnByb3RvYnVmX3Rlc3RfbWVzc2FnZXMucHJvdG8ySAH4AQGiAgZQ",
-            "cm90bzI="));
+            "GAIgASgJEhAKCHJlcXVpcmVzGAMgAygJIt4TChpUZXN0QWxsUmVxdWlyZWRU",
+            "eXBlc1Byb3RvMhIWCg5yZXF1aXJlZF9pbnQzMhgBIAIoBRIWCg5yZXF1aXJl",
+            "ZF9pbnQ2NBgCIAIoAxIXCg9yZXF1aXJlZF91aW50MzIYAyACKA0SFwoPcmVx",
+            "dWlyZWRfdWludDY0GAQgAigEEhcKD3JlcXVpcmVkX3NpbnQzMhgFIAIoERIX",
+            "Cg9yZXF1aXJlZF9zaW50NjQYBiACKBISGAoQcmVxdWlyZWRfZml4ZWQzMhgH",
+            "IAIoBxIYChByZXF1aXJlZF9maXhlZDY0GAggAigGEhkKEXJlcXVpcmVkX3Nm",
+            "aXhlZDMyGAkgAigPEhkKEXJlcXVpcmVkX3NmaXhlZDY0GAogAigQEhYKDnJl",
+            "cXVpcmVkX2Zsb2F0GAsgAigCEhcKD3JlcXVpcmVkX2RvdWJsZRgMIAIoARIV",
+            "Cg1yZXF1aXJlZF9ib29sGA0gAigIEhcKD3JlcXVpcmVkX3N0cmluZxgOIAIo",
+            "CRIWCg5yZXF1aXJlZF9ieXRlcxgPIAIoDBJoChdyZXF1aXJlZF9uZXN0ZWRf",
+            "bWVzc2FnZRgSIAIoCzJHLnByb3RvYnVmX3Rlc3RfbWVzc2FnZXMucHJvdG8y",
+            "LlRlc3RBbGxSZXF1aXJlZFR5cGVzUHJvdG8yLk5lc3RlZE1lc3NhZ2USVQoY",
+            "cmVxdWlyZWRfZm9yZWlnbl9tZXNzYWdlGBMgAigLMjMucHJvdG9idWZfdGVz",
+            "dF9tZXNzYWdlcy5wcm90bzIuRm9yZWlnbk1lc3NhZ2VQcm90bzISYgoUcmVx",
+            "dWlyZWRfbmVzdGVkX2VudW0YFSACKA4yRC5wcm90b2J1Zl90ZXN0X21lc3Nh",
+            "Z2VzLnByb3RvMi5UZXN0QWxsUmVxdWlyZWRUeXBlc1Byb3RvMi5OZXN0ZWRF",
+            "bnVtEk8KFXJlcXVpcmVkX2ZvcmVpZ25fZW51bRgWIAIoDjIwLnByb3RvYnVm",
+            "X3Rlc3RfbWVzc2FnZXMucHJvdG8yLkZvcmVpZ25FbnVtUHJvdG8yEiEKFXJl",
+            "cXVpcmVkX3N0cmluZ19waWVjZRgYIAIoCUICCAISGQoNcmVxdWlyZWRfY29y",
+            "ZBgZIAIoCUICCAESVAoRcmVjdXJzaXZlX21lc3NhZ2UYGyACKAsyOS5wcm90",
+            "b2J1Zl90ZXN0X21lc3NhZ2VzLnByb3RvMi5UZXN0QWxsUmVxdWlyZWRUeXBl",
+            "c1Byb3RvMhJdChpvcHRpb25hbF9yZWN1cnNpdmVfbWVzc2FnZRgcIAEoCzI5",
+            "LnByb3RvYnVmX3Rlc3RfbWVzc2FnZXMucHJvdG8yLlRlc3RBbGxSZXF1aXJl",
+            "ZFR5cGVzUHJvdG8yEk0KBGRhdGEYyQEgAigKMj4ucHJvdG9idWZfdGVzdF9t",
+            "ZXNzYWdlcy5wcm90bzIuVGVzdEFsbFJlcXVpcmVkVHlwZXNQcm90bzIuRGF0",
+            "YRIiCg1kZWZhdWx0X2ludDMyGPEBIAIoBToKLTEyMzQ1Njc4ORIsCg1kZWZh",
+            "dWx0X2ludDY0GPIBIAIoAzoULTkxMjM0NTY3ODkxMjM0NTY3ODkSIwoOZGVm",
+            "YXVsdF91aW50MzIY8wEgAigNOgoyMTIzNDU2Nzg5Ei0KDmRlZmF1bHRfdWlu",
+            "dDY0GPQBIAIoBDoUMTAxMjM0NTY3ODkxMjM0NTY3ODkSIwoOZGVmYXVsdF9z",
+            "aW50MzIY9QEgAigROgotMTIzNDU2Nzg5Ei0KDmRlZmF1bHRfc2ludDY0GPYB",
+            "IAIoEjoULTkxMjM0NTY3ODkxMjM0NTY3ODkSJAoPZGVmYXVsdF9maXhlZDMy",
+            "GPcBIAIoBzoKMjEyMzQ1Njc4ORIuCg9kZWZhdWx0X2ZpeGVkNjQY+AEgAigG",
+            "OhQxMDEyMzQ1Njc4OTEyMzQ1Njc4ORIlChBkZWZhdWx0X3NmaXhlZDMyGPkB",
+            "IAIoDzoKLTEyMzQ1Njc4ORIvChBkZWZhdWx0X3NmaXhlZDY0GPoBIAIoEDoU",
+            "LTkxMjM0NTY3ODkxMjM0NTY3ODkSHQoNZGVmYXVsdF9mbG9hdBj7ASACKAI6",
+            "BTllKzA5Eh4KDmRlZmF1bHRfZG91YmxlGPwBIAIoAToFN2UrMjISGwoMZGVm",
+            "YXVsdF9ib29sGP0BIAIoCDoEdHJ1ZRIgCg5kZWZhdWx0X3N0cmluZxj+ASAC",
+            "KAk6B1Jvc2VidWQSHgoNZGVmYXVsdF9ieXRlcxj/ASACKAw6Bmpvc2h1YRrD",
+            "AQoNTmVzdGVkTWVzc2FnZRIJCgFhGAEgAigFEk4KC2NvcmVjdXJzaXZlGAIg",
+            "AigLMjkucHJvdG9idWZfdGVzdF9tZXNzYWdlcy5wcm90bzIuVGVzdEFsbFJl",
+            "cXVpcmVkVHlwZXNQcm90bzISVwoUb3B0aW9uYWxfY29yZWN1cnNpdmUYAyAB",
+            "KAsyOS5wcm90b2J1Zl90ZXN0X21lc3NhZ2VzLnByb3RvMi5UZXN0QWxsUmVx",
+            "dWlyZWRUeXBlc1Byb3RvMhozCgREYXRhEhQKC2dyb3VwX2ludDMyGMoBIAIo",
+            "BRIVCgxncm91cF91aW50MzIYywEgAigNGiEKEU1lc3NhZ2VTZXRDb3JyZWN0",
+            "KggIBBD/////BzoCCAEa8AEKG01lc3NhZ2VTZXRDb3JyZWN0RXh0ZW5zaW9u",
+            "MRILCgNzdHIYGSACKAkywwEKFW1lc3NhZ2Vfc2V0X2V4dGVuc2lvbhJLLnBy",
+            "b3RvYnVmX3Rlc3RfbWVzc2FnZXMucHJvdG8yLlRlc3RBbGxSZXF1aXJlZFR5",
+            "cGVzUHJvdG8yLk1lc3NhZ2VTZXRDb3JyZWN0GPm7XiABKAsyVS5wcm90b2J1",
+            "Zl90ZXN0X21lc3NhZ2VzLnByb3RvMi5UZXN0QWxsUmVxdWlyZWRUeXBlc1By",
+            "b3RvMi5NZXNzYWdlU2V0Q29ycmVjdEV4dGVuc2lvbjEa7wEKG01lc3NhZ2VT",
+            "ZXRDb3JyZWN0RXh0ZW5zaW9uMhIJCgFpGAkgAigFMsQBChVtZXNzYWdlX3Nl",
+            "dF9leHRlbnNpb24SSy5wcm90b2J1Zl90ZXN0X21lc3NhZ2VzLnByb3RvMi5U",
+            "ZXN0QWxsUmVxdWlyZWRUeXBlc1Byb3RvMi5NZXNzYWdlU2V0Q29ycmVjdBiQ",
+            "s/wBIAEoCzJVLnByb3RvYnVmX3Rlc3RfbWVzc2FnZXMucHJvdG8yLlRlc3RB",
+            "bGxSZXF1aXJlZFR5cGVzUHJvdG8yLk1lc3NhZ2VTZXRDb3JyZWN0RXh0ZW5z",
+            "aW9uMiI5CgpOZXN0ZWRFbnVtEgcKA0ZPTxAAEgcKA0JBUhABEgcKA0JBWhAC",
+            "EhAKA05FRxD///////////8BKgUIeBDJAUoGCOgHEJBOKkYKEUZvcmVpZ25F",
+            "bnVtUHJvdG8yEg8KC0ZPUkVJR05fRk9PEAASDwoLRk9SRUlHTl9CQVIQARIP",
+            "CgtGT1JFSUdOX0JBWhACOkoKD2V4dGVuc2lvbl9pbnQzMhIxLnByb3RvYnVm",
+            "X3Rlc3RfbWVzc2FnZXMucHJvdG8yLlRlc3RBbGxUeXBlc1Byb3RvMhh4IAEo",
+            "BUI4Cihjb20uZ29vZ2xlLnByb3RvYnVmX3Rlc3RfbWVzc2FnZXMucHJvdG8y",
+            "SAH4AQGiAgZQcm90bzI="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { },
           new pbr::GeneratedClrTypeInfo(new[] {typeof(global::ProtobufTestMessages.Proto2.ForeignEnumProto2), }, new pb::Extension[] { TestMessagesProto2Extensions.ExtensionInt32 }, new pbr::GeneratedClrTypeInfo[] {
@@ -234,7 +290,12 @@
             new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufTestMessages.Proto2.NullHypothesisProto2), global::ProtobufTestMessages.Proto2.NullHypothesisProto2.Parser, null, null, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufTestMessages.Proto2.EnumOnlyProto2), global::ProtobufTestMessages.Proto2.EnumOnlyProto2.Parser, null, null, new[]{ typeof(global::ProtobufTestMessages.Proto2.EnumOnlyProto2.Types.Bool) }, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufTestMessages.Proto2.OneStringProto2), global::ProtobufTestMessages.Proto2.OneStringProto2.Parser, new[]{ "Data" }, null, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufTestMessages.Proto2.ProtoWithKeywords), global::ProtobufTestMessages.Proto2.ProtoWithKeywords.Parser, new[]{ "Inline", "Concept", "Requires" }, null, null, null, null)
+            new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufTestMessages.Proto2.ProtoWithKeywords), global::ProtobufTestMessages.Proto2.ProtoWithKeywords.Parser, new[]{ "Inline", "Concept", "Requires" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2), global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Parser, new[]{ "RequiredInt32", "RequiredInt64", "RequiredUint32", "RequiredUint64", "RequiredSint32", "RequiredSint64", "RequiredFixed32", "RequiredFixed64", "RequiredSfixed32", "RequiredSfixed64", "RequiredFloat", "RequiredDouble", "RequiredBool", "RequiredString", "RequiredBytes", "RequiredNestedMessage", "RequiredForeignMessage", "RequiredNestedEnum", "RequiredForeignEnum", "RequiredStringPiece", "RequiredCord", "RecursiveMessage", "OptionalRecursiveMessage", "Data", "DefaultInt32", "DefaultInt64", "DefaultUint32", "DefaultUint64", "DefaultSint32", "DefaultSint64", "DefaultFixed32", "DefaultFixed64", "DefaultSfixed32", "DefaultSfixed64", "DefaultFloat", "DefaultDouble", "DefaultBool", "DefaultString", "DefaultBytes" }, null, new[]{ typeof(global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.NestedEnum) }, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.NestedMessage), global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.NestedMessage.Parser, new[]{ "A", "Corecursive", "OptionalCorecursive" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.Data), global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.Data.Parser, new[]{ "GroupInt32", "GroupUint32" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrect), global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrect.Parser, null, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrectExtension1), global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrectExtension1.Parser, new[]{ "Str" }, null, null, new pb::Extension[] { global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrectExtension1.Extensions.MessageSetExtension }, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrectExtension2), global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrectExtension2.Parser, new[]{ "I" }, null, null, new pb::Extension[] { global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrectExtension2.Extensions.MessageSetExtension }, null)})
           }));
     }
     #endregion
@@ -8586,6 +8647,3427 @@
 
   }
 
+  public sealed partial class TestAllRequiredTypesProto2 : pb::IExtendableMessage<TestAllRequiredTypesProto2>
+  #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      , pb::IBufferMessage
+  #endif
+  {
+    private static readonly pb::MessageParser<TestAllRequiredTypesProto2> _parser = new pb::MessageParser<TestAllRequiredTypesProto2>(() => new TestAllRequiredTypesProto2());
+    private pb::UnknownFieldSet _unknownFields;
+    private pb::ExtensionSet<TestAllRequiredTypesProto2> _extensions;
+    private pb::ExtensionSet<TestAllRequiredTypesProto2> _Extensions { get { return _extensions; } }
+    private int _hasBits0;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pb::MessageParser<TestAllRequiredTypesProto2> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::ProtobufTestMessages.Proto2.TestMessagesProto2Reflection.Descriptor.MessageTypes[7]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public TestAllRequiredTypesProto2() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public TestAllRequiredTypesProto2(TestAllRequiredTypesProto2 other) : this() {
+      _hasBits0 = other._hasBits0;
+      requiredInt32_ = other.requiredInt32_;
+      requiredInt64_ = other.requiredInt64_;
+      requiredUint32_ = other.requiredUint32_;
+      requiredUint64_ = other.requiredUint64_;
+      requiredSint32_ = other.requiredSint32_;
+      requiredSint64_ = other.requiredSint64_;
+      requiredFixed32_ = other.requiredFixed32_;
+      requiredFixed64_ = other.requiredFixed64_;
+      requiredSfixed32_ = other.requiredSfixed32_;
+      requiredSfixed64_ = other.requiredSfixed64_;
+      requiredFloat_ = other.requiredFloat_;
+      requiredDouble_ = other.requiredDouble_;
+      requiredBool_ = other.requiredBool_;
+      requiredString_ = other.requiredString_;
+      requiredBytes_ = other.requiredBytes_;
+      requiredNestedMessage_ = other.requiredNestedMessage_ != null ? other.requiredNestedMessage_.Clone() : null;
+      requiredForeignMessage_ = other.requiredForeignMessage_ != null ? other.requiredForeignMessage_.Clone() : null;
+      requiredNestedEnum_ = other.requiredNestedEnum_;
+      requiredForeignEnum_ = other.requiredForeignEnum_;
+      requiredStringPiece_ = other.requiredStringPiece_;
+      requiredCord_ = other.requiredCord_;
+      recursiveMessage_ = other.recursiveMessage_ != null ? other.recursiveMessage_.Clone() : null;
+      optionalRecursiveMessage_ = other.optionalRecursiveMessage_ != null ? other.optionalRecursiveMessage_.Clone() : null;
+      data_ = other.HasData ? other.data_.Clone() : null;
+      defaultInt32_ = other.defaultInt32_;
+      defaultInt64_ = other.defaultInt64_;
+      defaultUint32_ = other.defaultUint32_;
+      defaultUint64_ = other.defaultUint64_;
+      defaultSint32_ = other.defaultSint32_;
+      defaultSint64_ = other.defaultSint64_;
+      defaultFixed32_ = other.defaultFixed32_;
+      defaultFixed64_ = other.defaultFixed64_;
+      defaultSfixed32_ = other.defaultSfixed32_;
+      defaultSfixed64_ = other.defaultSfixed64_;
+      defaultFloat_ = other.defaultFloat_;
+      defaultDouble_ = other.defaultDouble_;
+      defaultBool_ = other.defaultBool_;
+      defaultString_ = other.defaultString_;
+      defaultBytes_ = other.defaultBytes_;
+      _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+      _extensions = pb::ExtensionSet.Clone(other._extensions);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public TestAllRequiredTypesProto2 Clone() {
+      return new TestAllRequiredTypesProto2(this);
+    }
+
+    /// <summary>Field number for the "required_int32" field.</summary>
+    public const int RequiredInt32FieldNumber = 1;
+    private readonly static int RequiredInt32DefaultValue = 0;
+
+    private int requiredInt32_;
+    /// <summary>
+    /// Singular
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int RequiredInt32 {
+      get { if ((_hasBits0 & 1) != 0) { return requiredInt32_; } else { return RequiredInt32DefaultValue; } }
+      set {
+        _hasBits0 |= 1;
+        requiredInt32_ = value;
+      }
+    }
+    /// <summary>Gets whether the "required_int32" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredInt32 {
+      get { return (_hasBits0 & 1) != 0; }
+    }
+    /// <summary>Clears the value of the "required_int32" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredInt32() {
+      _hasBits0 &= ~1;
+    }
+
+    /// <summary>Field number for the "required_int64" field.</summary>
+    public const int RequiredInt64FieldNumber = 2;
+    private readonly static long RequiredInt64DefaultValue = 0L;
+
+    private long requiredInt64_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public long RequiredInt64 {
+      get { if ((_hasBits0 & 2) != 0) { return requiredInt64_; } else { return RequiredInt64DefaultValue; } }
+      set {
+        _hasBits0 |= 2;
+        requiredInt64_ = value;
+      }
+    }
+    /// <summary>Gets whether the "required_int64" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredInt64 {
+      get { return (_hasBits0 & 2) != 0; }
+    }
+    /// <summary>Clears the value of the "required_int64" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredInt64() {
+      _hasBits0 &= ~2;
+    }
+
+    /// <summary>Field number for the "required_uint32" field.</summary>
+    public const int RequiredUint32FieldNumber = 3;
+    private readonly static uint RequiredUint32DefaultValue = 0;
+
+    private uint requiredUint32_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint RequiredUint32 {
+      get { if ((_hasBits0 & 4) != 0) { return requiredUint32_; } else { return RequiredUint32DefaultValue; } }
+      set {
+        _hasBits0 |= 4;
+        requiredUint32_ = value;
+      }
+    }
+    /// <summary>Gets whether the "required_uint32" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredUint32 {
+      get { return (_hasBits0 & 4) != 0; }
+    }
+    /// <summary>Clears the value of the "required_uint32" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredUint32() {
+      _hasBits0 &= ~4;
+    }
+
+    /// <summary>Field number for the "required_uint64" field.</summary>
+    public const int RequiredUint64FieldNumber = 4;
+    private readonly static ulong RequiredUint64DefaultValue = 0UL;
+
+    private ulong requiredUint64_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ulong RequiredUint64 {
+      get { if ((_hasBits0 & 8) != 0) { return requiredUint64_; } else { return RequiredUint64DefaultValue; } }
+      set {
+        _hasBits0 |= 8;
+        requiredUint64_ = value;
+      }
+    }
+    /// <summary>Gets whether the "required_uint64" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredUint64 {
+      get { return (_hasBits0 & 8) != 0; }
+    }
+    /// <summary>Clears the value of the "required_uint64" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredUint64() {
+      _hasBits0 &= ~8;
+    }
+
+    /// <summary>Field number for the "required_sint32" field.</summary>
+    public const int RequiredSint32FieldNumber = 5;
+    private readonly static int RequiredSint32DefaultValue = 0;
+
+    private int requiredSint32_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int RequiredSint32 {
+      get { if ((_hasBits0 & 16) != 0) { return requiredSint32_; } else { return RequiredSint32DefaultValue; } }
+      set {
+        _hasBits0 |= 16;
+        requiredSint32_ = value;
+      }
+    }
+    /// <summary>Gets whether the "required_sint32" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredSint32 {
+      get { return (_hasBits0 & 16) != 0; }
+    }
+    /// <summary>Clears the value of the "required_sint32" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredSint32() {
+      _hasBits0 &= ~16;
+    }
+
+    /// <summary>Field number for the "required_sint64" field.</summary>
+    public const int RequiredSint64FieldNumber = 6;
+    private readonly static long RequiredSint64DefaultValue = 0L;
+
+    private long requiredSint64_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public long RequiredSint64 {
+      get { if ((_hasBits0 & 32) != 0) { return requiredSint64_; } else { return RequiredSint64DefaultValue; } }
+      set {
+        _hasBits0 |= 32;
+        requiredSint64_ = value;
+      }
+    }
+    /// <summary>Gets whether the "required_sint64" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredSint64 {
+      get { return (_hasBits0 & 32) != 0; }
+    }
+    /// <summary>Clears the value of the "required_sint64" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredSint64() {
+      _hasBits0 &= ~32;
+    }
+
+    /// <summary>Field number for the "required_fixed32" field.</summary>
+    public const int RequiredFixed32FieldNumber = 7;
+    private readonly static uint RequiredFixed32DefaultValue = 0;
+
+    private uint requiredFixed32_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint RequiredFixed32 {
+      get { if ((_hasBits0 & 64) != 0) { return requiredFixed32_; } else { return RequiredFixed32DefaultValue; } }
+      set {
+        _hasBits0 |= 64;
+        requiredFixed32_ = value;
+      }
+    }
+    /// <summary>Gets whether the "required_fixed32" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredFixed32 {
+      get { return (_hasBits0 & 64) != 0; }
+    }
+    /// <summary>Clears the value of the "required_fixed32" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredFixed32() {
+      _hasBits0 &= ~64;
+    }
+
+    /// <summary>Field number for the "required_fixed64" field.</summary>
+    public const int RequiredFixed64FieldNumber = 8;
+    private readonly static ulong RequiredFixed64DefaultValue = 0UL;
+
+    private ulong requiredFixed64_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ulong RequiredFixed64 {
+      get { if ((_hasBits0 & 128) != 0) { return requiredFixed64_; } else { return RequiredFixed64DefaultValue; } }
+      set {
+        _hasBits0 |= 128;
+        requiredFixed64_ = value;
+      }
+    }
+    /// <summary>Gets whether the "required_fixed64" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredFixed64 {
+      get { return (_hasBits0 & 128) != 0; }
+    }
+    /// <summary>Clears the value of the "required_fixed64" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredFixed64() {
+      _hasBits0 &= ~128;
+    }
+
+    /// <summary>Field number for the "required_sfixed32" field.</summary>
+    public const int RequiredSfixed32FieldNumber = 9;
+    private readonly static int RequiredSfixed32DefaultValue = 0;
+
+    private int requiredSfixed32_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int RequiredSfixed32 {
+      get { if ((_hasBits0 & 256) != 0) { return requiredSfixed32_; } else { return RequiredSfixed32DefaultValue; } }
+      set {
+        _hasBits0 |= 256;
+        requiredSfixed32_ = value;
+      }
+    }
+    /// <summary>Gets whether the "required_sfixed32" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredSfixed32 {
+      get { return (_hasBits0 & 256) != 0; }
+    }
+    /// <summary>Clears the value of the "required_sfixed32" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredSfixed32() {
+      _hasBits0 &= ~256;
+    }
+
+    /// <summary>Field number for the "required_sfixed64" field.</summary>
+    public const int RequiredSfixed64FieldNumber = 10;
+    private readonly static long RequiredSfixed64DefaultValue = 0L;
+
+    private long requiredSfixed64_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public long RequiredSfixed64 {
+      get { if ((_hasBits0 & 512) != 0) { return requiredSfixed64_; } else { return RequiredSfixed64DefaultValue; } }
+      set {
+        _hasBits0 |= 512;
+        requiredSfixed64_ = value;
+      }
+    }
+    /// <summary>Gets whether the "required_sfixed64" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredSfixed64 {
+      get { return (_hasBits0 & 512) != 0; }
+    }
+    /// <summary>Clears the value of the "required_sfixed64" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredSfixed64() {
+      _hasBits0 &= ~512;
+    }
+
+    /// <summary>Field number for the "required_float" field.</summary>
+    public const int RequiredFloatFieldNumber = 11;
+    private readonly static float RequiredFloatDefaultValue = 0F;
+
+    private float requiredFloat_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public float RequiredFloat {
+      get { if ((_hasBits0 & 1024) != 0) { return requiredFloat_; } else { return RequiredFloatDefaultValue; } }
+      set {
+        _hasBits0 |= 1024;
+        requiredFloat_ = value;
+      }
+    }
+    /// <summary>Gets whether the "required_float" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredFloat {
+      get { return (_hasBits0 & 1024) != 0; }
+    }
+    /// <summary>Clears the value of the "required_float" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredFloat() {
+      _hasBits0 &= ~1024;
+    }
+
+    /// <summary>Field number for the "required_double" field.</summary>
+    public const int RequiredDoubleFieldNumber = 12;
+    private readonly static double RequiredDoubleDefaultValue = 0D;
+
+    private double requiredDouble_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public double RequiredDouble {
+      get { if ((_hasBits0 & 2048) != 0) { return requiredDouble_; } else { return RequiredDoubleDefaultValue; } }
+      set {
+        _hasBits0 |= 2048;
+        requiredDouble_ = value;
+      }
+    }
+    /// <summary>Gets whether the "required_double" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredDouble {
+      get { return (_hasBits0 & 2048) != 0; }
+    }
+    /// <summary>Clears the value of the "required_double" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredDouble() {
+      _hasBits0 &= ~2048;
+    }
+
+    /// <summary>Field number for the "required_bool" field.</summary>
+    public const int RequiredBoolFieldNumber = 13;
+    private readonly static bool RequiredBoolDefaultValue = false;
+
+    private bool requiredBool_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool RequiredBool {
+      get { if ((_hasBits0 & 4096) != 0) { return requiredBool_; } else { return RequiredBoolDefaultValue; } }
+      set {
+        _hasBits0 |= 4096;
+        requiredBool_ = value;
+      }
+    }
+    /// <summary>Gets whether the "required_bool" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredBool {
+      get { return (_hasBits0 & 4096) != 0; }
+    }
+    /// <summary>Clears the value of the "required_bool" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredBool() {
+      _hasBits0 &= ~4096;
+    }
+
+    /// <summary>Field number for the "required_string" field.</summary>
+    public const int RequiredStringFieldNumber = 14;
+    private readonly static string RequiredStringDefaultValue = "";
+
+    private string requiredString_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string RequiredString {
+      get { return requiredString_ ?? RequiredStringDefaultValue; }
+      set {
+        requiredString_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+    /// <summary>Gets whether the "required_string" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredString {
+      get { return requiredString_ != null; }
+    }
+    /// <summary>Clears the value of the "required_string" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredString() {
+      requiredString_ = null;
+    }
+
+    /// <summary>Field number for the "required_bytes" field.</summary>
+    public const int RequiredBytesFieldNumber = 15;
+    private readonly static pb::ByteString RequiredBytesDefaultValue = pb::ByteString.Empty;
+
+    private pb::ByteString requiredBytes_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString RequiredBytes {
+      get { return requiredBytes_ ?? RequiredBytesDefaultValue; }
+      set {
+        requiredBytes_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+    /// <summary>Gets whether the "required_bytes" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredBytes {
+      get { return requiredBytes_ != null; }
+    }
+    /// <summary>Clears the value of the "required_bytes" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredBytes() {
+      requiredBytes_ = null;
+    }
+
+    /// <summary>Field number for the "required_nested_message" field.</summary>
+    public const int RequiredNestedMessageFieldNumber = 18;
+    private global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.NestedMessage requiredNestedMessage_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.NestedMessage RequiredNestedMessage {
+      get { return requiredNestedMessage_; }
+      set {
+        requiredNestedMessage_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "required_foreign_message" field.</summary>
+    public const int RequiredForeignMessageFieldNumber = 19;
+    private global::ProtobufTestMessages.Proto2.ForeignMessageProto2 requiredForeignMessage_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::ProtobufTestMessages.Proto2.ForeignMessageProto2 RequiredForeignMessage {
+      get { return requiredForeignMessage_; }
+      set {
+        requiredForeignMessage_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "required_nested_enum" field.</summary>
+    public const int RequiredNestedEnumFieldNumber = 21;
+    private readonly static global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.NestedEnum RequiredNestedEnumDefaultValue = global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.NestedEnum.Foo;
+
+    private global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.NestedEnum requiredNestedEnum_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.NestedEnum RequiredNestedEnum {
+      get { if ((_hasBits0 & 8192) != 0) { return requiredNestedEnum_; } else { return RequiredNestedEnumDefaultValue; } }
+      set {
+        _hasBits0 |= 8192;
+        requiredNestedEnum_ = value;
+      }
+    }
+    /// <summary>Gets whether the "required_nested_enum" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredNestedEnum {
+      get { return (_hasBits0 & 8192) != 0; }
+    }
+    /// <summary>Clears the value of the "required_nested_enum" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredNestedEnum() {
+      _hasBits0 &= ~8192;
+    }
+
+    /// <summary>Field number for the "required_foreign_enum" field.</summary>
+    public const int RequiredForeignEnumFieldNumber = 22;
+    private readonly static global::ProtobufTestMessages.Proto2.ForeignEnumProto2 RequiredForeignEnumDefaultValue = global::ProtobufTestMessages.Proto2.ForeignEnumProto2.ForeignFoo;
+
+    private global::ProtobufTestMessages.Proto2.ForeignEnumProto2 requiredForeignEnum_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::ProtobufTestMessages.Proto2.ForeignEnumProto2 RequiredForeignEnum {
+      get { if ((_hasBits0 & 16384) != 0) { return requiredForeignEnum_; } else { return RequiredForeignEnumDefaultValue; } }
+      set {
+        _hasBits0 |= 16384;
+        requiredForeignEnum_ = value;
+      }
+    }
+    /// <summary>Gets whether the "required_foreign_enum" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredForeignEnum {
+      get { return (_hasBits0 & 16384) != 0; }
+    }
+    /// <summary>Clears the value of the "required_foreign_enum" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredForeignEnum() {
+      _hasBits0 &= ~16384;
+    }
+
+    /// <summary>Field number for the "required_string_piece" field.</summary>
+    public const int RequiredStringPieceFieldNumber = 24;
+    private readonly static string RequiredStringPieceDefaultValue = "";
+
+    private string requiredStringPiece_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string RequiredStringPiece {
+      get { return requiredStringPiece_ ?? RequiredStringPieceDefaultValue; }
+      set {
+        requiredStringPiece_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+    /// <summary>Gets whether the "required_string_piece" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredStringPiece {
+      get { return requiredStringPiece_ != null; }
+    }
+    /// <summary>Clears the value of the "required_string_piece" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredStringPiece() {
+      requiredStringPiece_ = null;
+    }
+
+    /// <summary>Field number for the "required_cord" field.</summary>
+    public const int RequiredCordFieldNumber = 25;
+    private readonly static string RequiredCordDefaultValue = "";
+
+    private string requiredCord_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string RequiredCord {
+      get { return requiredCord_ ?? RequiredCordDefaultValue; }
+      set {
+        requiredCord_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+    /// <summary>Gets whether the "required_cord" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasRequiredCord {
+      get { return requiredCord_ != null; }
+    }
+    /// <summary>Clears the value of the "required_cord" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearRequiredCord() {
+      requiredCord_ = null;
+    }
+
+    /// <summary>Field number for the "recursive_message" field.</summary>
+    public const int RecursiveMessageFieldNumber = 27;
+    private global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2 recursiveMessage_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2 RecursiveMessage {
+      get { return recursiveMessage_; }
+      set {
+        recursiveMessage_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "optional_recursive_message" field.</summary>
+    public const int OptionalRecursiveMessageFieldNumber = 28;
+    private global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2 optionalRecursiveMessage_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2 OptionalRecursiveMessage {
+      get { return optionalRecursiveMessage_; }
+      set {
+        optionalRecursiveMessage_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "data" field.</summary>
+    public const int DataFieldNumber = 201;
+    private global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.Data data_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.Data Data {
+      get { return data_; }
+      set {
+        data_ = value;
+      }
+    }
+    /// <summary>Gets whether the data field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasData {
+      get { return data_ != null; }
+    }
+    /// <summary>Clears the value of the data field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearData() {
+      data_ = null;
+    }
+
+    /// <summary>Field number for the "default_int32" field.</summary>
+    public const int DefaultInt32FieldNumber = 241;
+    private readonly static int DefaultInt32DefaultValue = -123456789;
+
+    private int defaultInt32_;
+    /// <summary>
+    /// default values
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int DefaultInt32 {
+      get { if ((_hasBits0 & 32768) != 0) { return defaultInt32_; } else { return DefaultInt32DefaultValue; } }
+      set {
+        _hasBits0 |= 32768;
+        defaultInt32_ = value;
+      }
+    }
+    /// <summary>Gets whether the "default_int32" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasDefaultInt32 {
+      get { return (_hasBits0 & 32768) != 0; }
+    }
+    /// <summary>Clears the value of the "default_int32" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearDefaultInt32() {
+      _hasBits0 &= ~32768;
+    }
+
+    /// <summary>Field number for the "default_int64" field.</summary>
+    public const int DefaultInt64FieldNumber = 242;
+    private readonly static long DefaultInt64DefaultValue = -9123456789123456789L;
+
+    private long defaultInt64_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public long DefaultInt64 {
+      get { if ((_hasBits0 & 65536) != 0) { return defaultInt64_; } else { return DefaultInt64DefaultValue; } }
+      set {
+        _hasBits0 |= 65536;
+        defaultInt64_ = value;
+      }
+    }
+    /// <summary>Gets whether the "default_int64" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasDefaultInt64 {
+      get { return (_hasBits0 & 65536) != 0; }
+    }
+    /// <summary>Clears the value of the "default_int64" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearDefaultInt64() {
+      _hasBits0 &= ~65536;
+    }
+
+    /// <summary>Field number for the "default_uint32" field.</summary>
+    public const int DefaultUint32FieldNumber = 243;
+    private readonly static uint DefaultUint32DefaultValue = 2123456789;
+
+    private uint defaultUint32_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint DefaultUint32 {
+      get { if ((_hasBits0 & 131072) != 0) { return defaultUint32_; } else { return DefaultUint32DefaultValue; } }
+      set {
+        _hasBits0 |= 131072;
+        defaultUint32_ = value;
+      }
+    }
+    /// <summary>Gets whether the "default_uint32" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasDefaultUint32 {
+      get { return (_hasBits0 & 131072) != 0; }
+    }
+    /// <summary>Clears the value of the "default_uint32" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearDefaultUint32() {
+      _hasBits0 &= ~131072;
+    }
+
+    /// <summary>Field number for the "default_uint64" field.</summary>
+    public const int DefaultUint64FieldNumber = 244;
+    private readonly static ulong DefaultUint64DefaultValue = 10123456789123456789UL;
+
+    private ulong defaultUint64_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ulong DefaultUint64 {
+      get { if ((_hasBits0 & 262144) != 0) { return defaultUint64_; } else { return DefaultUint64DefaultValue; } }
+      set {
+        _hasBits0 |= 262144;
+        defaultUint64_ = value;
+      }
+    }
+    /// <summary>Gets whether the "default_uint64" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasDefaultUint64 {
+      get { return (_hasBits0 & 262144) != 0; }
+    }
+    /// <summary>Clears the value of the "default_uint64" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearDefaultUint64() {
+      _hasBits0 &= ~262144;
+    }
+
+    /// <summary>Field number for the "default_sint32" field.</summary>
+    public const int DefaultSint32FieldNumber = 245;
+    private readonly static int DefaultSint32DefaultValue = -123456789;
+
+    private int defaultSint32_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int DefaultSint32 {
+      get { if ((_hasBits0 & 524288) != 0) { return defaultSint32_; } else { return DefaultSint32DefaultValue; } }
+      set {
+        _hasBits0 |= 524288;
+        defaultSint32_ = value;
+      }
+    }
+    /// <summary>Gets whether the "default_sint32" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasDefaultSint32 {
+      get { return (_hasBits0 & 524288) != 0; }
+    }
+    /// <summary>Clears the value of the "default_sint32" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearDefaultSint32() {
+      _hasBits0 &= ~524288;
+    }
+
+    /// <summary>Field number for the "default_sint64" field.</summary>
+    public const int DefaultSint64FieldNumber = 246;
+    private readonly static long DefaultSint64DefaultValue = -9123456789123456789L;
+
+    private long defaultSint64_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public long DefaultSint64 {
+      get { if ((_hasBits0 & 1048576) != 0) { return defaultSint64_; } else { return DefaultSint64DefaultValue; } }
+      set {
+        _hasBits0 |= 1048576;
+        defaultSint64_ = value;
+      }
+    }
+    /// <summary>Gets whether the "default_sint64" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasDefaultSint64 {
+      get { return (_hasBits0 & 1048576) != 0; }
+    }
+    /// <summary>Clears the value of the "default_sint64" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearDefaultSint64() {
+      _hasBits0 &= ~1048576;
+    }
+
+    /// <summary>Field number for the "default_fixed32" field.</summary>
+    public const int DefaultFixed32FieldNumber = 247;
+    private readonly static uint DefaultFixed32DefaultValue = 2123456789;
+
+    private uint defaultFixed32_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public uint DefaultFixed32 {
+      get { if ((_hasBits0 & 2097152) != 0) { return defaultFixed32_; } else { return DefaultFixed32DefaultValue; } }
+      set {
+        _hasBits0 |= 2097152;
+        defaultFixed32_ = value;
+      }
+    }
+    /// <summary>Gets whether the "default_fixed32" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasDefaultFixed32 {
+      get { return (_hasBits0 & 2097152) != 0; }
+    }
+    /// <summary>Clears the value of the "default_fixed32" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearDefaultFixed32() {
+      _hasBits0 &= ~2097152;
+    }
+
+    /// <summary>Field number for the "default_fixed64" field.</summary>
+    public const int DefaultFixed64FieldNumber = 248;
+    private readonly static ulong DefaultFixed64DefaultValue = 10123456789123456789UL;
+
+    private ulong defaultFixed64_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public ulong DefaultFixed64 {
+      get { if ((_hasBits0 & 4194304) != 0) { return defaultFixed64_; } else { return DefaultFixed64DefaultValue; } }
+      set {
+        _hasBits0 |= 4194304;
+        defaultFixed64_ = value;
+      }
+    }
+    /// <summary>Gets whether the "default_fixed64" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasDefaultFixed64 {
+      get { return (_hasBits0 & 4194304) != 0; }
+    }
+    /// <summary>Clears the value of the "default_fixed64" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearDefaultFixed64() {
+      _hasBits0 &= ~4194304;
+    }
+
+    /// <summary>Field number for the "default_sfixed32" field.</summary>
+    public const int DefaultSfixed32FieldNumber = 249;
+    private readonly static int DefaultSfixed32DefaultValue = -123456789;
+
+    private int defaultSfixed32_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int DefaultSfixed32 {
+      get { if ((_hasBits0 & 8388608) != 0) { return defaultSfixed32_; } else { return DefaultSfixed32DefaultValue; } }
+      set {
+        _hasBits0 |= 8388608;
+        defaultSfixed32_ = value;
+      }
+    }
+    /// <summary>Gets whether the "default_sfixed32" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasDefaultSfixed32 {
+      get { return (_hasBits0 & 8388608) != 0; }
+    }
+    /// <summary>Clears the value of the "default_sfixed32" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearDefaultSfixed32() {
+      _hasBits0 &= ~8388608;
+    }
+
+    /// <summary>Field number for the "default_sfixed64" field.</summary>
+    public const int DefaultSfixed64FieldNumber = 250;
+    private readonly static long DefaultSfixed64DefaultValue = -9123456789123456789L;
+
+    private long defaultSfixed64_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public long DefaultSfixed64 {
+      get { if ((_hasBits0 & 16777216) != 0) { return defaultSfixed64_; } else { return DefaultSfixed64DefaultValue; } }
+      set {
+        _hasBits0 |= 16777216;
+        defaultSfixed64_ = value;
+      }
+    }
+    /// <summary>Gets whether the "default_sfixed64" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasDefaultSfixed64 {
+      get { return (_hasBits0 & 16777216) != 0; }
+    }
+    /// <summary>Clears the value of the "default_sfixed64" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearDefaultSfixed64() {
+      _hasBits0 &= ~16777216;
+    }
+
+    /// <summary>Field number for the "default_float" field.</summary>
+    public const int DefaultFloatFieldNumber = 251;
+    private readonly static float DefaultFloatDefaultValue = 9e+09F;
+
+    private float defaultFloat_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public float DefaultFloat {
+      get { if ((_hasBits0 & 33554432) != 0) { return defaultFloat_; } else { return DefaultFloatDefaultValue; } }
+      set {
+        _hasBits0 |= 33554432;
+        defaultFloat_ = value;
+      }
+    }
+    /// <summary>Gets whether the "default_float" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasDefaultFloat {
+      get { return (_hasBits0 & 33554432) != 0; }
+    }
+    /// <summary>Clears the value of the "default_float" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearDefaultFloat() {
+      _hasBits0 &= ~33554432;
+    }
+
+    /// <summary>Field number for the "default_double" field.</summary>
+    public const int DefaultDoubleFieldNumber = 252;
+    private readonly static double DefaultDoubleDefaultValue = 7e+22D;
+
+    private double defaultDouble_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public double DefaultDouble {
+      get { if ((_hasBits0 & 67108864) != 0) { return defaultDouble_; } else { return DefaultDoubleDefaultValue; } }
+      set {
+        _hasBits0 |= 67108864;
+        defaultDouble_ = value;
+      }
+    }
+    /// <summary>Gets whether the "default_double" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasDefaultDouble {
+      get { return (_hasBits0 & 67108864) != 0; }
+    }
+    /// <summary>Clears the value of the "default_double" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearDefaultDouble() {
+      _hasBits0 &= ~67108864;
+    }
+
+    /// <summary>Field number for the "default_bool" field.</summary>
+    public const int DefaultBoolFieldNumber = 253;
+    private readonly static bool DefaultBoolDefaultValue = true;
+
+    private bool defaultBool_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool DefaultBool {
+      get { if ((_hasBits0 & 134217728) != 0) { return defaultBool_; } else { return DefaultBoolDefaultValue; } }
+      set {
+        _hasBits0 |= 134217728;
+        defaultBool_ = value;
+      }
+    }
+    /// <summary>Gets whether the "default_bool" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasDefaultBool {
+      get { return (_hasBits0 & 134217728) != 0; }
+    }
+    /// <summary>Clears the value of the "default_bool" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearDefaultBool() {
+      _hasBits0 &= ~134217728;
+    }
+
+    /// <summary>Field number for the "default_string" field.</summary>
+    public const int DefaultStringFieldNumber = 254;
+    private readonly static string DefaultStringDefaultValue = global::System.Text.Encoding.UTF8.GetString(global::System.Convert.FromBase64String("Um9zZWJ1ZA=="), 0, 7);
+
+    private string defaultString_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string DefaultString {
+      get { return defaultString_ ?? DefaultStringDefaultValue; }
+      set {
+        defaultString_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+    /// <summary>Gets whether the "default_string" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasDefaultString {
+      get { return defaultString_ != null; }
+    }
+    /// <summary>Clears the value of the "default_string" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearDefaultString() {
+      defaultString_ = null;
+    }
+
+    /// <summary>Field number for the "default_bytes" field.</summary>
+    public const int DefaultBytesFieldNumber = 255;
+    private readonly static pb::ByteString DefaultBytesDefaultValue = pb::ByteString.FromBase64("am9zaHVh");
+
+    private pb::ByteString defaultBytes_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public pb::ByteString DefaultBytes {
+      get { return defaultBytes_ ?? DefaultBytesDefaultValue; }
+      set {
+        defaultBytes_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+    /// <summary>Gets whether the "default_bytes" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasDefaultBytes {
+      get { return defaultBytes_ != null; }
+    }
+    /// <summary>Clears the value of the "default_bytes" field</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearDefaultBytes() {
+      defaultBytes_ = null;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override bool Equals(object other) {
+      return Equals(other as TestAllRequiredTypesProto2);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool Equals(TestAllRequiredTypesProto2 other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (RequiredInt32 != other.RequiredInt32) return false;
+      if (RequiredInt64 != other.RequiredInt64) return false;
+      if (RequiredUint32 != other.RequiredUint32) return false;
+      if (RequiredUint64 != other.RequiredUint64) return false;
+      if (RequiredSint32 != other.RequiredSint32) return false;
+      if (RequiredSint64 != other.RequiredSint64) return false;
+      if (RequiredFixed32 != other.RequiredFixed32) return false;
+      if (RequiredFixed64 != other.RequiredFixed64) return false;
+      if (RequiredSfixed32 != other.RequiredSfixed32) return false;
+      if (RequiredSfixed64 != other.RequiredSfixed64) return false;
+      if (!pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.Equals(RequiredFloat, other.RequiredFloat)) return false;
+      if (!pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.Equals(RequiredDouble, other.RequiredDouble)) return false;
+      if (RequiredBool != other.RequiredBool) return false;
+      if (RequiredString != other.RequiredString) return false;
+      if (RequiredBytes != other.RequiredBytes) return false;
+      if (!object.Equals(RequiredNestedMessage, other.RequiredNestedMessage)) return false;
+      if (!object.Equals(RequiredForeignMessage, other.RequiredForeignMessage)) return false;
+      if (RequiredNestedEnum != other.RequiredNestedEnum) return false;
+      if (RequiredForeignEnum != other.RequiredForeignEnum) return false;
+      if (RequiredStringPiece != other.RequiredStringPiece) return false;
+      if (RequiredCord != other.RequiredCord) return false;
+      if (!object.Equals(RecursiveMessage, other.RecursiveMessage)) return false;
+      if (!object.Equals(OptionalRecursiveMessage, other.OptionalRecursiveMessage)) return false;
+      if (!object.Equals(Data, other.Data)) return false;
+      if (DefaultInt32 != other.DefaultInt32) return false;
+      if (DefaultInt64 != other.DefaultInt64) return false;
+      if (DefaultUint32 != other.DefaultUint32) return false;
+      if (DefaultUint64 != other.DefaultUint64) return false;
+      if (DefaultSint32 != other.DefaultSint32) return false;
+      if (DefaultSint64 != other.DefaultSint64) return false;
+      if (DefaultFixed32 != other.DefaultFixed32) return false;
+      if (DefaultFixed64 != other.DefaultFixed64) return false;
+      if (DefaultSfixed32 != other.DefaultSfixed32) return false;
+      if (DefaultSfixed64 != other.DefaultSfixed64) return false;
+      if (!pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.Equals(DefaultFloat, other.DefaultFloat)) return false;
+      if (!pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.Equals(DefaultDouble, other.DefaultDouble)) return false;
+      if (DefaultBool != other.DefaultBool) return false;
+      if (DefaultString != other.DefaultString) return false;
+      if (DefaultBytes != other.DefaultBytes) return false;
+      if (!Equals(_extensions, other._extensions)) {
+        return false;
+      }
+      return Equals(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (HasRequiredInt32) hash ^= RequiredInt32.GetHashCode();
+      if (HasRequiredInt64) hash ^= RequiredInt64.GetHashCode();
+      if (HasRequiredUint32) hash ^= RequiredUint32.GetHashCode();
+      if (HasRequiredUint64) hash ^= RequiredUint64.GetHashCode();
+      if (HasRequiredSint32) hash ^= RequiredSint32.GetHashCode();
+      if (HasRequiredSint64) hash ^= RequiredSint64.GetHashCode();
+      if (HasRequiredFixed32) hash ^= RequiredFixed32.GetHashCode();
+      if (HasRequiredFixed64) hash ^= RequiredFixed64.GetHashCode();
+      if (HasRequiredSfixed32) hash ^= RequiredSfixed32.GetHashCode();
+      if (HasRequiredSfixed64) hash ^= RequiredSfixed64.GetHashCode();
+      if (HasRequiredFloat) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode(RequiredFloat);
+      if (HasRequiredDouble) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode(RequiredDouble);
+      if (HasRequiredBool) hash ^= RequiredBool.GetHashCode();
+      if (HasRequiredString) hash ^= RequiredString.GetHashCode();
+      if (HasRequiredBytes) hash ^= RequiredBytes.GetHashCode();
+      if (requiredNestedMessage_ != null) hash ^= RequiredNestedMessage.GetHashCode();
+      if (requiredForeignMessage_ != null) hash ^= RequiredForeignMessage.GetHashCode();
+      if (HasRequiredNestedEnum) hash ^= RequiredNestedEnum.GetHashCode();
+      if (HasRequiredForeignEnum) hash ^= RequiredForeignEnum.GetHashCode();
+      if (HasRequiredStringPiece) hash ^= RequiredStringPiece.GetHashCode();
+      if (HasRequiredCord) hash ^= RequiredCord.GetHashCode();
+      if (recursiveMessage_ != null) hash ^= RecursiveMessage.GetHashCode();
+      if (optionalRecursiveMessage_ != null) hash ^= OptionalRecursiveMessage.GetHashCode();
+      if (HasData) hash ^= Data.GetHashCode();
+      if (HasDefaultInt32) hash ^= DefaultInt32.GetHashCode();
+      if (HasDefaultInt64) hash ^= DefaultInt64.GetHashCode();
+      if (HasDefaultUint32) hash ^= DefaultUint32.GetHashCode();
+      if (HasDefaultUint64) hash ^= DefaultUint64.GetHashCode();
+      if (HasDefaultSint32) hash ^= DefaultSint32.GetHashCode();
+      if (HasDefaultSint64) hash ^= DefaultSint64.GetHashCode();
+      if (HasDefaultFixed32) hash ^= DefaultFixed32.GetHashCode();
+      if (HasDefaultFixed64) hash ^= DefaultFixed64.GetHashCode();
+      if (HasDefaultSfixed32) hash ^= DefaultSfixed32.GetHashCode();
+      if (HasDefaultSfixed64) hash ^= DefaultSfixed64.GetHashCode();
+      if (HasDefaultFloat) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode(DefaultFloat);
+      if (HasDefaultDouble) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode(DefaultDouble);
+      if (HasDefaultBool) hash ^= DefaultBool.GetHashCode();
+      if (HasDefaultString) hash ^= DefaultString.GetHashCode();
+      if (HasDefaultBytes) hash ^= DefaultBytes.GetHashCode();
+      if (_extensions != null) {
+        hash ^= _extensions.GetHashCode();
+      }
+      if (_unknownFields != null) {
+        hash ^= _unknownFields.GetHashCode();
+      }
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void WriteTo(pb::CodedOutputStream output) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      output.WriteRawMessage(this);
+    #else
+      if (HasRequiredInt32) {
+        output.WriteRawTag(8);
+        output.WriteInt32(RequiredInt32);
+      }
+      if (HasRequiredInt64) {
+        output.WriteRawTag(16);
+        output.WriteInt64(RequiredInt64);
+      }
+      if (HasRequiredUint32) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(RequiredUint32);
+      }
+      if (HasRequiredUint64) {
+        output.WriteRawTag(32);
+        output.WriteUInt64(RequiredUint64);
+      }
+      if (HasRequiredSint32) {
+        output.WriteRawTag(40);
+        output.WriteSInt32(RequiredSint32);
+      }
+      if (HasRequiredSint64) {
+        output.WriteRawTag(48);
+        output.WriteSInt64(RequiredSint64);
+      }
+      if (HasRequiredFixed32) {
+        output.WriteRawTag(61);
+        output.WriteFixed32(RequiredFixed32);
+      }
+      if (HasRequiredFixed64) {
+        output.WriteRawTag(65);
+        output.WriteFixed64(RequiredFixed64);
+      }
+      if (HasRequiredSfixed32) {
+        output.WriteRawTag(77);
+        output.WriteSFixed32(RequiredSfixed32);
+      }
+      if (HasRequiredSfixed64) {
+        output.WriteRawTag(81);
+        output.WriteSFixed64(RequiredSfixed64);
+      }
+      if (HasRequiredFloat) {
+        output.WriteRawTag(93);
+        output.WriteFloat(RequiredFloat);
+      }
+      if (HasRequiredDouble) {
+        output.WriteRawTag(97);
+        output.WriteDouble(RequiredDouble);
+      }
+      if (HasRequiredBool) {
+        output.WriteRawTag(104);
+        output.WriteBool(RequiredBool);
+      }
+      if (HasRequiredString) {
+        output.WriteRawTag(114);
+        output.WriteString(RequiredString);
+      }
+      if (HasRequiredBytes) {
+        output.WriteRawTag(122);
+        output.WriteBytes(RequiredBytes);
+      }
+      if (requiredNestedMessage_ != null) {
+        output.WriteRawTag(146, 1);
+        output.WriteMessage(RequiredNestedMessage);
+      }
+      if (requiredForeignMessage_ != null) {
+        output.WriteRawTag(154, 1);
+        output.WriteMessage(RequiredForeignMessage);
+      }
+      if (HasRequiredNestedEnum) {
+        output.WriteRawTag(168, 1);
+        output.WriteEnum((int) RequiredNestedEnum);
+      }
+      if (HasRequiredForeignEnum) {
+        output.WriteRawTag(176, 1);
+        output.WriteEnum((int) RequiredForeignEnum);
+      }
+      if (HasRequiredStringPiece) {
+        output.WriteRawTag(194, 1);
+        output.WriteString(RequiredStringPiece);
+      }
+      if (HasRequiredCord) {
+        output.WriteRawTag(202, 1);
+        output.WriteString(RequiredCord);
+      }
+      if (recursiveMessage_ != null) {
+        output.WriteRawTag(218, 1);
+        output.WriteMessage(RecursiveMessage);
+      }
+      if (optionalRecursiveMessage_ != null) {
+        output.WriteRawTag(226, 1);
+        output.WriteMessage(OptionalRecursiveMessage);
+      }
+      if (HasData) {
+        output.WriteRawTag(203, 12);
+        output.WriteGroup(Data);
+        output.WriteRawTag(204, 12);
+      }
+      if (HasDefaultInt32) {
+        output.WriteRawTag(136, 15);
+        output.WriteInt32(DefaultInt32);
+      }
+      if (HasDefaultInt64) {
+        output.WriteRawTag(144, 15);
+        output.WriteInt64(DefaultInt64);
+      }
+      if (HasDefaultUint32) {
+        output.WriteRawTag(152, 15);
+        output.WriteUInt32(DefaultUint32);
+      }
+      if (HasDefaultUint64) {
+        output.WriteRawTag(160, 15);
+        output.WriteUInt64(DefaultUint64);
+      }
+      if (HasDefaultSint32) {
+        output.WriteRawTag(168, 15);
+        output.WriteSInt32(DefaultSint32);
+      }
+      if (HasDefaultSint64) {
+        output.WriteRawTag(176, 15);
+        output.WriteSInt64(DefaultSint64);
+      }
+      if (HasDefaultFixed32) {
+        output.WriteRawTag(189, 15);
+        output.WriteFixed32(DefaultFixed32);
+      }
+      if (HasDefaultFixed64) {
+        output.WriteRawTag(193, 15);
+        output.WriteFixed64(DefaultFixed64);
+      }
+      if (HasDefaultSfixed32) {
+        output.WriteRawTag(205, 15);
+        output.WriteSFixed32(DefaultSfixed32);
+      }
+      if (HasDefaultSfixed64) {
+        output.WriteRawTag(209, 15);
+        output.WriteSFixed64(DefaultSfixed64);
+      }
+      if (HasDefaultFloat) {
+        output.WriteRawTag(221, 15);
+        output.WriteFloat(DefaultFloat);
+      }
+      if (HasDefaultDouble) {
+        output.WriteRawTag(225, 15);
+        output.WriteDouble(DefaultDouble);
+      }
+      if (HasDefaultBool) {
+        output.WriteRawTag(232, 15);
+        output.WriteBool(DefaultBool);
+      }
+      if (HasDefaultString) {
+        output.WriteRawTag(242, 15);
+        output.WriteString(DefaultString);
+      }
+      if (HasDefaultBytes) {
+        output.WriteRawTag(250, 15);
+        output.WriteBytes(DefaultBytes);
+      }
+      if (_extensions != null) {
+        _extensions.WriteTo(output);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(output);
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+      if (HasRequiredInt32) {
+        output.WriteRawTag(8);
+        output.WriteInt32(RequiredInt32);
+      }
+      if (HasRequiredInt64) {
+        output.WriteRawTag(16);
+        output.WriteInt64(RequiredInt64);
+      }
+      if (HasRequiredUint32) {
+        output.WriteRawTag(24);
+        output.WriteUInt32(RequiredUint32);
+      }
+      if (HasRequiredUint64) {
+        output.WriteRawTag(32);
+        output.WriteUInt64(RequiredUint64);
+      }
+      if (HasRequiredSint32) {
+        output.WriteRawTag(40);
+        output.WriteSInt32(RequiredSint32);
+      }
+      if (HasRequiredSint64) {
+        output.WriteRawTag(48);
+        output.WriteSInt64(RequiredSint64);
+      }
+      if (HasRequiredFixed32) {
+        output.WriteRawTag(61);
+        output.WriteFixed32(RequiredFixed32);
+      }
+      if (HasRequiredFixed64) {
+        output.WriteRawTag(65);
+        output.WriteFixed64(RequiredFixed64);
+      }
+      if (HasRequiredSfixed32) {
+        output.WriteRawTag(77);
+        output.WriteSFixed32(RequiredSfixed32);
+      }
+      if (HasRequiredSfixed64) {
+        output.WriteRawTag(81);
+        output.WriteSFixed64(RequiredSfixed64);
+      }
+      if (HasRequiredFloat) {
+        output.WriteRawTag(93);
+        output.WriteFloat(RequiredFloat);
+      }
+      if (HasRequiredDouble) {
+        output.WriteRawTag(97);
+        output.WriteDouble(RequiredDouble);
+      }
+      if (HasRequiredBool) {
+        output.WriteRawTag(104);
+        output.WriteBool(RequiredBool);
+      }
+      if (HasRequiredString) {
+        output.WriteRawTag(114);
+        output.WriteString(RequiredString);
+      }
+      if (HasRequiredBytes) {
+        output.WriteRawTag(122);
+        output.WriteBytes(RequiredBytes);
+      }
+      if (requiredNestedMessage_ != null) {
+        output.WriteRawTag(146, 1);
+        output.WriteMessage(RequiredNestedMessage);
+      }
+      if (requiredForeignMessage_ != null) {
+        output.WriteRawTag(154, 1);
+        output.WriteMessage(RequiredForeignMessage);
+      }
+      if (HasRequiredNestedEnum) {
+        output.WriteRawTag(168, 1);
+        output.WriteEnum((int) RequiredNestedEnum);
+      }
+      if (HasRequiredForeignEnum) {
+        output.WriteRawTag(176, 1);
+        output.WriteEnum((int) RequiredForeignEnum);
+      }
+      if (HasRequiredStringPiece) {
+        output.WriteRawTag(194, 1);
+        output.WriteString(RequiredStringPiece);
+      }
+      if (HasRequiredCord) {
+        output.WriteRawTag(202, 1);
+        output.WriteString(RequiredCord);
+      }
+      if (recursiveMessage_ != null) {
+        output.WriteRawTag(218, 1);
+        output.WriteMessage(RecursiveMessage);
+      }
+      if (optionalRecursiveMessage_ != null) {
+        output.WriteRawTag(226, 1);
+        output.WriteMessage(OptionalRecursiveMessage);
+      }
+      if (HasData) {
+        output.WriteRawTag(203, 12);
+        output.WriteGroup(Data);
+        output.WriteRawTag(204, 12);
+      }
+      if (HasDefaultInt32) {
+        output.WriteRawTag(136, 15);
+        output.WriteInt32(DefaultInt32);
+      }
+      if (HasDefaultInt64) {
+        output.WriteRawTag(144, 15);
+        output.WriteInt64(DefaultInt64);
+      }
+      if (HasDefaultUint32) {
+        output.WriteRawTag(152, 15);
+        output.WriteUInt32(DefaultUint32);
+      }
+      if (HasDefaultUint64) {
+        output.WriteRawTag(160, 15);
+        output.WriteUInt64(DefaultUint64);
+      }
+      if (HasDefaultSint32) {
+        output.WriteRawTag(168, 15);
+        output.WriteSInt32(DefaultSint32);
+      }
+      if (HasDefaultSint64) {
+        output.WriteRawTag(176, 15);
+        output.WriteSInt64(DefaultSint64);
+      }
+      if (HasDefaultFixed32) {
+        output.WriteRawTag(189, 15);
+        output.WriteFixed32(DefaultFixed32);
+      }
+      if (HasDefaultFixed64) {
+        output.WriteRawTag(193, 15);
+        output.WriteFixed64(DefaultFixed64);
+      }
+      if (HasDefaultSfixed32) {
+        output.WriteRawTag(205, 15);
+        output.WriteSFixed32(DefaultSfixed32);
+      }
+      if (HasDefaultSfixed64) {
+        output.WriteRawTag(209, 15);
+        output.WriteSFixed64(DefaultSfixed64);
+      }
+      if (HasDefaultFloat) {
+        output.WriteRawTag(221, 15);
+        output.WriteFloat(DefaultFloat);
+      }
+      if (HasDefaultDouble) {
+        output.WriteRawTag(225, 15);
+        output.WriteDouble(DefaultDouble);
+      }
+      if (HasDefaultBool) {
+        output.WriteRawTag(232, 15);
+        output.WriteBool(DefaultBool);
+      }
+      if (HasDefaultString) {
+        output.WriteRawTag(242, 15);
+        output.WriteString(DefaultString);
+      }
+      if (HasDefaultBytes) {
+        output.WriteRawTag(250, 15);
+        output.WriteBytes(DefaultBytes);
+      }
+      if (_extensions != null) {
+        _extensions.WriteTo(ref output);
+      }
+      if (_unknownFields != null) {
+        _unknownFields.WriteTo(ref output);
+      }
+    }
+    #endif
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public int CalculateSize() {
+      int size = 0;
+      if (HasRequiredInt32) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(RequiredInt32);
+      }
+      if (HasRequiredInt64) {
+        size += 1 + pb::CodedOutputStream.ComputeInt64Size(RequiredInt64);
+      }
+      if (HasRequiredUint32) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt32Size(RequiredUint32);
+      }
+      if (HasRequiredUint64) {
+        size += 1 + pb::CodedOutputStream.ComputeUInt64Size(RequiredUint64);
+      }
+      if (HasRequiredSint32) {
+        size += 1 + pb::CodedOutputStream.ComputeSInt32Size(RequiredSint32);
+      }
+      if (HasRequiredSint64) {
+        size += 1 + pb::CodedOutputStream.ComputeSInt64Size(RequiredSint64);
+      }
+      if (HasRequiredFixed32) {
+        size += 1 + 4;
+      }
+      if (HasRequiredFixed64) {
+        size += 1 + 8;
+      }
+      if (HasRequiredSfixed32) {
+        size += 1 + 4;
+      }
+      if (HasRequiredSfixed64) {
+        size += 1 + 8;
+      }
+      if (HasRequiredFloat) {
+        size += 1 + 4;
+      }
+      if (HasRequiredDouble) {
+        size += 1 + 8;
+      }
+      if (HasRequiredBool) {
+        size += 1 + 1;
+      }
+      if (HasRequiredString) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(RequiredString);
+      }
+      if (HasRequiredBytes) {
+        size += 1 + pb::CodedOutputStream.ComputeBytesSize(RequiredBytes);
+      }
+      if (requiredNestedMessage_ != null) {
+        size += 2 + pb::CodedOutputStream.ComputeMessageSize(RequiredNestedMessage);
+      }
+      if (requiredForeignMessage_ != null) {
+        size += 2 + pb::CodedOutputStream.ComputeMessageSize(RequiredForeignMessage);
+      }
+      if (HasRequiredNestedEnum) {
+        size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) RequiredNestedEnum);
+      }
+      if (HasRequiredForeignEnum) {
+        size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) RequiredForeignEnum);
+      }
+      if (HasRequiredStringPiece) {
+        size += 2 + pb::CodedOutputStream.ComputeStringSize(RequiredStringPiece);
+      }
+      if (HasRequiredCord) {
+        size += 2 + pb::CodedOutputStream.ComputeStringSize(RequiredCord);
+      }
+      if (recursiveMessage_ != null) {
+        size += 2 + pb::CodedOutputStream.ComputeMessageSize(RecursiveMessage);
+      }
+      if (optionalRecursiveMessage_ != null) {
+        size += 2 + pb::CodedOutputStream.ComputeMessageSize(OptionalRecursiveMessage);
+      }
+      if (HasData) {
+        size += 4 + pb::CodedOutputStream.ComputeGroupSize(Data);
+      }
+      if (HasDefaultInt32) {
+        size += 2 + pb::CodedOutputStream.ComputeInt32Size(DefaultInt32);
+      }
+      if (HasDefaultInt64) {
+        size += 2 + pb::CodedOutputStream.ComputeInt64Size(DefaultInt64);
+      }
+      if (HasDefaultUint32) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt32Size(DefaultUint32);
+      }
+      if (HasDefaultUint64) {
+        size += 2 + pb::CodedOutputStream.ComputeUInt64Size(DefaultUint64);
+      }
+      if (HasDefaultSint32) {
+        size += 2 + pb::CodedOutputStream.ComputeSInt32Size(DefaultSint32);
+      }
+      if (HasDefaultSint64) {
+        size += 2 + pb::CodedOutputStream.ComputeSInt64Size(DefaultSint64);
+      }
+      if (HasDefaultFixed32) {
+        size += 2 + 4;
+      }
+      if (HasDefaultFixed64) {
+        size += 2 + 8;
+      }
+      if (HasDefaultSfixed32) {
+        size += 2 + 4;
+      }
+      if (HasDefaultSfixed64) {
+        size += 2 + 8;
+      }
+      if (HasDefaultFloat) {
+        size += 2 + 4;
+      }
+      if (HasDefaultDouble) {
+        size += 2 + 8;
+      }
+      if (HasDefaultBool) {
+        size += 2 + 1;
+      }
+      if (HasDefaultString) {
+        size += 2 + pb::CodedOutputStream.ComputeStringSize(DefaultString);
+      }
+      if (HasDefaultBytes) {
+        size += 2 + pb::CodedOutputStream.ComputeBytesSize(DefaultBytes);
+      }
+      if (_extensions != null) {
+        size += _extensions.CalculateSize();
+      }
+      if (_unknownFields != null) {
+        size += _unknownFields.CalculateSize();
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(TestAllRequiredTypesProto2 other) {
+      if (other == null) {
+        return;
+      }
+      if (other.HasRequiredInt32) {
+        RequiredInt32 = other.RequiredInt32;
+      }
+      if (other.HasRequiredInt64) {
+        RequiredInt64 = other.RequiredInt64;
+      }
+      if (other.HasRequiredUint32) {
+        RequiredUint32 = other.RequiredUint32;
+      }
+      if (other.HasRequiredUint64) {
+        RequiredUint64 = other.RequiredUint64;
+      }
+      if (other.HasRequiredSint32) {
+        RequiredSint32 = other.RequiredSint32;
+      }
+      if (other.HasRequiredSint64) {
+        RequiredSint64 = other.RequiredSint64;
+      }
+      if (other.HasRequiredFixed32) {
+        RequiredFixed32 = other.RequiredFixed32;
+      }
+      if (other.HasRequiredFixed64) {
+        RequiredFixed64 = other.RequiredFixed64;
+      }
+      if (other.HasRequiredSfixed32) {
+        RequiredSfixed32 = other.RequiredSfixed32;
+      }
+      if (other.HasRequiredSfixed64) {
+        RequiredSfixed64 = other.RequiredSfixed64;
+      }
+      if (other.HasRequiredFloat) {
+        RequiredFloat = other.RequiredFloat;
+      }
+      if (other.HasRequiredDouble) {
+        RequiredDouble = other.RequiredDouble;
+      }
+      if (other.HasRequiredBool) {
+        RequiredBool = other.RequiredBool;
+      }
+      if (other.HasRequiredString) {
+        RequiredString = other.RequiredString;
+      }
+      if (other.HasRequiredBytes) {
+        RequiredBytes = other.RequiredBytes;
+      }
+      if (other.requiredNestedMessage_ != null) {
+        if (requiredNestedMessage_ == null) {
+          RequiredNestedMessage = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.NestedMessage();
+        }
+        RequiredNestedMessage.MergeFrom(other.RequiredNestedMessage);
+      }
+      if (other.requiredForeignMessage_ != null) {
+        if (requiredForeignMessage_ == null) {
+          RequiredForeignMessage = new global::ProtobufTestMessages.Proto2.ForeignMessageProto2();
+        }
+        RequiredForeignMessage.MergeFrom(other.RequiredForeignMessage);
+      }
+      if (other.HasRequiredNestedEnum) {
+        RequiredNestedEnum = other.RequiredNestedEnum;
+      }
+      if (other.HasRequiredForeignEnum) {
+        RequiredForeignEnum = other.RequiredForeignEnum;
+      }
+      if (other.HasRequiredStringPiece) {
+        RequiredStringPiece = other.RequiredStringPiece;
+      }
+      if (other.HasRequiredCord) {
+        RequiredCord = other.RequiredCord;
+      }
+      if (other.recursiveMessage_ != null) {
+        if (recursiveMessage_ == null) {
+          RecursiveMessage = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2();
+        }
+        RecursiveMessage.MergeFrom(other.RecursiveMessage);
+      }
+      if (other.optionalRecursiveMessage_ != null) {
+        if (optionalRecursiveMessage_ == null) {
+          OptionalRecursiveMessage = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2();
+        }
+        OptionalRecursiveMessage.MergeFrom(other.OptionalRecursiveMessage);
+      }
+      if (other.HasData) {
+        if (!HasData) {
+          Data = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.Data();
+        }
+        Data.MergeFrom(other.Data);
+      }
+      if (other.HasDefaultInt32) {
+        DefaultInt32 = other.DefaultInt32;
+      }
+      if (other.HasDefaultInt64) {
+        DefaultInt64 = other.DefaultInt64;
+      }
+      if (other.HasDefaultUint32) {
+        DefaultUint32 = other.DefaultUint32;
+      }
+      if (other.HasDefaultUint64) {
+        DefaultUint64 = other.DefaultUint64;
+      }
+      if (other.HasDefaultSint32) {
+        DefaultSint32 = other.DefaultSint32;
+      }
+      if (other.HasDefaultSint64) {
+        DefaultSint64 = other.DefaultSint64;
+      }
+      if (other.HasDefaultFixed32) {
+        DefaultFixed32 = other.DefaultFixed32;
+      }
+      if (other.HasDefaultFixed64) {
+        DefaultFixed64 = other.DefaultFixed64;
+      }
+      if (other.HasDefaultSfixed32) {
+        DefaultSfixed32 = other.DefaultSfixed32;
+      }
+      if (other.HasDefaultSfixed64) {
+        DefaultSfixed64 = other.DefaultSfixed64;
+      }
+      if (other.HasDefaultFloat) {
+        DefaultFloat = other.DefaultFloat;
+      }
+      if (other.HasDefaultDouble) {
+        DefaultDouble = other.DefaultDouble;
+      }
+      if (other.HasDefaultBool) {
+        DefaultBool = other.DefaultBool;
+      }
+      if (other.HasDefaultString) {
+        DefaultString = other.DefaultString;
+      }
+      if (other.HasDefaultBytes) {
+        DefaultBytes = other.DefaultBytes;
+      }
+      pb::ExtensionSet.MergeFrom(ref _extensions, other._extensions);
+      _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void MergeFrom(pb::CodedInputStream input) {
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+      input.ReadRawMessage(this);
+    #else
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+            }
+            break;
+          case 8: {
+            RequiredInt32 = input.ReadInt32();
+            break;
+          }
+          case 16: {
+            RequiredInt64 = input.ReadInt64();
+            break;
+          }
+          case 24: {
+            RequiredUint32 = input.ReadUInt32();
+            break;
+          }
+          case 32: {
+            RequiredUint64 = input.ReadUInt64();
+            break;
+          }
+          case 40: {
+            RequiredSint32 = input.ReadSInt32();
+            break;
+          }
+          case 48: {
+            RequiredSint64 = input.ReadSInt64();
+            break;
+          }
+          case 61: {
+            RequiredFixed32 = input.ReadFixed32();
+            break;
+          }
+          case 65: {
+            RequiredFixed64 = input.ReadFixed64();
+            break;
+          }
+          case 77: {
+            RequiredSfixed32 = input.ReadSFixed32();
+            break;
+          }
+          case 81: {
+            RequiredSfixed64 = input.ReadSFixed64();
+            break;
+          }
+          case 93: {
+            RequiredFloat = input.ReadFloat();
+            break;
+          }
+          case 97: {
+            RequiredDouble = input.ReadDouble();
+            break;
+          }
+          case 104: {
+            RequiredBool = input.ReadBool();
+            break;
+          }
+          case 114: {
+            RequiredString = input.ReadString();
+            break;
+          }
+          case 122: {
+            RequiredBytes = input.ReadBytes();
+            break;
+          }
+          case 146: {
+            if (requiredNestedMessage_ == null) {
+              RequiredNestedMessage = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.NestedMessage();
+            }
+            input.ReadMessage(RequiredNestedMessage);
+            break;
+          }
+          case 154: {
+            if (requiredForeignMessage_ == null) {
+              RequiredForeignMessage = new global::ProtobufTestMessages.Proto2.ForeignMessageProto2();
+            }
+            input.ReadMessage(RequiredForeignMessage);
+            break;
+          }
+          case 168: {
+            RequiredNestedEnum = (global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.NestedEnum) input.ReadEnum();
+            break;
+          }
+          case 176: {
+            RequiredForeignEnum = (global::ProtobufTestMessages.Proto2.ForeignEnumProto2) input.ReadEnum();
+            break;
+          }
+          case 194: {
+            RequiredStringPiece = input.ReadString();
+            break;
+          }
+          case 202: {
+            RequiredCord = input.ReadString();
+            break;
+          }
+          case 218: {
+            if (recursiveMessage_ == null) {
+              RecursiveMessage = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2();
+            }
+            input.ReadMessage(RecursiveMessage);
+            break;
+          }
+          case 226: {
+            if (optionalRecursiveMessage_ == null) {
+              OptionalRecursiveMessage = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2();
+            }
+            input.ReadMessage(OptionalRecursiveMessage);
+            break;
+          }
+          case 1611: {
+            if (!HasData) {
+              Data = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.Data();
+            }
+            input.ReadGroup(Data);
+            break;
+          }
+          case 1928: {
+            DefaultInt32 = input.ReadInt32();
+            break;
+          }
+          case 1936: {
+            DefaultInt64 = input.ReadInt64();
+            break;
+          }
+          case 1944: {
+            DefaultUint32 = input.ReadUInt32();
+            break;
+          }
+          case 1952: {
+            DefaultUint64 = input.ReadUInt64();
+            break;
+          }
+          case 1960: {
+            DefaultSint32 = input.ReadSInt32();
+            break;
+          }
+          case 1968: {
+            DefaultSint64 = input.ReadSInt64();
+            break;
+          }
+          case 1981: {
+            DefaultFixed32 = input.ReadFixed32();
+            break;
+          }
+          case 1985: {
+            DefaultFixed64 = input.ReadFixed64();
+            break;
+          }
+          case 1997: {
+            DefaultSfixed32 = input.ReadSFixed32();
+            break;
+          }
+          case 2001: {
+            DefaultSfixed64 = input.ReadSFixed64();
+            break;
+          }
+          case 2013: {
+            DefaultFloat = input.ReadFloat();
+            break;
+          }
+          case 2017: {
+            DefaultDouble = input.ReadDouble();
+            break;
+          }
+          case 2024: {
+            DefaultBool = input.ReadBool();
+            break;
+          }
+          case 2034: {
+            DefaultString = input.ReadString();
+            break;
+          }
+          case 2042: {
+            DefaultBytes = input.ReadBytes();
+            break;
+          }
+        }
+      }
+    #endif
+    }
+
+    #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, ref input)) {
+              _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+            }
+            break;
+          case 8: {
+            RequiredInt32 = input.ReadInt32();
+            break;
+          }
+          case 16: {
+            RequiredInt64 = input.ReadInt64();
+            break;
+          }
+          case 24: {
+            RequiredUint32 = input.ReadUInt32();
+            break;
+          }
+          case 32: {
+            RequiredUint64 = input.ReadUInt64();
+            break;
+          }
+          case 40: {
+            RequiredSint32 = input.ReadSInt32();
+            break;
+          }
+          case 48: {
+            RequiredSint64 = input.ReadSInt64();
+            break;
+          }
+          case 61: {
+            RequiredFixed32 = input.ReadFixed32();
+            break;
+          }
+          case 65: {
+            RequiredFixed64 = input.ReadFixed64();
+            break;
+          }
+          case 77: {
+            RequiredSfixed32 = input.ReadSFixed32();
+            break;
+          }
+          case 81: {
+            RequiredSfixed64 = input.ReadSFixed64();
+            break;
+          }
+          case 93: {
+            RequiredFloat = input.ReadFloat();
+            break;
+          }
+          case 97: {
+            RequiredDouble = input.ReadDouble();
+            break;
+          }
+          case 104: {
+            RequiredBool = input.ReadBool();
+            break;
+          }
+          case 114: {
+            RequiredString = input.ReadString();
+            break;
+          }
+          case 122: {
+            RequiredBytes = input.ReadBytes();
+            break;
+          }
+          case 146: {
+            if (requiredNestedMessage_ == null) {
+              RequiredNestedMessage = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.NestedMessage();
+            }
+            input.ReadMessage(RequiredNestedMessage);
+            break;
+          }
+          case 154: {
+            if (requiredForeignMessage_ == null) {
+              RequiredForeignMessage = new global::ProtobufTestMessages.Proto2.ForeignMessageProto2();
+            }
+            input.ReadMessage(RequiredForeignMessage);
+            break;
+          }
+          case 168: {
+            RequiredNestedEnum = (global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.NestedEnum) input.ReadEnum();
+            break;
+          }
+          case 176: {
+            RequiredForeignEnum = (global::ProtobufTestMessages.Proto2.ForeignEnumProto2) input.ReadEnum();
+            break;
+          }
+          case 194: {
+            RequiredStringPiece = input.ReadString();
+            break;
+          }
+          case 202: {
+            RequiredCord = input.ReadString();
+            break;
+          }
+          case 218: {
+            if (recursiveMessage_ == null) {
+              RecursiveMessage = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2();
+            }
+            input.ReadMessage(RecursiveMessage);
+            break;
+          }
+          case 226: {
+            if (optionalRecursiveMessage_ == null) {
+              OptionalRecursiveMessage = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2();
+            }
+            input.ReadMessage(OptionalRecursiveMessage);
+            break;
+          }
+          case 1611: {
+            if (!HasData) {
+              Data = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.Data();
+            }
+            input.ReadGroup(Data);
+            break;
+          }
+          case 1928: {
+            DefaultInt32 = input.ReadInt32();
+            break;
+          }
+          case 1936: {
+            DefaultInt64 = input.ReadInt64();
+            break;
+          }
+          case 1944: {
+            DefaultUint32 = input.ReadUInt32();
+            break;
+          }
+          case 1952: {
+            DefaultUint64 = input.ReadUInt64();
+            break;
+          }
+          case 1960: {
+            DefaultSint32 = input.ReadSInt32();
+            break;
+          }
+          case 1968: {
+            DefaultSint64 = input.ReadSInt64();
+            break;
+          }
+          case 1981: {
+            DefaultFixed32 = input.ReadFixed32();
+            break;
+          }
+          case 1985: {
+            DefaultFixed64 = input.ReadFixed64();
+            break;
+          }
+          case 1997: {
+            DefaultSfixed32 = input.ReadSFixed32();
+            break;
+          }
+          case 2001: {
+            DefaultSfixed64 = input.ReadSFixed64();
+            break;
+          }
+          case 2013: {
+            DefaultFloat = input.ReadFloat();
+            break;
+          }
+          case 2017: {
+            DefaultDouble = input.ReadDouble();
+            break;
+          }
+          case 2024: {
+            DefaultBool = input.ReadBool();
+            break;
+          }
+          case 2034: {
+            DefaultString = input.ReadString();
+            break;
+          }
+          case 2042: {
+            DefaultBytes = input.ReadBytes();
+            break;
+          }
+        }
+      }
+    }
+    #endif
+
+    public TValue GetExtension<TValue>(pb::Extension<TestAllRequiredTypesProto2, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetExtension<TValue>(pb::RepeatedExtension<TestAllRequiredTypesProto2, TValue> extension) {
+      return pb::ExtensionSet.Get(ref _extensions, extension);
+    }
+    public pbc::RepeatedField<TValue> GetOrInitializeExtension<TValue>(pb::RepeatedExtension<TestAllRequiredTypesProto2, TValue> extension) {
+      return pb::ExtensionSet.GetOrInitialize(ref _extensions, extension);
+    }
+    public void SetExtension<TValue>(pb::Extension<TestAllRequiredTypesProto2, TValue> extension, TValue value) {
+      pb::ExtensionSet.Set(ref _extensions, extension, value);
+    }
+    public bool HasExtension<TValue>(pb::Extension<TestAllRequiredTypesProto2, TValue> extension) {
+      return pb::ExtensionSet.Has(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::Extension<TestAllRequiredTypesProto2, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+    public void ClearExtension<TValue>(pb::RepeatedExtension<TestAllRequiredTypesProto2, TValue> extension) {
+      pb::ExtensionSet.Clear(ref _extensions, extension);
+    }
+
+    #region Nested types
+    /// <summary>Container for nested types declared in the TestAllRequiredTypesProto2 message type.</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public static partial class Types {
+      public enum NestedEnum {
+        [pbr::OriginalName("FOO")] Foo = 0,
+        [pbr::OriginalName("BAR")] Bar = 1,
+        [pbr::OriginalName("BAZ")] Baz = 2,
+        /// <summary>
+        /// Intentionally negative.
+        /// </summary>
+        [pbr::OriginalName("NEG")] Neg = -1,
+      }
+
+      public sealed partial class NestedMessage : pb::IMessage<NestedMessage>
+      #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          , pb::IBufferMessage
+      #endif
+      {
+        private static readonly pb::MessageParser<NestedMessage> _parser = new pb::MessageParser<NestedMessage>(() => new NestedMessage());
+        private pb::UnknownFieldSet _unknownFields;
+        private int _hasBits0;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public static pb::MessageParser<NestedMessage> Parser { get { return _parser; } }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public static pbr::MessageDescriptor Descriptor {
+          get { return global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Descriptor.NestedTypes[0]; }
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        pbr::MessageDescriptor pb::IMessage.Descriptor {
+          get { return Descriptor; }
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public NestedMessage() {
+          OnConstruction();
+        }
+
+        partial void OnConstruction();
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public NestedMessage(NestedMessage other) : this() {
+          _hasBits0 = other._hasBits0;
+          a_ = other.a_;
+          corecursive_ = other.corecursive_ != null ? other.corecursive_.Clone() : null;
+          optionalCorecursive_ = other.optionalCorecursive_ != null ? other.optionalCorecursive_.Clone() : null;
+          _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public NestedMessage Clone() {
+          return new NestedMessage(this);
+        }
+
+        /// <summary>Field number for the "a" field.</summary>
+        public const int AFieldNumber = 1;
+        private readonly static int ADefaultValue = 0;
+
+        private int a_;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public int A {
+          get { if ((_hasBits0 & 1) != 0) { return a_; } else { return ADefaultValue; } }
+          set {
+            _hasBits0 |= 1;
+            a_ = value;
+          }
+        }
+        /// <summary>Gets whether the "a" field is set</summary>
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public bool HasA {
+          get { return (_hasBits0 & 1) != 0; }
+        }
+        /// <summary>Clears the value of the "a" field</summary>
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void ClearA() {
+          _hasBits0 &= ~1;
+        }
+
+        /// <summary>Field number for the "corecursive" field.</summary>
+        public const int CorecursiveFieldNumber = 2;
+        private global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2 corecursive_;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2 Corecursive {
+          get { return corecursive_; }
+          set {
+            corecursive_ = value;
+          }
+        }
+
+        /// <summary>Field number for the "optional_corecursive" field.</summary>
+        public const int OptionalCorecursiveFieldNumber = 3;
+        private global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2 optionalCorecursive_;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2 OptionalCorecursive {
+          get { return optionalCorecursive_; }
+          set {
+            optionalCorecursive_ = value;
+          }
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override bool Equals(object other) {
+          return Equals(other as NestedMessage);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public bool Equals(NestedMessage other) {
+          if (ReferenceEquals(other, null)) {
+            return false;
+          }
+          if (ReferenceEquals(other, this)) {
+            return true;
+          }
+          if (A != other.A) return false;
+          if (!object.Equals(Corecursive, other.Corecursive)) return false;
+          if (!object.Equals(OptionalCorecursive, other.OptionalCorecursive)) return false;
+          return Equals(_unknownFields, other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override int GetHashCode() {
+          int hash = 1;
+          if (HasA) hash ^= A.GetHashCode();
+          if (corecursive_ != null) hash ^= Corecursive.GetHashCode();
+          if (optionalCorecursive_ != null) hash ^= OptionalCorecursive.GetHashCode();
+          if (_unknownFields != null) {
+            hash ^= _unknownFields.GetHashCode();
+          }
+          return hash;
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override string ToString() {
+          return pb::JsonFormatter.ToDiagnosticString(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
+          if (HasA) {
+            output.WriteRawTag(8);
+            output.WriteInt32(A);
+          }
+          if (corecursive_ != null) {
+            output.WriteRawTag(18);
+            output.WriteMessage(Corecursive);
+          }
+          if (optionalCorecursive_ != null) {
+            output.WriteRawTag(26);
+            output.WriteMessage(OptionalCorecursive);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(output);
+          }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (HasA) {
+            output.WriteRawTag(8);
+            output.WriteInt32(A);
+          }
+          if (corecursive_ != null) {
+            output.WriteRawTag(18);
+            output.WriteMessage(Corecursive);
+          }
+          if (optionalCorecursive_ != null) {
+            output.WriteRawTag(26);
+            output.WriteMessage(OptionalCorecursive);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
+        }
+        #endif
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public int CalculateSize() {
+          int size = 0;
+          if (HasA) {
+            size += 1 + pb::CodedOutputStream.ComputeInt32Size(A);
+          }
+          if (corecursive_ != null) {
+            size += 1 + pb::CodedOutputStream.ComputeMessageSize(Corecursive);
+          }
+          if (optionalCorecursive_ != null) {
+            size += 1 + pb::CodedOutputStream.ComputeMessageSize(OptionalCorecursive);
+          }
+          if (_unknownFields != null) {
+            size += _unknownFields.CalculateSize();
+          }
+          return size;
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void MergeFrom(NestedMessage other) {
+          if (other == null) {
+            return;
+          }
+          if (other.HasA) {
+            A = other.A;
+          }
+          if (other.corecursive_ != null) {
+            if (corecursive_ == null) {
+              Corecursive = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2();
+            }
+            Corecursive.MergeFrom(other.Corecursive);
+          }
+          if (other.optionalCorecursive_ != null) {
+            if (optionalCorecursive_ == null) {
+              OptionalCorecursive = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2();
+            }
+            OptionalCorecursive.MergeFrom(other.OptionalCorecursive);
+          }
+          _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void MergeFrom(pb::CodedInputStream input) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          input.ReadRawMessage(this);
+        #else
+          uint tag;
+          while ((tag = input.ReadTag()) != 0) {
+            switch(tag) {
+              default:
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                break;
+              case 8: {
+                A = input.ReadInt32();
+                break;
+              }
+              case 18: {
+                if (corecursive_ == null) {
+                  Corecursive = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2();
+                }
+                input.ReadMessage(Corecursive);
+                break;
+              }
+              case 26: {
+                if (optionalCorecursive_ == null) {
+                  OptionalCorecursive = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2();
+                }
+                input.ReadMessage(OptionalCorecursive);
+                break;
+              }
+            }
+          }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+          uint tag;
+          while ((tag = input.ReadTag()) != 0) {
+            switch(tag) {
+              default:
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+                break;
+              case 8: {
+                A = input.ReadInt32();
+                break;
+              }
+              case 18: {
+                if (corecursive_ == null) {
+                  Corecursive = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2();
+                }
+                input.ReadMessage(Corecursive);
+                break;
+              }
+              case 26: {
+                if (optionalCorecursive_ == null) {
+                  OptionalCorecursive = new global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2();
+                }
+                input.ReadMessage(OptionalCorecursive);
+                break;
+              }
+            }
+          }
+        }
+        #endif
+
+      }
+
+      /// <summary>
+      /// groups
+      /// </summary>
+      public sealed partial class Data : pb::IMessage<Data>
+      #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          , pb::IBufferMessage
+      #endif
+      {
+        private static readonly pb::MessageParser<Data> _parser = new pb::MessageParser<Data>(() => new Data());
+        private pb::UnknownFieldSet _unknownFields;
+        private int _hasBits0;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public static pb::MessageParser<Data> Parser { get { return _parser; } }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public static pbr::MessageDescriptor Descriptor {
+          get { return global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Descriptor.NestedTypes[1]; }
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        pbr::MessageDescriptor pb::IMessage.Descriptor {
+          get { return Descriptor; }
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public Data() {
+          OnConstruction();
+        }
+
+        partial void OnConstruction();
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public Data(Data other) : this() {
+          _hasBits0 = other._hasBits0;
+          groupInt32_ = other.groupInt32_;
+          groupUint32_ = other.groupUint32_;
+          _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public Data Clone() {
+          return new Data(this);
+        }
+
+        /// <summary>Field number for the "group_int32" field.</summary>
+        public const int GroupInt32FieldNumber = 202;
+        private readonly static int GroupInt32DefaultValue = 0;
+
+        private int groupInt32_;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public int GroupInt32 {
+          get { if ((_hasBits0 & 1) != 0) { return groupInt32_; } else { return GroupInt32DefaultValue; } }
+          set {
+            _hasBits0 |= 1;
+            groupInt32_ = value;
+          }
+        }
+        /// <summary>Gets whether the "group_int32" field is set</summary>
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public bool HasGroupInt32 {
+          get { return (_hasBits0 & 1) != 0; }
+        }
+        /// <summary>Clears the value of the "group_int32" field</summary>
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void ClearGroupInt32() {
+          _hasBits0 &= ~1;
+        }
+
+        /// <summary>Field number for the "group_uint32" field.</summary>
+        public const int GroupUint32FieldNumber = 203;
+        private readonly static uint GroupUint32DefaultValue = 0;
+
+        private uint groupUint32_;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public uint GroupUint32 {
+          get { if ((_hasBits0 & 2) != 0) { return groupUint32_; } else { return GroupUint32DefaultValue; } }
+          set {
+            _hasBits0 |= 2;
+            groupUint32_ = value;
+          }
+        }
+        /// <summary>Gets whether the "group_uint32" field is set</summary>
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public bool HasGroupUint32 {
+          get { return (_hasBits0 & 2) != 0; }
+        }
+        /// <summary>Clears the value of the "group_uint32" field</summary>
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void ClearGroupUint32() {
+          _hasBits0 &= ~2;
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override bool Equals(object other) {
+          return Equals(other as Data);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public bool Equals(Data other) {
+          if (ReferenceEquals(other, null)) {
+            return false;
+          }
+          if (ReferenceEquals(other, this)) {
+            return true;
+          }
+          if (GroupInt32 != other.GroupInt32) return false;
+          if (GroupUint32 != other.GroupUint32) return false;
+          return Equals(_unknownFields, other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override int GetHashCode() {
+          int hash = 1;
+          if (HasGroupInt32) hash ^= GroupInt32.GetHashCode();
+          if (HasGroupUint32) hash ^= GroupUint32.GetHashCode();
+          if (_unknownFields != null) {
+            hash ^= _unknownFields.GetHashCode();
+          }
+          return hash;
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override string ToString() {
+          return pb::JsonFormatter.ToDiagnosticString(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
+          if (HasGroupInt32) {
+            output.WriteRawTag(208, 12);
+            output.WriteInt32(GroupInt32);
+          }
+          if (HasGroupUint32) {
+            output.WriteRawTag(216, 12);
+            output.WriteUInt32(GroupUint32);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(output);
+          }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (HasGroupInt32) {
+            output.WriteRawTag(208, 12);
+            output.WriteInt32(GroupInt32);
+          }
+          if (HasGroupUint32) {
+            output.WriteRawTag(216, 12);
+            output.WriteUInt32(GroupUint32);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
+        }
+        #endif
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public int CalculateSize() {
+          int size = 0;
+          if (HasGroupInt32) {
+            size += 2 + pb::CodedOutputStream.ComputeInt32Size(GroupInt32);
+          }
+          if (HasGroupUint32) {
+            size += 2 + pb::CodedOutputStream.ComputeUInt32Size(GroupUint32);
+          }
+          if (_unknownFields != null) {
+            size += _unknownFields.CalculateSize();
+          }
+          return size;
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void MergeFrom(Data other) {
+          if (other == null) {
+            return;
+          }
+          if (other.HasGroupInt32) {
+            GroupInt32 = other.GroupInt32;
+          }
+          if (other.HasGroupUint32) {
+            GroupUint32 = other.GroupUint32;
+          }
+          _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void MergeFrom(pb::CodedInputStream input) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          input.ReadRawMessage(this);
+        #else
+          uint tag;
+          while ((tag = input.ReadTag()) != 0) {
+            switch(tag) {
+              case 1612:
+                return;
+              default:
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                break;
+              case 1616: {
+                GroupInt32 = input.ReadInt32();
+                break;
+              }
+              case 1624: {
+                GroupUint32 = input.ReadUInt32();
+                break;
+              }
+            }
+          }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+          uint tag;
+          while ((tag = input.ReadTag()) != 0) {
+            switch(tag) {
+              case 1612:
+                return;
+              default:
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+                break;
+              case 1616: {
+                GroupInt32 = input.ReadInt32();
+                break;
+              }
+              case 1624: {
+                GroupUint32 = input.ReadUInt32();
+                break;
+              }
+            }
+          }
+        }
+        #endif
+
+      }
+
+      /// <summary>
+      /// message_set test case.
+      /// </summary>
+      public sealed partial class MessageSetCorrect : pb::IExtendableMessage<MessageSetCorrect>
+      #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          , pb::IBufferMessage
+      #endif
+      {
+        private static readonly pb::MessageParser<MessageSetCorrect> _parser = new pb::MessageParser<MessageSetCorrect>(() => new MessageSetCorrect());
+        private pb::UnknownFieldSet _unknownFields;
+        private pb::ExtensionSet<MessageSetCorrect> _extensions;
+        private pb::ExtensionSet<MessageSetCorrect> _Extensions { get { return _extensions; } }
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public static pb::MessageParser<MessageSetCorrect> Parser { get { return _parser; } }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public static pbr::MessageDescriptor Descriptor {
+          get { return global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Descriptor.NestedTypes[2]; }
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        pbr::MessageDescriptor pb::IMessage.Descriptor {
+          get { return Descriptor; }
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public MessageSetCorrect() {
+          OnConstruction();
+        }
+
+        partial void OnConstruction();
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public MessageSetCorrect(MessageSetCorrect other) : this() {
+          _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+          _extensions = pb::ExtensionSet.Clone(other._extensions);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public MessageSetCorrect Clone() {
+          return new MessageSetCorrect(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override bool Equals(object other) {
+          return Equals(other as MessageSetCorrect);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public bool Equals(MessageSetCorrect other) {
+          if (ReferenceEquals(other, null)) {
+            return false;
+          }
+          if (ReferenceEquals(other, this)) {
+            return true;
+          }
+          if (!Equals(_extensions, other._extensions)) {
+            return false;
+          }
+          return Equals(_unknownFields, other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override int GetHashCode() {
+          int hash = 1;
+          if (_extensions != null) {
+            hash ^= _extensions.GetHashCode();
+          }
+          if (_unknownFields != null) {
+            hash ^= _unknownFields.GetHashCode();
+          }
+          return hash;
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override string ToString() {
+          return pb::JsonFormatter.ToDiagnosticString(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
+          if (_extensions != null) {
+            _extensions.WriteTo(output);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(output);
+          }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (_extensions != null) {
+            _extensions.WriteTo(ref output);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
+        }
+        #endif
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public int CalculateSize() {
+          int size = 0;
+          if (_extensions != null) {
+            size += _extensions.CalculateSize();
+          }
+          if (_unknownFields != null) {
+            size += _unknownFields.CalculateSize();
+          }
+          return size;
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void MergeFrom(MessageSetCorrect other) {
+          if (other == null) {
+            return;
+          }
+          pb::ExtensionSet.MergeFrom(ref _extensions, other._extensions);
+          _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void MergeFrom(pb::CodedInputStream input) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          input.ReadRawMessage(this);
+        #else
+          uint tag;
+          while ((tag = input.ReadTag()) != 0) {
+            switch(tag) {
+              default:
+                if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, input)) {
+                  _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                }
+                break;
+            }
+          }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+          uint tag;
+          while ((tag = input.ReadTag()) != 0) {
+            switch(tag) {
+              default:
+                if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, ref input)) {
+                  _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+                }
+                break;
+            }
+          }
+        }
+        #endif
+
+        public TValue GetExtension<TValue>(pb::Extension<MessageSetCorrect, TValue> extension) {
+          return pb::ExtensionSet.Get(ref _extensions, extension);
+        }
+        public pbc::RepeatedField<TValue> GetExtension<TValue>(pb::RepeatedExtension<MessageSetCorrect, TValue> extension) {
+          return pb::ExtensionSet.Get(ref _extensions, extension);
+        }
+        public pbc::RepeatedField<TValue> GetOrInitializeExtension<TValue>(pb::RepeatedExtension<MessageSetCorrect, TValue> extension) {
+          return pb::ExtensionSet.GetOrInitialize(ref _extensions, extension);
+        }
+        public void SetExtension<TValue>(pb::Extension<MessageSetCorrect, TValue> extension, TValue value) {
+          pb::ExtensionSet.Set(ref _extensions, extension, value);
+        }
+        public bool HasExtension<TValue>(pb::Extension<MessageSetCorrect, TValue> extension) {
+          return pb::ExtensionSet.Has(ref _extensions, extension);
+        }
+        public void ClearExtension<TValue>(pb::Extension<MessageSetCorrect, TValue> extension) {
+          pb::ExtensionSet.Clear(ref _extensions, extension);
+        }
+        public void ClearExtension<TValue>(pb::RepeatedExtension<MessageSetCorrect, TValue> extension) {
+          pb::ExtensionSet.Clear(ref _extensions, extension);
+        }
+
+      }
+
+      public sealed partial class MessageSetCorrectExtension1 : pb::IMessage<MessageSetCorrectExtension1>
+      #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          , pb::IBufferMessage
+      #endif
+      {
+        private static readonly pb::MessageParser<MessageSetCorrectExtension1> _parser = new pb::MessageParser<MessageSetCorrectExtension1>(() => new MessageSetCorrectExtension1());
+        private pb::UnknownFieldSet _unknownFields;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public static pb::MessageParser<MessageSetCorrectExtension1> Parser { get { return _parser; } }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public static pbr::MessageDescriptor Descriptor {
+          get { return global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Descriptor.NestedTypes[3]; }
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        pbr::MessageDescriptor pb::IMessage.Descriptor {
+          get { return Descriptor; }
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public MessageSetCorrectExtension1() {
+          OnConstruction();
+        }
+
+        partial void OnConstruction();
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public MessageSetCorrectExtension1(MessageSetCorrectExtension1 other) : this() {
+          str_ = other.str_;
+          _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public MessageSetCorrectExtension1 Clone() {
+          return new MessageSetCorrectExtension1(this);
+        }
+
+        /// <summary>Field number for the "str" field.</summary>
+        public const int StrFieldNumber = 25;
+        private readonly static string StrDefaultValue = "";
+
+        private string str_;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public string Str {
+          get { return str_ ?? StrDefaultValue; }
+          set {
+            str_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+          }
+        }
+        /// <summary>Gets whether the "str" field is set</summary>
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public bool HasStr {
+          get { return str_ != null; }
+        }
+        /// <summary>Clears the value of the "str" field</summary>
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void ClearStr() {
+          str_ = null;
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override bool Equals(object other) {
+          return Equals(other as MessageSetCorrectExtension1);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public bool Equals(MessageSetCorrectExtension1 other) {
+          if (ReferenceEquals(other, null)) {
+            return false;
+          }
+          if (ReferenceEquals(other, this)) {
+            return true;
+          }
+          if (Str != other.Str) return false;
+          return Equals(_unknownFields, other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override int GetHashCode() {
+          int hash = 1;
+          if (HasStr) hash ^= Str.GetHashCode();
+          if (_unknownFields != null) {
+            hash ^= _unknownFields.GetHashCode();
+          }
+          return hash;
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override string ToString() {
+          return pb::JsonFormatter.ToDiagnosticString(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
+          if (HasStr) {
+            output.WriteRawTag(202, 1);
+            output.WriteString(Str);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(output);
+          }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (HasStr) {
+            output.WriteRawTag(202, 1);
+            output.WriteString(Str);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
+        }
+        #endif
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public int CalculateSize() {
+          int size = 0;
+          if (HasStr) {
+            size += 2 + pb::CodedOutputStream.ComputeStringSize(Str);
+          }
+          if (_unknownFields != null) {
+            size += _unknownFields.CalculateSize();
+          }
+          return size;
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void MergeFrom(MessageSetCorrectExtension1 other) {
+          if (other == null) {
+            return;
+          }
+          if (other.HasStr) {
+            Str = other.Str;
+          }
+          _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void MergeFrom(pb::CodedInputStream input) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          input.ReadRawMessage(this);
+        #else
+          uint tag;
+          while ((tag = input.ReadTag()) != 0) {
+            switch(tag) {
+              default:
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                break;
+              case 202: {
+                Str = input.ReadString();
+                break;
+              }
+            }
+          }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+          uint tag;
+          while ((tag = input.ReadTag()) != 0) {
+            switch(tag) {
+              default:
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+                break;
+              case 202: {
+                Str = input.ReadString();
+                break;
+              }
+            }
+          }
+        }
+        #endif
+
+        #region Extensions
+        /// <summary>Container for extensions for other messages declared in the MessageSetCorrectExtension1 message type.</summary>
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public static partial class Extensions {
+          public static readonly pb::Extension<global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrect, global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrectExtension1> MessageSetExtension =
+            new pb::Extension<global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrect, global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrectExtension1>(1547769, pb::FieldCodec.ForMessage(12382154, global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrectExtension1.Parser));
+        }
+        #endregion
+
+      }
+
+      public sealed partial class MessageSetCorrectExtension2 : pb::IMessage<MessageSetCorrectExtension2>
+      #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          , pb::IBufferMessage
+      #endif
+      {
+        private static readonly pb::MessageParser<MessageSetCorrectExtension2> _parser = new pb::MessageParser<MessageSetCorrectExtension2>(() => new MessageSetCorrectExtension2());
+        private pb::UnknownFieldSet _unknownFields;
+        private int _hasBits0;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public static pb::MessageParser<MessageSetCorrectExtension2> Parser { get { return _parser; } }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public static pbr::MessageDescriptor Descriptor {
+          get { return global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Descriptor.NestedTypes[4]; }
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        pbr::MessageDescriptor pb::IMessage.Descriptor {
+          get { return Descriptor; }
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public MessageSetCorrectExtension2() {
+          OnConstruction();
+        }
+
+        partial void OnConstruction();
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public MessageSetCorrectExtension2(MessageSetCorrectExtension2 other) : this() {
+          _hasBits0 = other._hasBits0;
+          i_ = other.i_;
+          _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public MessageSetCorrectExtension2 Clone() {
+          return new MessageSetCorrectExtension2(this);
+        }
+
+        /// <summary>Field number for the "i" field.</summary>
+        public const int IFieldNumber = 9;
+        private readonly static int IDefaultValue = 0;
+
+        private int i_;
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public int I {
+          get { if ((_hasBits0 & 1) != 0) { return i_; } else { return IDefaultValue; } }
+          set {
+            _hasBits0 |= 1;
+            i_ = value;
+          }
+        }
+        /// <summary>Gets whether the "i" field is set</summary>
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public bool HasI {
+          get { return (_hasBits0 & 1) != 0; }
+        }
+        /// <summary>Clears the value of the "i" field</summary>
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void ClearI() {
+          _hasBits0 &= ~1;
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override bool Equals(object other) {
+          return Equals(other as MessageSetCorrectExtension2);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public bool Equals(MessageSetCorrectExtension2 other) {
+          if (ReferenceEquals(other, null)) {
+            return false;
+          }
+          if (ReferenceEquals(other, this)) {
+            return true;
+          }
+          if (I != other.I) return false;
+          return Equals(_unknownFields, other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override int GetHashCode() {
+          int hash = 1;
+          if (HasI) hash ^= I.GetHashCode();
+          if (_unknownFields != null) {
+            hash ^= _unknownFields.GetHashCode();
+          }
+          return hash;
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public override string ToString() {
+          return pb::JsonFormatter.ToDiagnosticString(this);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void WriteTo(pb::CodedOutputStream output) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          output.WriteRawMessage(this);
+        #else
+          if (HasI) {
+            output.WriteRawTag(72);
+            output.WriteInt32(I);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(output);
+          }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
+          if (HasI) {
+            output.WriteRawTag(72);
+            output.WriteInt32(I);
+          }
+          if (_unknownFields != null) {
+            _unknownFields.WriteTo(ref output);
+          }
+        }
+        #endif
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public int CalculateSize() {
+          int size = 0;
+          if (HasI) {
+            size += 1 + pb::CodedOutputStream.ComputeInt32Size(I);
+          }
+          if (_unknownFields != null) {
+            size += _unknownFields.CalculateSize();
+          }
+          return size;
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void MergeFrom(MessageSetCorrectExtension2 other) {
+          if (other == null) {
+            return;
+          }
+          if (other.HasI) {
+            I = other.I;
+          }
+          _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
+        }
+
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public void MergeFrom(pb::CodedInputStream input) {
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+          input.ReadRawMessage(this);
+        #else
+          uint tag;
+          while ((tag = input.ReadTag()) != 0) {
+            switch(tag) {
+              default:
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
+                break;
+              case 72: {
+                I = input.ReadInt32();
+                break;
+              }
+            }
+          }
+        #endif
+        }
+
+        #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
+          uint tag;
+          while ((tag = input.ReadTag()) != 0) {
+            switch(tag) {
+              default:
+                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
+                break;
+              case 72: {
+                I = input.ReadInt32();
+                break;
+              }
+            }
+          }
+        }
+        #endif
+
+        #region Extensions
+        /// <summary>Container for extensions for other messages declared in the MessageSetCorrectExtension2 message type.</summary>
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+        [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+        public static partial class Extensions {
+          public static readonly pb::Extension<global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrect, global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrectExtension2> MessageSetExtension =
+            new pb::Extension<global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrect, global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrectExtension2>(4135312, pb::FieldCodec.ForMessage(33082498, global::ProtobufTestMessages.Proto2.TestAllRequiredTypesProto2.Types.MessageSetCorrectExtension2.Parser));
+        }
+        #endregion
+
+      }
+
+    }
+    #endregion
+
+  }
+
   #endregion
 
 }
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.pb.cs b/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.pb.cs
index 74e2a57..47bb7d0 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.pb.cs
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.pb.cs
@@ -1526,12 +1526,26 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public uint OneofUint32 {
-      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofUint32 ? (uint) oneofField_ : 0; }
+      get { return HasOneofUint32 ? (uint) oneofField_ : 0; }
       set {
         oneofField_ = value;
         oneofFieldCase_ = OneofFieldOneofCase.OneofUint32;
       }
     }
+    /// <summary>Gets whether the "oneof_uint32" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasOneofUint32 {
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofUint32; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "oneof_uint32" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearOneofUint32() {
+      if (HasOneofUint32) {
+        ClearOneofField();
+      }
+    }
 
     /// <summary>Field number for the "oneof_nested_message" field.</summary>
     public const int OneofNestedMessageFieldNumber = 112;
@@ -1550,96 +1564,208 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string OneofString {
-      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofString ? (string) oneofField_ : ""; }
+      get { return HasOneofString ? (string) oneofField_ : ""; }
       set {
         oneofField_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         oneofFieldCase_ = OneofFieldOneofCase.OneofString;
       }
     }
+    /// <summary>Gets whether the "oneof_string" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasOneofString {
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofString; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "oneof_string" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearOneofString() {
+      if (HasOneofString) {
+        ClearOneofField();
+      }
+    }
 
     /// <summary>Field number for the "oneof_bytes" field.</summary>
     public const int OneofBytesFieldNumber = 114;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public pb::ByteString OneofBytes {
-      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofBytes ? (pb::ByteString) oneofField_ : pb::ByteString.Empty; }
+      get { return HasOneofBytes ? (pb::ByteString) oneofField_ : pb::ByteString.Empty; }
       set {
         oneofField_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         oneofFieldCase_ = OneofFieldOneofCase.OneofBytes;
       }
     }
+    /// <summary>Gets whether the "oneof_bytes" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasOneofBytes {
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofBytes; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "oneof_bytes" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearOneofBytes() {
+      if (HasOneofBytes) {
+        ClearOneofField();
+      }
+    }
 
     /// <summary>Field number for the "oneof_bool" field.</summary>
     public const int OneofBoolFieldNumber = 115;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public bool OneofBool {
-      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofBool ? (bool) oneofField_ : false; }
+      get { return HasOneofBool ? (bool) oneofField_ : false; }
       set {
         oneofField_ = value;
         oneofFieldCase_ = OneofFieldOneofCase.OneofBool;
       }
     }
+    /// <summary>Gets whether the "oneof_bool" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasOneofBool {
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofBool; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "oneof_bool" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearOneofBool() {
+      if (HasOneofBool) {
+        ClearOneofField();
+      }
+    }
 
     /// <summary>Field number for the "oneof_uint64" field.</summary>
     public const int OneofUint64FieldNumber = 116;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public ulong OneofUint64 {
-      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofUint64 ? (ulong) oneofField_ : 0UL; }
+      get { return HasOneofUint64 ? (ulong) oneofField_ : 0UL; }
       set {
         oneofField_ = value;
         oneofFieldCase_ = OneofFieldOneofCase.OneofUint64;
       }
     }
+    /// <summary>Gets whether the "oneof_uint64" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasOneofUint64 {
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofUint64; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "oneof_uint64" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearOneofUint64() {
+      if (HasOneofUint64) {
+        ClearOneofField();
+      }
+    }
 
     /// <summary>Field number for the "oneof_float" field.</summary>
     public const int OneofFloatFieldNumber = 117;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public float OneofFloat {
-      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofFloat ? (float) oneofField_ : 0F; }
+      get { return HasOneofFloat ? (float) oneofField_ : 0F; }
       set {
         oneofField_ = value;
         oneofFieldCase_ = OneofFieldOneofCase.OneofFloat;
       }
     }
+    /// <summary>Gets whether the "oneof_float" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasOneofFloat {
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofFloat; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "oneof_float" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearOneofFloat() {
+      if (HasOneofFloat) {
+        ClearOneofField();
+      }
+    }
 
     /// <summary>Field number for the "oneof_double" field.</summary>
     public const int OneofDoubleFieldNumber = 118;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public double OneofDouble {
-      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofDouble ? (double) oneofField_ : 0D; }
+      get { return HasOneofDouble ? (double) oneofField_ : 0D; }
       set {
         oneofField_ = value;
         oneofFieldCase_ = OneofFieldOneofCase.OneofDouble;
       }
     }
+    /// <summary>Gets whether the "oneof_double" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasOneofDouble {
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofDouble; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "oneof_double" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearOneofDouble() {
+      if (HasOneofDouble) {
+        ClearOneofField();
+      }
+    }
 
     /// <summary>Field number for the "oneof_enum" field.</summary>
     public const int OneofEnumFieldNumber = 119;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum OneofEnum {
-      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofEnum ? (global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum) oneofField_ : global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum.Foo; }
+      get { return HasOneofEnum ? (global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum) oneofField_ : global::ProtobufTestMessages.Proto3.TestAllTypesProto3.Types.NestedEnum.Foo; }
       set {
         oneofField_ = value;
         oneofFieldCase_ = OneofFieldOneofCase.OneofEnum;
       }
     }
+    /// <summary>Gets whether the "oneof_enum" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasOneofEnum {
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofEnum; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "oneof_enum" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearOneofEnum() {
+      if (HasOneofEnum) {
+        ClearOneofField();
+      }
+    }
 
     /// <summary>Field number for the "oneof_null_value" field.</summary>
     public const int OneofNullValueFieldNumber = 120;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public global::Google.Protobuf.WellKnownTypes.NullValue OneofNullValue {
-      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofNullValue ? (global::Google.Protobuf.WellKnownTypes.NullValue) oneofField_ : global::Google.Protobuf.WellKnownTypes.NullValue.NullValue; }
+      get { return HasOneofNullValue ? (global::Google.Protobuf.WellKnownTypes.NullValue) oneofField_ : global::Google.Protobuf.WellKnownTypes.NullValue.NullValue; }
       set {
         oneofField_ = value;
         oneofFieldCase_ = OneofFieldOneofCase.OneofNullValue;
       }
     }
+    /// <summary>Gets whether the "oneof_null_value" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasOneofNullValue {
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofNullValue; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "oneof_null_value" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearOneofNullValue() {
+      if (HasOneofNullValue) {
+        ClearOneofField();
+      }
+    }
 
     /// <summary>Field number for the "optional_bool_wrapper" field.</summary>
     public const int OptionalBoolWrapperFieldNumber = 201;
@@ -2544,16 +2670,16 @@
       hash ^= MapStringForeignMessage.GetHashCode();
       hash ^= MapStringNestedEnum.GetHashCode();
       hash ^= MapStringForeignEnum.GetHashCode();
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint32) hash ^= OneofUint32.GetHashCode();
+      if (HasOneofUint32) hash ^= OneofUint32.GetHashCode();
       if (oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage) hash ^= OneofNestedMessage.GetHashCode();
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofString) hash ^= OneofString.GetHashCode();
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofBytes) hash ^= OneofBytes.GetHashCode();
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofBool) hash ^= OneofBool.GetHashCode();
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint64) hash ^= OneofUint64.GetHashCode();
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofFloat) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode(OneofFloat);
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofDouble) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode(OneofDouble);
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofEnum) hash ^= OneofEnum.GetHashCode();
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofNullValue) hash ^= OneofNullValue.GetHashCode();
+      if (HasOneofString) hash ^= OneofString.GetHashCode();
+      if (HasOneofBytes) hash ^= OneofBytes.GetHashCode();
+      if (HasOneofBool) hash ^= OneofBool.GetHashCode();
+      if (HasOneofUint64) hash ^= OneofUint64.GetHashCode();
+      if (HasOneofFloat) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode(OneofFloat);
+      if (HasOneofDouble) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode(OneofDouble);
+      if (HasOneofEnum) hash ^= OneofEnum.GetHashCode();
+      if (HasOneofNullValue) hash ^= OneofNullValue.GetHashCode();
       if (optionalBoolWrapper_ != null) hash ^= OptionalBoolWrapper.GetHashCode();
       if (optionalInt32Wrapper_ != null) hash ^= OptionalInt32Wrapper.GetHashCode();
       if (optionalInt64Wrapper_ != null) hash ^= OptionalInt64Wrapper.GetHashCode();
@@ -2783,7 +2909,7 @@
       unpackedDouble_.WriteTo(output, _repeated_unpackedDouble_codec);
       unpackedBool_.WriteTo(output, _repeated_unpackedBool_codec);
       unpackedNestedEnum_.WriteTo(output, _repeated_unpackedNestedEnum_codec);
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint32) {
+      if (HasOneofUint32) {
         output.WriteRawTag(248, 6);
         output.WriteUInt32(OneofUint32);
       }
@@ -2791,35 +2917,35 @@
         output.WriteRawTag(130, 7);
         output.WriteMessage(OneofNestedMessage);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofString) {
+      if (HasOneofString) {
         output.WriteRawTag(138, 7);
         output.WriteString(OneofString);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofBytes) {
+      if (HasOneofBytes) {
         output.WriteRawTag(146, 7);
         output.WriteBytes(OneofBytes);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofBool) {
+      if (HasOneofBool) {
         output.WriteRawTag(152, 7);
         output.WriteBool(OneofBool);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint64) {
+      if (HasOneofUint64) {
         output.WriteRawTag(160, 7);
         output.WriteUInt64(OneofUint64);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofFloat) {
+      if (HasOneofFloat) {
         output.WriteRawTag(173, 7);
         output.WriteFloat(OneofFloat);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofDouble) {
+      if (HasOneofDouble) {
         output.WriteRawTag(177, 7);
         output.WriteDouble(OneofDouble);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofEnum) {
+      if (HasOneofEnum) {
         output.WriteRawTag(184, 7);
         output.WriteEnum((int) OneofEnum);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofNullValue) {
+      if (HasOneofNullValue) {
         output.WriteRawTag(192, 7);
         output.WriteEnum((int) OneofNullValue);
       }
@@ -3136,7 +3262,7 @@
       unpackedDouble_.WriteTo(ref output, _repeated_unpackedDouble_codec);
       unpackedBool_.WriteTo(ref output, _repeated_unpackedBool_codec);
       unpackedNestedEnum_.WriteTo(ref output, _repeated_unpackedNestedEnum_codec);
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint32) {
+      if (HasOneofUint32) {
         output.WriteRawTag(248, 6);
         output.WriteUInt32(OneofUint32);
       }
@@ -3144,35 +3270,35 @@
         output.WriteRawTag(130, 7);
         output.WriteMessage(OneofNestedMessage);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofString) {
+      if (HasOneofString) {
         output.WriteRawTag(138, 7);
         output.WriteString(OneofString);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofBytes) {
+      if (HasOneofBytes) {
         output.WriteRawTag(146, 7);
         output.WriteBytes(OneofBytes);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofBool) {
+      if (HasOneofBool) {
         output.WriteRawTag(152, 7);
         output.WriteBool(OneofBool);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint64) {
+      if (HasOneofUint64) {
         output.WriteRawTag(160, 7);
         output.WriteUInt64(OneofUint64);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofFloat) {
+      if (HasOneofFloat) {
         output.WriteRawTag(173, 7);
         output.WriteFloat(OneofFloat);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofDouble) {
+      if (HasOneofDouble) {
         output.WriteRawTag(177, 7);
         output.WriteDouble(OneofDouble);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofEnum) {
+      if (HasOneofEnum) {
         output.WriteRawTag(184, 7);
         output.WriteEnum((int) OneofEnum);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofNullValue) {
+      if (HasOneofNullValue) {
         output.WriteRawTag(192, 7);
         output.WriteEnum((int) OneofNullValue);
       }
@@ -3466,34 +3592,34 @@
       size += mapStringForeignMessage_.CalculateSize(_map_mapStringForeignMessage_codec);
       size += mapStringNestedEnum_.CalculateSize(_map_mapStringNestedEnum_codec);
       size += mapStringForeignEnum_.CalculateSize(_map_mapStringForeignEnum_codec);
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint32) {
+      if (HasOneofUint32) {
         size += 2 + pb::CodedOutputStream.ComputeUInt32Size(OneofUint32);
       }
       if (oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage) {
         size += 2 + pb::CodedOutputStream.ComputeMessageSize(OneofNestedMessage);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofString) {
+      if (HasOneofString) {
         size += 2 + pb::CodedOutputStream.ComputeStringSize(OneofString);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofBytes) {
+      if (HasOneofBytes) {
         size += 2 + pb::CodedOutputStream.ComputeBytesSize(OneofBytes);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofBool) {
+      if (HasOneofBool) {
         size += 2 + 1;
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint64) {
+      if (HasOneofUint64) {
         size += 2 + pb::CodedOutputStream.ComputeUInt64Size(OneofUint64);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofFloat) {
+      if (HasOneofFloat) {
         size += 2 + 4;
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofDouble) {
+      if (HasOneofDouble) {
         size += 2 + 8;
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofEnum) {
+      if (HasOneofEnum) {
         size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) OneofEnum);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofNullValue) {
+      if (HasOneofNullValue) {
         size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) OneofNullValue);
       }
       if (optionalBoolWrapper_ != null) {
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/UnittestCustomOptionsProto3.pb.cs b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestCustomOptionsProto3.pb.cs
index 42cac04..8b94027 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/UnittestCustomOptionsProto3.pb.cs
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestCustomOptionsProto3.pb.cs
@@ -324,12 +324,26 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public int OneofField {
-      get { return anOneofCase_ == AnOneofOneofCase.OneofField ? (int) anOneof_ : 0; }
+      get { return HasOneofField ? (int) anOneof_ : 0; }
       set {
         anOneof_ = value;
         anOneofCase_ = AnOneofOneofCase.OneofField;
       }
     }
+    /// <summary>Gets whether the "oneof_field" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasOneofField {
+      get { return anOneofCase_ == AnOneofOneofCase.OneofField; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "oneof_field" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearOneofField() {
+      if (HasOneofField) {
+        ClearAnOneof();
+      }
+    }
 
     private object anOneof_;
     /// <summary>Enum of possible cases for the "AnOneof" oneof.</summary>
@@ -377,7 +391,7 @@
     public override int GetHashCode() {
       int hash = 1;
       if (Field1.Length != 0) hash ^= Field1.GetHashCode();
-      if (anOneofCase_ == AnOneofOneofCase.OneofField) hash ^= OneofField.GetHashCode();
+      if (HasOneofField) hash ^= OneofField.GetHashCode();
       hash ^= (int) anOneofCase_;
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
@@ -401,7 +415,7 @@
         output.WriteRawTag(10);
         output.WriteString(Field1);
       }
-      if (anOneofCase_ == AnOneofOneofCase.OneofField) {
+      if (HasOneofField) {
         output.WriteRawTag(16);
         output.WriteInt32(OneofField);
       }
@@ -419,7 +433,7 @@
         output.WriteRawTag(10);
         output.WriteString(Field1);
       }
-      if (anOneofCase_ == AnOneofOneofCase.OneofField) {
+      if (HasOneofField) {
         output.WriteRawTag(16);
         output.WriteInt32(OneofField);
       }
@@ -436,7 +450,7 @@
       if (Field1.Length != 0) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(Field1);
       }
-      if (anOneofCase_ == AnOneofOneofCase.OneofField) {
+      if (HasOneofField) {
         size += 1 + pb::CodedOutputStream.ComputeInt32Size(OneofField);
       }
       if (_unknownFields != null) {
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.pb.cs b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.pb.cs
index b5a517a..82e935e 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.pb.cs
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.pb.cs
@@ -2015,24 +2015,52 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string O1String {
-      get { return o1Case_ == O1OneofCase.O1String ? (string) o1_ : ""; }
+      get { return HasO1String ? (string) o1_ : ""; }
       set {
         o1_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         o1Case_ = O1OneofCase.O1String;
       }
     }
+    /// <summary>Gets whether the "o1_string" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasO1String {
+      get { return o1Case_ == O1OneofCase.O1String; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "o1_string" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearO1String() {
+      if (HasO1String) {
+        ClearO1();
+      }
+    }
 
     /// <summary>Field number for the "o1_int32" field.</summary>
     public const int O1Int32FieldNumber = 5;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public int O1Int32 {
-      get { return o1Case_ == O1OneofCase.O1Int32 ? (int) o1_ : 0; }
+      get { return HasO1Int32 ? (int) o1_ : 0; }
       set {
         o1_ = value;
         o1Case_ = O1OneofCase.O1Int32;
       }
     }
+    /// <summary>Gets whether the "o1_int32" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasO1Int32 {
+      get { return o1Case_ == O1OneofCase.O1Int32; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "o1_int32" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearO1Int32() {
+      if (HasO1Int32) {
+        ClearO1();
+      }
+    }
 
     /// <summary>Field number for the "plain_string" field.</summary>
     public const int PlainStringFieldNumber = 1;
@@ -2051,24 +2079,52 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public int O2Int32 {
-      get { return o2Case_ == O2OneofCase.O2Int32 ? (int) o2_ : 0; }
+      get { return HasO2Int32 ? (int) o2_ : 0; }
       set {
         o2_ = value;
         o2Case_ = O2OneofCase.O2Int32;
       }
     }
+    /// <summary>Gets whether the "o2_int32" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasO2Int32 {
+      get { return o2Case_ == O2OneofCase.O2Int32; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "o2_int32" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearO2Int32() {
+      if (HasO2Int32) {
+        ClearO2();
+      }
+    }
 
     /// <summary>Field number for the "o2_string" field.</summary>
     public const int O2StringFieldNumber = 3;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string O2String {
-      get { return o2Case_ == O2OneofCase.O2String ? (string) o2_ : ""; }
+      get { return HasO2String ? (string) o2_ : ""; }
       set {
         o2_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         o2Case_ = O2OneofCase.O2String;
       }
     }
+    /// <summary>Gets whether the "o2_string" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasO2String {
+      get { return o2Case_ == O2OneofCase.O2String; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "o2_string" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearO2String() {
+      if (HasO2String) {
+        ClearO2();
+      }
+    }
 
     private object o1_;
     /// <summary>Enum of possible cases for the "o1" oneof.</summary>
@@ -2143,11 +2199,11 @@
     public override int GetHashCode() {
       int hash = 1;
       if (PlainInt32 != 0) hash ^= PlainInt32.GetHashCode();
-      if (o1Case_ == O1OneofCase.O1String) hash ^= O1String.GetHashCode();
-      if (o1Case_ == O1OneofCase.O1Int32) hash ^= O1Int32.GetHashCode();
+      if (HasO1String) hash ^= O1String.GetHashCode();
+      if (HasO1Int32) hash ^= O1Int32.GetHashCode();
       if (PlainString.Length != 0) hash ^= PlainString.GetHashCode();
-      if (o2Case_ == O2OneofCase.O2Int32) hash ^= O2Int32.GetHashCode();
-      if (o2Case_ == O2OneofCase.O2String) hash ^= O2String.GetHashCode();
+      if (HasO2Int32) hash ^= O2Int32.GetHashCode();
+      if (HasO2String) hash ^= O2String.GetHashCode();
       hash ^= (int) o1Case_;
       hash ^= (int) o2Case_;
       if (_unknownFields != null) {
@@ -2172,11 +2228,11 @@
         output.WriteRawTag(10);
         output.WriteString(PlainString);
       }
-      if (o1Case_ == O1OneofCase.O1String) {
+      if (HasO1String) {
         output.WriteRawTag(18);
         output.WriteString(O1String);
       }
-      if (o2Case_ == O2OneofCase.O2String) {
+      if (HasO2String) {
         output.WriteRawTag(26);
         output.WriteString(O2String);
       }
@@ -2184,11 +2240,11 @@
         output.WriteRawTag(32);
         output.WriteInt32(PlainInt32);
       }
-      if (o1Case_ == O1OneofCase.O1Int32) {
+      if (HasO1Int32) {
         output.WriteRawTag(40);
         output.WriteInt32(O1Int32);
       }
-      if (o2Case_ == O2OneofCase.O2Int32) {
+      if (HasO2Int32) {
         output.WriteRawTag(48);
         output.WriteInt32(O2Int32);
       }
@@ -2206,11 +2262,11 @@
         output.WriteRawTag(10);
         output.WriteString(PlainString);
       }
-      if (o1Case_ == O1OneofCase.O1String) {
+      if (HasO1String) {
         output.WriteRawTag(18);
         output.WriteString(O1String);
       }
-      if (o2Case_ == O2OneofCase.O2String) {
+      if (HasO2String) {
         output.WriteRawTag(26);
         output.WriteString(O2String);
       }
@@ -2218,11 +2274,11 @@
         output.WriteRawTag(32);
         output.WriteInt32(PlainInt32);
       }
-      if (o1Case_ == O1OneofCase.O1Int32) {
+      if (HasO1Int32) {
         output.WriteRawTag(40);
         output.WriteInt32(O1Int32);
       }
-      if (o2Case_ == O2OneofCase.O2Int32) {
+      if (HasO2Int32) {
         output.WriteRawTag(48);
         output.WriteInt32(O2Int32);
       }
@@ -2239,19 +2295,19 @@
       if (PlainInt32 != 0) {
         size += 1 + pb::CodedOutputStream.ComputeInt32Size(PlainInt32);
       }
-      if (o1Case_ == O1OneofCase.O1String) {
+      if (HasO1String) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(O1String);
       }
-      if (o1Case_ == O1OneofCase.O1Int32) {
+      if (HasO1Int32) {
         size += 1 + pb::CodedOutputStream.ComputeInt32Size(O1Int32);
       }
       if (PlainString.Length != 0) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(PlainString);
       }
-      if (o2Case_ == O2OneofCase.O2Int32) {
+      if (HasO2Int32) {
         size += 1 + pb::CodedOutputStream.ComputeInt32Size(O2Int32);
       }
-      if (o2Case_ == O2OneofCase.O2String) {
+      if (HasO2String) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(O2String);
       }
       if (_unknownFields != null) {
@@ -2703,12 +2759,26 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string Text {
-      get { return valueCase_ == ValueOneofCase.Text ? (string) value_ : ""; }
+      get { return HasText ? (string) value_ : ""; }
       set {
         value_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         valueCase_ = ValueOneofCase.Text;
       }
     }
+    /// <summary>Gets whether the "text" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasText {
+      get { return valueCase_ == ValueOneofCase.Text; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "text" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearText() {
+      if (HasText) {
+        ClearValue();
+      }
+    }
 
     /// <summary>Field number for the "nested" field.</summary>
     public const int NestedFieldNumber = 2;
@@ -2768,7 +2838,7 @@
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public override int GetHashCode() {
       int hash = 1;
-      if (valueCase_ == ValueOneofCase.Text) hash ^= Text.GetHashCode();
+      if (HasText) hash ^= Text.GetHashCode();
       if (valueCase_ == ValueOneofCase.Nested) hash ^= Nested.GetHashCode();
       hash ^= (int) valueCase_;
       if (_unknownFields != null) {
@@ -2789,7 +2859,7 @@
     #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
       output.WriteRawMessage(this);
     #else
-      if (valueCase_ == ValueOneofCase.Text) {
+      if (HasText) {
         output.WriteRawTag(10);
         output.WriteString(Text);
       }
@@ -2807,7 +2877,7 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
-      if (valueCase_ == ValueOneofCase.Text) {
+      if (HasText) {
         output.WriteRawTag(10);
         output.WriteString(Text);
       }
@@ -2825,7 +2895,7 @@
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public int CalculateSize() {
       int size = 0;
-      if (valueCase_ == ValueOneofCase.Text) {
+      if (HasText) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(Text);
       }
       if (valueCase_ == ValueOneofCase.Nested) {
@@ -3209,24 +3279,52 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string StringValue {
-      get { return valueCase_ == ValueOneofCase.StringValue ? (string) value_ : ""; }
+      get { return HasStringValue ? (string) value_ : ""; }
       set {
         value_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         valueCase_ = ValueOneofCase.StringValue;
       }
     }
+    /// <summary>Gets whether the "string_value" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasStringValue {
+      get { return valueCase_ == ValueOneofCase.StringValue; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "string_value" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearStringValue() {
+      if (HasStringValue) {
+        ClearValue();
+      }
+    }
 
     /// <summary>Field number for the "null_value" field.</summary>
     public const int NullValueFieldNumber = 2;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public global::Google.Protobuf.WellKnownTypes.NullValue NullValue {
-      get { return valueCase_ == ValueOneofCase.NullValue ? (global::Google.Protobuf.WellKnownTypes.NullValue) value_ : global::Google.Protobuf.WellKnownTypes.NullValue.NullValue; }
+      get { return HasNullValue ? (global::Google.Protobuf.WellKnownTypes.NullValue) value_ : global::Google.Protobuf.WellKnownTypes.NullValue.NullValue; }
       set {
         value_ = value;
         valueCase_ = ValueOneofCase.NullValue;
       }
     }
+    /// <summary>Gets whether the "null_value" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasNullValue {
+      get { return valueCase_ == ValueOneofCase.NullValue; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "null_value" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearNullValue() {
+      if (HasNullValue) {
+        ClearValue();
+      }
+    }
 
     private object value_;
     /// <summary>Enum of possible cases for the "value" oneof.</summary>
@@ -3274,8 +3372,8 @@
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public override int GetHashCode() {
       int hash = 1;
-      if (valueCase_ == ValueOneofCase.StringValue) hash ^= StringValue.GetHashCode();
-      if (valueCase_ == ValueOneofCase.NullValue) hash ^= NullValue.GetHashCode();
+      if (HasStringValue) hash ^= StringValue.GetHashCode();
+      if (HasNullValue) hash ^= NullValue.GetHashCode();
       hash ^= (int) valueCase_;
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
@@ -3295,11 +3393,11 @@
     #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
       output.WriteRawMessage(this);
     #else
-      if (valueCase_ == ValueOneofCase.StringValue) {
+      if (HasStringValue) {
         output.WriteRawTag(10);
         output.WriteString(StringValue);
       }
-      if (valueCase_ == ValueOneofCase.NullValue) {
+      if (HasNullValue) {
         output.WriteRawTag(16);
         output.WriteEnum((int) NullValue);
       }
@@ -3313,11 +3411,11 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
-      if (valueCase_ == ValueOneofCase.StringValue) {
+      if (HasStringValue) {
         output.WriteRawTag(10);
         output.WriteString(StringValue);
       }
-      if (valueCase_ == ValueOneofCase.NullValue) {
+      if (HasNullValue) {
         output.WriteRawTag(16);
         output.WriteEnum((int) NullValue);
       }
@@ -3331,10 +3429,10 @@
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public int CalculateSize() {
       int size = 0;
-      if (valueCase_ == ValueOneofCase.StringValue) {
+      if (HasStringValue) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(StringValue);
       }
-      if (valueCase_ == ValueOneofCase.NullValue) {
+      if (HasNullValue) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) NullValue);
       }
       if (_unknownFields != null) {
@@ -3661,11 +3759,13 @@
 
     /// <summary>Field number for the "optional_field" field.</summary>
     public const int OptionalFieldFieldNumber = 2;
+    private readonly static string OptionalFieldDefaultValue = "";
+
     private string optionalField_;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string OptionalField {
-      get { return optionalField_ ?? ""; }
+      get { return optionalField_ ?? OptionalFieldDefaultValue; }
       set {
         optionalField_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
       }
@@ -3897,24 +3997,52 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string X {
-      get { return testCase_ == TestOneofCase.X ? (string) test_ : ""; }
+      get { return HasX ? (string) test_ : ""; }
       set {
         test_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         testCase_ = TestOneofCase.X;
       }
     }
+    /// <summary>Gets whether the "x" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasX {
+      get { return testCase_ == TestOneofCase.X; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "x" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearX() {
+      if (HasX) {
+        ClearTest();
+      }
+    }
 
     /// <summary>Field number for the "none" field.</summary>
     public const int NoneFieldNumber = 2;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string None {
-      get { return testCase_ == TestOneofCase.None_ ? (string) test_ : ""; }
+      get { return HasNone ? (string) test_ : ""; }
       set {
         test_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         testCase_ = TestOneofCase.None_;
       }
     }
+    /// <summary>Gets whether the "none" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasNone {
+      get { return testCase_ == TestOneofCase.None_; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "none" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearNone() {
+      if (HasNone) {
+        ClearTest();
+      }
+    }
 
     private object test_;
     /// <summary>Enum of possible cases for the "test" oneof.</summary>
@@ -3962,8 +4090,8 @@
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public override int GetHashCode() {
       int hash = 1;
-      if (testCase_ == TestOneofCase.X) hash ^= X.GetHashCode();
-      if (testCase_ == TestOneofCase.None_) hash ^= None.GetHashCode();
+      if (HasX) hash ^= X.GetHashCode();
+      if (HasNone) hash ^= None.GetHashCode();
       hash ^= (int) testCase_;
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
@@ -3983,11 +4111,11 @@
     #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
       output.WriteRawMessage(this);
     #else
-      if (testCase_ == TestOneofCase.X) {
+      if (HasX) {
         output.WriteRawTag(10);
         output.WriteString(X);
       }
-      if (testCase_ == TestOneofCase.None_) {
+      if (HasNone) {
         output.WriteRawTag(18);
         output.WriteString(None);
       }
@@ -4001,11 +4129,11 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
-      if (testCase_ == TestOneofCase.X) {
+      if (HasX) {
         output.WriteRawTag(10);
         output.WriteString(X);
       }
-      if (testCase_ == TestOneofCase.None_) {
+      if (HasNone) {
         output.WriteRawTag(18);
         output.WriteString(None);
       }
@@ -4019,10 +4147,10 @@
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public int CalculateSize() {
       int size = 0;
-      if (testCase_ == TestOneofCase.X) {
+      if (HasX) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(X);
       }
-      if (testCase_ == TestOneofCase.None_) {
+      if (HasNone) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(None);
       }
       if (_unknownFields != null) {
@@ -4156,24 +4284,52 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string X {
-      get { return noneCase_ == NoneOneofCase.X ? (string) none_ : ""; }
+      get { return HasX ? (string) none_ : ""; }
       set {
         none_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         noneCase_ = NoneOneofCase.X;
       }
     }
+    /// <summary>Gets whether the "x" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasX {
+      get { return noneCase_ == NoneOneofCase.X; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "x" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearX() {
+      if (HasX) {
+        ClearNone();
+      }
+    }
 
     /// <summary>Field number for the "y" field.</summary>
     public const int YFieldNumber = 2;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string Y {
-      get { return noneCase_ == NoneOneofCase.Y ? (string) none_ : ""; }
+      get { return HasY ? (string) none_ : ""; }
       set {
         none_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         noneCase_ = NoneOneofCase.Y;
       }
     }
+    /// <summary>Gets whether the "y" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasY {
+      get { return noneCase_ == NoneOneofCase.Y; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "y" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearY() {
+      if (HasY) {
+        ClearNone();
+      }
+    }
 
     private object none_;
     /// <summary>Enum of possible cases for the "none" oneof.</summary>
@@ -4221,8 +4377,8 @@
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public override int GetHashCode() {
       int hash = 1;
-      if (noneCase_ == NoneOneofCase.X) hash ^= X.GetHashCode();
-      if (noneCase_ == NoneOneofCase.Y) hash ^= Y.GetHashCode();
+      if (HasX) hash ^= X.GetHashCode();
+      if (HasY) hash ^= Y.GetHashCode();
       hash ^= (int) noneCase_;
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
@@ -4242,11 +4398,11 @@
     #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
       output.WriteRawMessage(this);
     #else
-      if (noneCase_ == NoneOneofCase.X) {
+      if (HasX) {
         output.WriteRawTag(10);
         output.WriteString(X);
       }
-      if (noneCase_ == NoneOneofCase.Y) {
+      if (HasY) {
         output.WriteRawTag(18);
         output.WriteString(Y);
       }
@@ -4260,11 +4416,11 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
-      if (noneCase_ == NoneOneofCase.X) {
+      if (HasX) {
         output.WriteRawTag(10);
         output.WriteString(X);
       }
-      if (noneCase_ == NoneOneofCase.Y) {
+      if (HasY) {
         output.WriteRawTag(18);
         output.WriteString(Y);
       }
@@ -4278,10 +4434,10 @@
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public int CalculateSize() {
       int size = 0;
-      if (noneCase_ == NoneOneofCase.X) {
+      if (HasX) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(X);
       }
-      if (noneCase_ == NoneOneofCase.Y) {
+      if (HasY) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(Y);
       }
       if (_unknownFields != null) {
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3.pb.cs b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3.pb.cs
index 7236b1c..209d37b 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3.pb.cs
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3.pb.cs
@@ -880,12 +880,26 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public uint OneofUint32 {
-      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofUint32 ? (uint) oneofField_ : 0; }
+      get { return HasOneofUint32 ? (uint) oneofField_ : 0; }
       set {
         oneofField_ = value;
         oneofFieldCase_ = OneofFieldOneofCase.OneofUint32;
       }
     }
+    /// <summary>Gets whether the "oneof_uint32" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasOneofUint32 {
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofUint32; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "oneof_uint32" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearOneofUint32() {
+      if (HasOneofUint32) {
+        ClearOneofField();
+      }
+    }
 
     /// <summary>Field number for the "oneof_nested_message" field.</summary>
     public const int OneofNestedMessageFieldNumber = 112;
@@ -904,24 +918,52 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string OneofString {
-      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofString ? (string) oneofField_ : ""; }
+      get { return HasOneofString ? (string) oneofField_ : ""; }
       set {
         oneofField_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         oneofFieldCase_ = OneofFieldOneofCase.OneofString;
       }
     }
+    /// <summary>Gets whether the "oneof_string" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasOneofString {
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofString; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "oneof_string" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearOneofString() {
+      if (HasOneofString) {
+        ClearOneofField();
+      }
+    }
 
     /// <summary>Field number for the "oneof_bytes" field.</summary>
     public const int OneofBytesFieldNumber = 114;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public pb::ByteString OneofBytes {
-      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofBytes ? (pb::ByteString) oneofField_ : pb::ByteString.Empty; }
+      get { return HasOneofBytes ? (pb::ByteString) oneofField_ : pb::ByteString.Empty; }
       set {
         oneofField_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         oneofFieldCase_ = OneofFieldOneofCase.OneofBytes;
       }
     }
+    /// <summary>Gets whether the "oneof_bytes" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasOneofBytes {
+      get { return oneofFieldCase_ == OneofFieldOneofCase.OneofBytes; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "oneof_bytes" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearOneofBytes() {
+      if (HasOneofBytes) {
+        ClearOneofField();
+      }
+    }
 
     private object oneofField_;
     /// <summary>Enum of possible cases for the "oneof_field" oneof.</summary>
@@ -1061,10 +1103,10 @@
       hash ^= repeatedForeignEnum_.GetHashCode();
       hash ^= repeatedImportEnum_.GetHashCode();
       hash ^= repeatedPublicImportMessage_.GetHashCode();
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint32) hash ^= OneofUint32.GetHashCode();
+      if (HasOneofUint32) hash ^= OneofUint32.GetHashCode();
       if (oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage) hash ^= OneofNestedMessage.GetHashCode();
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofString) hash ^= OneofString.GetHashCode();
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofBytes) hash ^= OneofBytes.GetHashCode();
+      if (HasOneofString) hash ^= OneofString.GetHashCode();
+      if (HasOneofBytes) hash ^= OneofBytes.GetHashCode();
       hash ^= (int) oneofFieldCase_;
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
@@ -1194,7 +1236,7 @@
       repeatedForeignEnum_.WriteTo(output, _repeated_repeatedForeignEnum_codec);
       repeatedImportEnum_.WriteTo(output, _repeated_repeatedImportEnum_codec);
       repeatedPublicImportMessage_.WriteTo(output, _repeated_repeatedPublicImportMessage_codec);
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint32) {
+      if (HasOneofUint32) {
         output.WriteRawTag(248, 6);
         output.WriteUInt32(OneofUint32);
       }
@@ -1202,11 +1244,11 @@
         output.WriteRawTag(130, 7);
         output.WriteMessage(OneofNestedMessage);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofString) {
+      if (HasOneofString) {
         output.WriteRawTag(138, 7);
         output.WriteString(OneofString);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofBytes) {
+      if (HasOneofBytes) {
         output.WriteRawTag(146, 7);
         output.WriteBytes(OneofBytes);
       }
@@ -1330,7 +1372,7 @@
       repeatedForeignEnum_.WriteTo(ref output, _repeated_repeatedForeignEnum_codec);
       repeatedImportEnum_.WriteTo(ref output, _repeated_repeatedImportEnum_codec);
       repeatedPublicImportMessage_.WriteTo(ref output, _repeated_repeatedPublicImportMessage_codec);
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint32) {
+      if (HasOneofUint32) {
         output.WriteRawTag(248, 6);
         output.WriteUInt32(OneofUint32);
       }
@@ -1338,11 +1380,11 @@
         output.WriteRawTag(130, 7);
         output.WriteMessage(OneofNestedMessage);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofString) {
+      if (HasOneofString) {
         output.WriteRawTag(138, 7);
         output.WriteString(OneofString);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofBytes) {
+      if (HasOneofBytes) {
         output.WriteRawTag(146, 7);
         output.WriteBytes(OneofBytes);
       }
@@ -1444,16 +1486,16 @@
       size += repeatedForeignEnum_.CalculateSize(_repeated_repeatedForeignEnum_codec);
       size += repeatedImportEnum_.CalculateSize(_repeated_repeatedImportEnum_codec);
       size += repeatedPublicImportMessage_.CalculateSize(_repeated_repeatedPublicImportMessage_codec);
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofUint32) {
+      if (HasOneofUint32) {
         size += 2 + pb::CodedOutputStream.ComputeUInt32Size(OneofUint32);
       }
       if (oneofFieldCase_ == OneofFieldOneofCase.OneofNestedMessage) {
         size += 2 + pb::CodedOutputStream.ComputeMessageSize(OneofNestedMessage);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofString) {
+      if (HasOneofString) {
         size += 2 + pb::CodedOutputStream.ComputeStringSize(OneofString);
       }
-      if (oneofFieldCase_ == OneofFieldOneofCase.OneofBytes) {
+      if (HasOneofBytes) {
         size += 2 + pb::CodedOutputStream.ComputeBytesSize(OneofBytes);
       }
       if (_unknownFields != null) {
@@ -7309,24 +7351,52 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public int FooInt {
-      get { return fooCase_ == FooOneofCase.FooInt ? (int) foo_ : 0; }
+      get { return HasFooInt ? (int) foo_ : 0; }
       set {
         foo_ = value;
         fooCase_ = FooOneofCase.FooInt;
       }
     }
+    /// <summary>Gets whether the "foo_int" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasFooInt {
+      get { return fooCase_ == FooOneofCase.FooInt; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "foo_int" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearFooInt() {
+      if (HasFooInt) {
+        ClearFoo();
+      }
+    }
 
     /// <summary>Field number for the "foo_string" field.</summary>
     public const int FooStringFieldNumber = 2;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string FooString {
-      get { return fooCase_ == FooOneofCase.FooString ? (string) foo_ : ""; }
+      get { return HasFooString ? (string) foo_ : ""; }
       set {
         foo_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         fooCase_ = FooOneofCase.FooString;
       }
     }
+    /// <summary>Gets whether the "foo_string" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasFooString {
+      get { return fooCase_ == FooOneofCase.FooString; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "foo_string" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearFooString() {
+      if (HasFooString) {
+        ClearFoo();
+      }
+    }
 
     /// <summary>Field number for the "foo_message" field.</summary>
     public const int FooMessageFieldNumber = 3;
@@ -7388,8 +7458,8 @@
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public override int GetHashCode() {
       int hash = 1;
-      if (fooCase_ == FooOneofCase.FooInt) hash ^= FooInt.GetHashCode();
-      if (fooCase_ == FooOneofCase.FooString) hash ^= FooString.GetHashCode();
+      if (HasFooInt) hash ^= FooInt.GetHashCode();
+      if (HasFooString) hash ^= FooString.GetHashCode();
       if (fooCase_ == FooOneofCase.FooMessage) hash ^= FooMessage.GetHashCode();
       hash ^= (int) fooCase_;
       if (_unknownFields != null) {
@@ -7410,11 +7480,11 @@
     #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
       output.WriteRawMessage(this);
     #else
-      if (fooCase_ == FooOneofCase.FooInt) {
+      if (HasFooInt) {
         output.WriteRawTag(8);
         output.WriteInt32(FooInt);
       }
-      if (fooCase_ == FooOneofCase.FooString) {
+      if (HasFooString) {
         output.WriteRawTag(18);
         output.WriteString(FooString);
       }
@@ -7432,11 +7502,11 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
-      if (fooCase_ == FooOneofCase.FooInt) {
+      if (HasFooInt) {
         output.WriteRawTag(8);
         output.WriteInt32(FooInt);
       }
-      if (fooCase_ == FooOneofCase.FooString) {
+      if (HasFooString) {
         output.WriteRawTag(18);
         output.WriteString(FooString);
       }
@@ -7454,10 +7524,10 @@
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public int CalculateSize() {
       int size = 0;
-      if (fooCase_ == FooOneofCase.FooInt) {
+      if (HasFooInt) {
         size += 1 + pb::CodedOutputStream.ComputeInt32Size(FooInt);
       }
-      if (fooCase_ == FooOneofCase.FooString) {
+      if (HasFooString) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(FooString);
       }
       if (fooCase_ == FooOneofCase.FooMessage) {
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3Optional.pb.cs b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3Optional.pb.cs
index b97d56f..ff9c203 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3Optional.pb.cs
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3Optional.pb.cs
@@ -148,6 +148,8 @@
 
     /// <summary>Field number for the "optional_int32" field.</summary>
     public const int OptionalInt32FieldNumber = 1;
+    private readonly static int OptionalInt32DefaultValue = 0;
+
     private int optionalInt32_;
     /// <summary>
     /// Singular
@@ -155,7 +157,7 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public int OptionalInt32 {
-      get { if ((_hasBits0 & 1) != 0) { return optionalInt32_; } else { return 0; } }
+      get { if ((_hasBits0 & 1) != 0) { return optionalInt32_; } else { return OptionalInt32DefaultValue; } }
       set {
         _hasBits0 |= 1;
         optionalInt32_ = value;
@@ -176,11 +178,13 @@
 
     /// <summary>Field number for the "optional_int64" field.</summary>
     public const int OptionalInt64FieldNumber = 2;
+    private readonly static long OptionalInt64DefaultValue = 0L;
+
     private long optionalInt64_;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public long OptionalInt64 {
-      get { if ((_hasBits0 & 2) != 0) { return optionalInt64_; } else { return 0L; } }
+      get { if ((_hasBits0 & 2) != 0) { return optionalInt64_; } else { return OptionalInt64DefaultValue; } }
       set {
         _hasBits0 |= 2;
         optionalInt64_ = value;
@@ -201,11 +205,13 @@
 
     /// <summary>Field number for the "optional_uint32" field.</summary>
     public const int OptionalUint32FieldNumber = 3;
+    private readonly static uint OptionalUint32DefaultValue = 0;
+
     private uint optionalUint32_;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public uint OptionalUint32 {
-      get { if ((_hasBits0 & 4) != 0) { return optionalUint32_; } else { return 0; } }
+      get { if ((_hasBits0 & 4) != 0) { return optionalUint32_; } else { return OptionalUint32DefaultValue; } }
       set {
         _hasBits0 |= 4;
         optionalUint32_ = value;
@@ -226,11 +232,13 @@
 
     /// <summary>Field number for the "optional_uint64" field.</summary>
     public const int OptionalUint64FieldNumber = 4;
+    private readonly static ulong OptionalUint64DefaultValue = 0UL;
+
     private ulong optionalUint64_;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public ulong OptionalUint64 {
-      get { if ((_hasBits0 & 8) != 0) { return optionalUint64_; } else { return 0UL; } }
+      get { if ((_hasBits0 & 8) != 0) { return optionalUint64_; } else { return OptionalUint64DefaultValue; } }
       set {
         _hasBits0 |= 8;
         optionalUint64_ = value;
@@ -251,11 +259,13 @@
 
     /// <summary>Field number for the "optional_sint32" field.</summary>
     public const int OptionalSint32FieldNumber = 5;
+    private readonly static int OptionalSint32DefaultValue = 0;
+
     private int optionalSint32_;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public int OptionalSint32 {
-      get { if ((_hasBits0 & 16) != 0) { return optionalSint32_; } else { return 0; } }
+      get { if ((_hasBits0 & 16) != 0) { return optionalSint32_; } else { return OptionalSint32DefaultValue; } }
       set {
         _hasBits0 |= 16;
         optionalSint32_ = value;
@@ -276,11 +286,13 @@
 
     /// <summary>Field number for the "optional_sint64" field.</summary>
     public const int OptionalSint64FieldNumber = 6;
+    private readonly static long OptionalSint64DefaultValue = 0L;
+
     private long optionalSint64_;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public long OptionalSint64 {
-      get { if ((_hasBits0 & 32) != 0) { return optionalSint64_; } else { return 0L; } }
+      get { if ((_hasBits0 & 32) != 0) { return optionalSint64_; } else { return OptionalSint64DefaultValue; } }
       set {
         _hasBits0 |= 32;
         optionalSint64_ = value;
@@ -301,11 +313,13 @@
 
     /// <summary>Field number for the "optional_fixed32" field.</summary>
     public const int OptionalFixed32FieldNumber = 7;
+    private readonly static uint OptionalFixed32DefaultValue = 0;
+
     private uint optionalFixed32_;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public uint OptionalFixed32 {
-      get { if ((_hasBits0 & 64) != 0) { return optionalFixed32_; } else { return 0; } }
+      get { if ((_hasBits0 & 64) != 0) { return optionalFixed32_; } else { return OptionalFixed32DefaultValue; } }
       set {
         _hasBits0 |= 64;
         optionalFixed32_ = value;
@@ -326,11 +340,13 @@
 
     /// <summary>Field number for the "optional_fixed64" field.</summary>
     public const int OptionalFixed64FieldNumber = 8;
+    private readonly static ulong OptionalFixed64DefaultValue = 0UL;
+
     private ulong optionalFixed64_;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public ulong OptionalFixed64 {
-      get { if ((_hasBits0 & 128) != 0) { return optionalFixed64_; } else { return 0UL; } }
+      get { if ((_hasBits0 & 128) != 0) { return optionalFixed64_; } else { return OptionalFixed64DefaultValue; } }
       set {
         _hasBits0 |= 128;
         optionalFixed64_ = value;
@@ -351,11 +367,13 @@
 
     /// <summary>Field number for the "optional_sfixed32" field.</summary>
     public const int OptionalSfixed32FieldNumber = 9;
+    private readonly static int OptionalSfixed32DefaultValue = 0;
+
     private int optionalSfixed32_;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public int OptionalSfixed32 {
-      get { if ((_hasBits0 & 256) != 0) { return optionalSfixed32_; } else { return 0; } }
+      get { if ((_hasBits0 & 256) != 0) { return optionalSfixed32_; } else { return OptionalSfixed32DefaultValue; } }
       set {
         _hasBits0 |= 256;
         optionalSfixed32_ = value;
@@ -376,11 +394,13 @@
 
     /// <summary>Field number for the "optional_sfixed64" field.</summary>
     public const int OptionalSfixed64FieldNumber = 10;
+    private readonly static long OptionalSfixed64DefaultValue = 0L;
+
     private long optionalSfixed64_;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public long OptionalSfixed64 {
-      get { if ((_hasBits0 & 512) != 0) { return optionalSfixed64_; } else { return 0L; } }
+      get { if ((_hasBits0 & 512) != 0) { return optionalSfixed64_; } else { return OptionalSfixed64DefaultValue; } }
       set {
         _hasBits0 |= 512;
         optionalSfixed64_ = value;
@@ -401,11 +421,13 @@
 
     /// <summary>Field number for the "optional_float" field.</summary>
     public const int OptionalFloatFieldNumber = 11;
+    private readonly static float OptionalFloatDefaultValue = 0F;
+
     private float optionalFloat_;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public float OptionalFloat {
-      get { if ((_hasBits0 & 1024) != 0) { return optionalFloat_; } else { return 0F; } }
+      get { if ((_hasBits0 & 1024) != 0) { return optionalFloat_; } else { return OptionalFloatDefaultValue; } }
       set {
         _hasBits0 |= 1024;
         optionalFloat_ = value;
@@ -426,11 +448,13 @@
 
     /// <summary>Field number for the "optional_double" field.</summary>
     public const int OptionalDoubleFieldNumber = 12;
+    private readonly static double OptionalDoubleDefaultValue = 0D;
+
     private double optionalDouble_;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public double OptionalDouble {
-      get { if ((_hasBits0 & 2048) != 0) { return optionalDouble_; } else { return 0D; } }
+      get { if ((_hasBits0 & 2048) != 0) { return optionalDouble_; } else { return OptionalDoubleDefaultValue; } }
       set {
         _hasBits0 |= 2048;
         optionalDouble_ = value;
@@ -451,11 +475,13 @@
 
     /// <summary>Field number for the "optional_bool" field.</summary>
     public const int OptionalBoolFieldNumber = 13;
+    private readonly static bool OptionalBoolDefaultValue = false;
+
     private bool optionalBool_;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public bool OptionalBool {
-      get { if ((_hasBits0 & 4096) != 0) { return optionalBool_; } else { return false; } }
+      get { if ((_hasBits0 & 4096) != 0) { return optionalBool_; } else { return OptionalBoolDefaultValue; } }
       set {
         _hasBits0 |= 4096;
         optionalBool_ = value;
@@ -476,11 +502,13 @@
 
     /// <summary>Field number for the "optional_string" field.</summary>
     public const int OptionalStringFieldNumber = 14;
+    private readonly static string OptionalStringDefaultValue = "";
+
     private string optionalString_;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string OptionalString {
-      get { return optionalString_ ?? ""; }
+      get { return optionalString_ ?? OptionalStringDefaultValue; }
       set {
         optionalString_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
       }
@@ -500,11 +528,13 @@
 
     /// <summary>Field number for the "optional_bytes" field.</summary>
     public const int OptionalBytesFieldNumber = 15;
+    private readonly static pb::ByteString OptionalBytesDefaultValue = pb::ByteString.Empty;
+
     private pb::ByteString optionalBytes_;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public pb::ByteString OptionalBytes {
-      get { return optionalBytes_ ?? pb::ByteString.Empty; }
+      get { return optionalBytes_ ?? OptionalBytesDefaultValue; }
       set {
         optionalBytes_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
       }
@@ -524,11 +554,13 @@
 
     /// <summary>Field number for the "optional_cord" field.</summary>
     public const int OptionalCordFieldNumber = 16;
+    private readonly static string OptionalCordDefaultValue = "";
+
     private string optionalCord_;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string OptionalCord {
-      get { return optionalCord_ ?? ""; }
+      get { return optionalCord_ ?? OptionalCordDefaultValue; }
       set {
         optionalCord_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
       }
@@ -572,11 +604,13 @@
 
     /// <summary>Field number for the "optional_nested_enum" field.</summary>
     public const int OptionalNestedEnumFieldNumber = 21;
+    private readonly static global::ProtobufUnittest.TestProto3Optional.Types.NestedEnum OptionalNestedEnumDefaultValue = global::ProtobufUnittest.TestProto3Optional.Types.NestedEnum.Unspecified;
+
     private global::ProtobufUnittest.TestProto3Optional.Types.NestedEnum optionalNestedEnum_;
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public global::ProtobufUnittest.TestProto3Optional.Types.NestedEnum OptionalNestedEnum {
-      get { if ((_hasBits0 & 8192) != 0) { return optionalNestedEnum_; } else { return global::ProtobufUnittest.TestProto3Optional.Types.NestedEnum.Unspecified; } }
+      get { if ((_hasBits0 & 8192) != 0) { return optionalNestedEnum_; } else { return OptionalNestedEnumDefaultValue; } }
       set {
         _hasBits0 |= 8192;
         optionalNestedEnum_ = value;
@@ -1315,6 +1349,8 @@
 
         /// <summary>Field number for the "bb" field.</summary>
         public const int BbFieldNumber = 1;
+        private readonly static int BbDefaultValue = 0;
+
         private int bb_;
         /// <summary>
         /// The field name "b" fails to compile in proto1 because it conflicts with
@@ -1324,7 +1360,7 @@
         [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
         [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
         public int Bb {
-          get { if ((_hasBits0 & 1) != 0) { return bb_; } else { return 0; } }
+          get { if ((_hasBits0 & 1) != 0) { return bb_; } else { return BbDefaultValue; } }
           set {
             _hasBits0 |= 1;
             bb_ = value;
diff --git a/csharp/src/Google.Protobuf.Test/testprotos.pb b/csharp/src/Google.Protobuf.Test/testprotos.pb
index 5e361f9..8fb22bc 100644
--- a/csharp/src/Google.Protobuf.Test/testprotos.pb
+++ b/csharp/src/Google.Protobuf.Test/testprotos.pb
Binary files differ
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Struct.pb.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Struct.pb.cs
index aa25686..38a49ac 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Struct.pb.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Struct.pb.cs
@@ -55,7 +55,7 @@
   /// `NullValue` is a singleton enumeration to represent the null value for the
   /// `Value` type union.
   ///
-  ///  The JSON representation for `NullValue` is JSON `null`.
+  /// The JSON representation for `NullValue` is JSON `null`.
   /// </summary>
   public enum NullValue {
     /// <summary>
@@ -338,12 +338,26 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public global::Google.Protobuf.WellKnownTypes.NullValue NullValue {
-      get { return kindCase_ == KindOneofCase.NullValue ? (global::Google.Protobuf.WellKnownTypes.NullValue) kind_ : global::Google.Protobuf.WellKnownTypes.NullValue.NullValue; }
+      get { return HasNullValue ? (global::Google.Protobuf.WellKnownTypes.NullValue) kind_ : global::Google.Protobuf.WellKnownTypes.NullValue.NullValue; }
       set {
         kind_ = value;
         kindCase_ = KindOneofCase.NullValue;
       }
     }
+    /// <summary>Gets whether the "null_value" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasNullValue {
+      get { return kindCase_ == KindOneofCase.NullValue; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "null_value" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearNullValue() {
+      if (HasNullValue) {
+        ClearKind();
+      }
+    }
 
     /// <summary>Field number for the "number_value" field.</summary>
     public const int NumberValueFieldNumber = 2;
@@ -353,12 +367,26 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public double NumberValue {
-      get { return kindCase_ == KindOneofCase.NumberValue ? (double) kind_ : 0D; }
+      get { return HasNumberValue ? (double) kind_ : 0D; }
       set {
         kind_ = value;
         kindCase_ = KindOneofCase.NumberValue;
       }
     }
+    /// <summary>Gets whether the "number_value" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasNumberValue {
+      get { return kindCase_ == KindOneofCase.NumberValue; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "number_value" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearNumberValue() {
+      if (HasNumberValue) {
+        ClearKind();
+      }
+    }
 
     /// <summary>Field number for the "string_value" field.</summary>
     public const int StringValueFieldNumber = 3;
@@ -368,12 +396,26 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public string StringValue {
-      get { return kindCase_ == KindOneofCase.StringValue ? (string) kind_ : ""; }
+      get { return HasStringValue ? (string) kind_ : ""; }
       set {
         kind_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
         kindCase_ = KindOneofCase.StringValue;
       }
     }
+    /// <summary>Gets whether the "string_value" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasStringValue {
+      get { return kindCase_ == KindOneofCase.StringValue; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "string_value" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearStringValue() {
+      if (HasStringValue) {
+        ClearKind();
+      }
+    }
 
     /// <summary>Field number for the "bool_value" field.</summary>
     public const int BoolValueFieldNumber = 4;
@@ -383,12 +425,26 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public bool BoolValue {
-      get { return kindCase_ == KindOneofCase.BoolValue ? (bool) kind_ : false; }
+      get { return HasBoolValue ? (bool) kind_ : false; }
       set {
         kind_ = value;
         kindCase_ = KindOneofCase.BoolValue;
       }
     }
+    /// <summary>Gets whether the "bool_value" field is set</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public bool HasBoolValue {
+      get { return kindCase_ == KindOneofCase.BoolValue; }
+    }
+    /// <summary> Clears the value of the oneof if it's currently set to "bool_value" </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public void ClearBoolValue() {
+      if (HasBoolValue) {
+        ClearKind();
+      }
+    }
 
     /// <summary>Field number for the "struct_value" field.</summary>
     public const int StructValueFieldNumber = 5;
@@ -474,10 +530,10 @@
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public override int GetHashCode() {
       int hash = 1;
-      if (kindCase_ == KindOneofCase.NullValue) hash ^= NullValue.GetHashCode();
-      if (kindCase_ == KindOneofCase.NumberValue) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode(NumberValue);
-      if (kindCase_ == KindOneofCase.StringValue) hash ^= StringValue.GetHashCode();
-      if (kindCase_ == KindOneofCase.BoolValue) hash ^= BoolValue.GetHashCode();
+      if (HasNullValue) hash ^= NullValue.GetHashCode();
+      if (HasNumberValue) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode(NumberValue);
+      if (HasStringValue) hash ^= StringValue.GetHashCode();
+      if (HasBoolValue) hash ^= BoolValue.GetHashCode();
       if (kindCase_ == KindOneofCase.StructValue) hash ^= StructValue.GetHashCode();
       if (kindCase_ == KindOneofCase.ListValue) hash ^= ListValue.GetHashCode();
       hash ^= (int) kindCase_;
@@ -499,19 +555,19 @@
     #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
       output.WriteRawMessage(this);
     #else
-      if (kindCase_ == KindOneofCase.NullValue) {
+      if (HasNullValue) {
         output.WriteRawTag(8);
         output.WriteEnum((int) NullValue);
       }
-      if (kindCase_ == KindOneofCase.NumberValue) {
+      if (HasNumberValue) {
         output.WriteRawTag(17);
         output.WriteDouble(NumberValue);
       }
-      if (kindCase_ == KindOneofCase.StringValue) {
+      if (HasStringValue) {
         output.WriteRawTag(26);
         output.WriteString(StringValue);
       }
-      if (kindCase_ == KindOneofCase.BoolValue) {
+      if (HasBoolValue) {
         output.WriteRawTag(32);
         output.WriteBool(BoolValue);
       }
@@ -533,19 +589,19 @@
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
-      if (kindCase_ == KindOneofCase.NullValue) {
+      if (HasNullValue) {
         output.WriteRawTag(8);
         output.WriteEnum((int) NullValue);
       }
-      if (kindCase_ == KindOneofCase.NumberValue) {
+      if (HasNumberValue) {
         output.WriteRawTag(17);
         output.WriteDouble(NumberValue);
       }
-      if (kindCase_ == KindOneofCase.StringValue) {
+      if (HasStringValue) {
         output.WriteRawTag(26);
         output.WriteString(StringValue);
       }
-      if (kindCase_ == KindOneofCase.BoolValue) {
+      if (HasBoolValue) {
         output.WriteRawTag(32);
         output.WriteBool(BoolValue);
       }
@@ -567,16 +623,16 @@
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public int CalculateSize() {
       int size = 0;
-      if (kindCase_ == KindOneofCase.NullValue) {
+      if (HasNullValue) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) NullValue);
       }
-      if (kindCase_ == KindOneofCase.NumberValue) {
+      if (HasNumberValue) {
         size += 1 + 8;
       }
-      if (kindCase_ == KindOneofCase.StringValue) {
+      if (HasStringValue) {
         size += 1 + pb::CodedOutputStream.ComputeStringSize(StringValue);
       }
-      if (kindCase_ == KindOneofCase.BoolValue) {
+      if (HasBoolValue) {
         size += 1 + 1;
       }
       if (kindCase_ == KindOneofCase.StructValue) {
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Type.pb.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Type.pb.cs
index 3088e38..7ad0a7a 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Type.pb.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Type.pb.cs
@@ -26,46 +26,47 @@
           string.Concat(
             "Chpnb29nbGUvcHJvdG9idWYvdHlwZS5wcm90bxIPZ29vZ2xlLnByb3RvYnVm",
             "Ghlnb29nbGUvcHJvdG9idWYvYW55LnByb3RvGiRnb29nbGUvcHJvdG9idWYv",
-            "c291cmNlX2NvbnRleHQucHJvdG8i1wEKBFR5cGUSDAoEbmFtZRgBIAEoCRIm",
+            "c291cmNlX2NvbnRleHQucHJvdG8i6AEKBFR5cGUSDAoEbmFtZRgBIAEoCRIm",
             "CgZmaWVsZHMYAiADKAsyFi5nb29nbGUucHJvdG9idWYuRmllbGQSDgoGb25l",
             "b2ZzGAMgAygJEigKB29wdGlvbnMYBCADKAsyFy5nb29nbGUucHJvdG9idWYu",
             "T3B0aW9uEjYKDnNvdXJjZV9jb250ZXh0GAUgASgLMh4uZ29vZ2xlLnByb3Rv",
             "YnVmLlNvdXJjZUNvbnRleHQSJwoGc3ludGF4GAYgASgOMhcuZ29vZ2xlLnBy",
-            "b3RvYnVmLlN5bnRheCLVBQoFRmllbGQSKQoEa2luZBgBIAEoDjIbLmdvb2ds",
-            "ZS5wcm90b2J1Zi5GaWVsZC5LaW5kEjcKC2NhcmRpbmFsaXR5GAIgASgOMiIu",
-            "Z29vZ2xlLnByb3RvYnVmLkZpZWxkLkNhcmRpbmFsaXR5Eg4KBm51bWJlchgD",
-            "IAEoBRIMCgRuYW1lGAQgASgJEhAKCHR5cGVfdXJsGAYgASgJEhMKC29uZW9m",
-            "X2luZGV4GAcgASgFEg4KBnBhY2tlZBgIIAEoCBIoCgdvcHRpb25zGAkgAygL",
-            "MhcuZ29vZ2xlLnByb3RvYnVmLk9wdGlvbhIRCglqc29uX25hbWUYCiABKAkS",
-            "FQoNZGVmYXVsdF92YWx1ZRgLIAEoCSLIAgoES2luZBIQCgxUWVBFX1VOS05P",
-            "V04QABIPCgtUWVBFX0RPVUJMRRABEg4KClRZUEVfRkxPQVQQAhIOCgpUWVBF",
-            "X0lOVDY0EAMSDwoLVFlQRV9VSU5UNjQQBBIOCgpUWVBFX0lOVDMyEAUSEAoM",
-            "VFlQRV9GSVhFRDY0EAYSEAoMVFlQRV9GSVhFRDMyEAcSDQoJVFlQRV9CT09M",
-            "EAgSDwoLVFlQRV9TVFJJTkcQCRIOCgpUWVBFX0dST1VQEAoSEAoMVFlQRV9N",
-            "RVNTQUdFEAsSDgoKVFlQRV9CWVRFUxAMEg8KC1RZUEVfVUlOVDMyEA0SDQoJ",
-            "VFlQRV9FTlVNEA4SEQoNVFlQRV9TRklYRUQzMhAPEhEKDVRZUEVfU0ZJWEVE",
-            "NjQQEBIPCgtUWVBFX1NJTlQzMhAREg8KC1RZUEVfU0lOVDY0EBIidAoLQ2Fy",
-            "ZGluYWxpdHkSFwoTQ0FSRElOQUxJVFlfVU5LTk9XThAAEhgKFENBUkRJTkFM",
-            "SVRZX09QVElPTkFMEAESGAoUQ0FSRElOQUxJVFlfUkVRVUlSRUQQAhIYChRD",
-            "QVJESU5BTElUWV9SRVBFQVRFRBADIs4BCgRFbnVtEgwKBG5hbWUYASABKAkS",
-            "LQoJZW51bXZhbHVlGAIgAygLMhouZ29vZ2xlLnByb3RvYnVmLkVudW1WYWx1",
-            "ZRIoCgdvcHRpb25zGAMgAygLMhcuZ29vZ2xlLnByb3RvYnVmLk9wdGlvbhI2",
-            "Cg5zb3VyY2VfY29udGV4dBgEIAEoCzIeLmdvb2dsZS5wcm90b2J1Zi5Tb3Vy",
-            "Y2VDb250ZXh0EicKBnN5bnRheBgFIAEoDjIXLmdvb2dsZS5wcm90b2J1Zi5T",
-            "eW50YXgiUwoJRW51bVZhbHVlEgwKBG5hbWUYASABKAkSDgoGbnVtYmVyGAIg",
-            "ASgFEigKB29wdGlvbnMYAyADKAsyFy5nb29nbGUucHJvdG9idWYuT3B0aW9u",
-            "IjsKBk9wdGlvbhIMCgRuYW1lGAEgASgJEiMKBXZhbHVlGAIgASgLMhQuZ29v",
-            "Z2xlLnByb3RvYnVmLkFueSouCgZTeW50YXgSEQoNU1lOVEFYX1BST1RPMhAA",
-            "EhEKDVNZTlRBWF9QUk9UTzMQAUJ7ChNjb20uZ29vZ2xlLnByb3RvYnVmQglU",
-            "eXBlUHJvdG9QAVotZ29vZ2xlLmdvbGFuZy5vcmcvcHJvdG9idWYvdHlwZXMv",
-            "a25vd24vdHlwZXBi+AEBogIDR1BCqgIeR29vZ2xlLlByb3RvYnVmLldlbGxL",
-            "bm93blR5cGVzYgZwcm90bzM="));
+            "b3RvYnVmLlN5bnRheBIPCgdlZGl0aW9uGAcgASgJItUFCgVGaWVsZBIpCgRr",
+            "aW5kGAEgASgOMhsuZ29vZ2xlLnByb3RvYnVmLkZpZWxkLktpbmQSNwoLY2Fy",
+            "ZGluYWxpdHkYAiABKA4yIi5nb29nbGUucHJvdG9idWYuRmllbGQuQ2FyZGlu",
+            "YWxpdHkSDgoGbnVtYmVyGAMgASgFEgwKBG5hbWUYBCABKAkSEAoIdHlwZV91",
+            "cmwYBiABKAkSEwoLb25lb2ZfaW5kZXgYByABKAUSDgoGcGFja2VkGAggASgI",
+            "EigKB29wdGlvbnMYCSADKAsyFy5nb29nbGUucHJvdG9idWYuT3B0aW9uEhEK",
+            "CWpzb25fbmFtZRgKIAEoCRIVCg1kZWZhdWx0X3ZhbHVlGAsgASgJIsgCCgRL",
+            "aW5kEhAKDFRZUEVfVU5LTk9XThAAEg8KC1RZUEVfRE9VQkxFEAESDgoKVFlQ",
+            "RV9GTE9BVBACEg4KClRZUEVfSU5UNjQQAxIPCgtUWVBFX1VJTlQ2NBAEEg4K",
+            "ClRZUEVfSU5UMzIQBRIQCgxUWVBFX0ZJWEVENjQQBhIQCgxUWVBFX0ZJWEVE",
+            "MzIQBxINCglUWVBFX0JPT0wQCBIPCgtUWVBFX1NUUklORxAJEg4KClRZUEVf",
+            "R1JPVVAQChIQCgxUWVBFX01FU1NBR0UQCxIOCgpUWVBFX0JZVEVTEAwSDwoL",
+            "VFlQRV9VSU5UMzIQDRINCglUWVBFX0VOVU0QDhIRCg1UWVBFX1NGSVhFRDMy",
+            "EA8SEQoNVFlQRV9TRklYRUQ2NBAQEg8KC1RZUEVfU0lOVDMyEBESDwoLVFlQ",
+            "RV9TSU5UNjQQEiJ0CgtDYXJkaW5hbGl0eRIXChNDQVJESU5BTElUWV9VTktO",
+            "T1dOEAASGAoUQ0FSRElOQUxJVFlfT1BUSU9OQUwQARIYChRDQVJESU5BTElU",
+            "WV9SRVFVSVJFRBACEhgKFENBUkRJTkFMSVRZX1JFUEVBVEVEEAMi3wEKBEVu",
+            "dW0SDAoEbmFtZRgBIAEoCRItCgllbnVtdmFsdWUYAiADKAsyGi5nb29nbGUu",
+            "cHJvdG9idWYuRW51bVZhbHVlEigKB29wdGlvbnMYAyADKAsyFy5nb29nbGUu",
+            "cHJvdG9idWYuT3B0aW9uEjYKDnNvdXJjZV9jb250ZXh0GAQgASgLMh4uZ29v",
+            "Z2xlLnByb3RvYnVmLlNvdXJjZUNvbnRleHQSJwoGc3ludGF4GAUgASgOMhcu",
+            "Z29vZ2xlLnByb3RvYnVmLlN5bnRheBIPCgdlZGl0aW9uGAYgASgJIlMKCUVu",
+            "dW1WYWx1ZRIMCgRuYW1lGAEgASgJEg4KBm51bWJlchgCIAEoBRIoCgdvcHRp",
+            "b25zGAMgAygLMhcuZ29vZ2xlLnByb3RvYnVmLk9wdGlvbiI7CgZPcHRpb24S",
+            "DAoEbmFtZRgBIAEoCRIjCgV2YWx1ZRgCIAEoCzIULmdvb2dsZS5wcm90b2J1",
+            "Zi5BbnkqQwoGU3ludGF4EhEKDVNZTlRBWF9QUk9UTzIQABIRCg1TWU5UQVhf",
+            "UFJPVE8zEAESEwoPU1lOVEFYX0VESVRJT05TEAJCewoTY29tLmdvb2dsZS5w",
+            "cm90b2J1ZkIJVHlwZVByb3RvUAFaLWdvb2dsZS5nb2xhbmcub3JnL3Byb3Rv",
+            "YnVmL3R5cGVzL2tub3duL3R5cGVwYvgBAaICA0dQQqoCHkdvb2dsZS5Qcm90",
+            "b2J1Zi5XZWxsS25vd25UeXBlc2IGcHJvdG8z"));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.AnyReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.SourceContextReflection.Descriptor, },
           new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Google.Protobuf.WellKnownTypes.Syntax), }, null, new pbr::GeneratedClrTypeInfo[] {
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Type), global::Google.Protobuf.WellKnownTypes.Type.Parser, new[]{ "Name", "Fields", "Oneofs", "Options", "SourceContext", "Syntax" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Type), global::Google.Protobuf.WellKnownTypes.Type.Parser, new[]{ "Name", "Fields", "Oneofs", "Options", "SourceContext", "Syntax", "Edition" }, null, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Field), global::Google.Protobuf.WellKnownTypes.Field.Parser, new[]{ "Kind", "Cardinality", "Number", "Name", "TypeUrl", "OneofIndex", "Packed", "Options", "JsonName", "DefaultValue" }, null, new[]{ typeof(global::Google.Protobuf.WellKnownTypes.Field.Types.Kind), typeof(global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality) }, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Enum), global::Google.Protobuf.WellKnownTypes.Enum.Parser, new[]{ "Name", "Enumvalue", "Options", "SourceContext", "Syntax" }, null, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Enum), global::Google.Protobuf.WellKnownTypes.Enum.Parser, new[]{ "Name", "Enumvalue", "Options", "SourceContext", "Syntax", "Edition" }, null, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.EnumValue), global::Google.Protobuf.WellKnownTypes.EnumValue.Parser, new[]{ "Name", "Number", "Options" }, null, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Option), global::Google.Protobuf.WellKnownTypes.Option.Parser, new[]{ "Name", "Value" }, null, null, null, null)
           }));
@@ -86,6 +87,10 @@
     /// Syntax `proto3`.
     /// </summary>
     [pbr::OriginalName("SYNTAX_PROTO3")] Proto3 = 1,
+    /// <summary>
+    /// Syntax `editions`.
+    /// </summary>
+    [pbr::OriginalName("SYNTAX_EDITIONS")] Editions = 2,
   }
 
   #endregion
@@ -134,6 +139,7 @@
       options_ = other.options_.Clone();
       sourceContext_ = other.sourceContext_ != null ? other.sourceContext_.Clone() : null;
       syntax_ = other.syntax_;
+      edition_ = other.edition_;
       _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
     }
 
@@ -230,6 +236,21 @@
       }
     }
 
+    /// <summary>Field number for the "edition" field.</summary>
+    public const int EditionFieldNumber = 7;
+    private string edition_ = "";
+    /// <summary>
+    /// The source edition string, only valid when syntax is SYNTAX_EDITIONS.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Edition {
+      get { return edition_; }
+      set {
+        edition_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public override bool Equals(object other) {
@@ -251,6 +272,7 @@
       if(!options_.Equals(other.options_)) return false;
       if (!object.Equals(SourceContext, other.SourceContext)) return false;
       if (Syntax != other.Syntax) return false;
+      if (Edition != other.Edition) return false;
       return Equals(_unknownFields, other._unknownFields);
     }
 
@@ -264,6 +286,7 @@
       hash ^= options_.GetHashCode();
       if (sourceContext_ != null) hash ^= SourceContext.GetHashCode();
       if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) hash ^= Syntax.GetHashCode();
+      if (Edition.Length != 0) hash ^= Edition.GetHashCode();
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
       }
@@ -297,6 +320,10 @@
         output.WriteRawTag(48);
         output.WriteEnum((int) Syntax);
       }
+      if (Edition.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(Edition);
+      }
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
@@ -322,6 +349,10 @@
         output.WriteRawTag(48);
         output.WriteEnum((int) Syntax);
       }
+      if (Edition.Length != 0) {
+        output.WriteRawTag(58);
+        output.WriteString(Edition);
+      }
       if (_unknownFields != null) {
         _unknownFields.WriteTo(ref output);
       }
@@ -344,6 +375,9 @@
       if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Syntax);
       }
+      if (Edition.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Edition);
+      }
       if (_unknownFields != null) {
         size += _unknownFields.CalculateSize();
       }
@@ -371,6 +405,9 @@
       if (other.Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
         Syntax = other.Syntax;
       }
+      if (other.Edition.Length != 0) {
+        Edition = other.Edition;
+      }
       _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
     }
 
@@ -413,6 +450,10 @@
             Syntax = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
             break;
           }
+          case 58: {
+            Edition = input.ReadString();
+            break;
+          }
         }
       }
     #endif
@@ -455,6 +496,10 @@
             Syntax = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
             break;
           }
+          case 58: {
+            Edition = input.ReadString();
+            break;
+          }
         }
       }
     }
@@ -1162,6 +1207,7 @@
       options_ = other.options_.Clone();
       sourceContext_ = other.sourceContext_ != null ? other.sourceContext_.Clone() : null;
       syntax_ = other.syntax_;
+      edition_ = other.edition_;
       _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
     }
 
@@ -1244,6 +1290,21 @@
       }
     }
 
+    /// <summary>Field number for the "edition" field.</summary>
+    public const int EditionFieldNumber = 6;
+    private string edition_ = "";
+    /// <summary>
+    /// The source edition string, only valid when syntax is SYNTAX_EDITIONS.
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
+    public string Edition {
+      get { return edition_; }
+      set {
+        edition_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
     [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
     [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
     public override bool Equals(object other) {
@@ -1264,6 +1325,7 @@
       if(!options_.Equals(other.options_)) return false;
       if (!object.Equals(SourceContext, other.SourceContext)) return false;
       if (Syntax != other.Syntax) return false;
+      if (Edition != other.Edition) return false;
       return Equals(_unknownFields, other._unknownFields);
     }
 
@@ -1276,6 +1338,7 @@
       hash ^= options_.GetHashCode();
       if (sourceContext_ != null) hash ^= SourceContext.GetHashCode();
       if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) hash ^= Syntax.GetHashCode();
+      if (Edition.Length != 0) hash ^= Edition.GetHashCode();
       if (_unknownFields != null) {
         hash ^= _unknownFields.GetHashCode();
       }
@@ -1308,6 +1371,10 @@
         output.WriteRawTag(40);
         output.WriteEnum((int) Syntax);
       }
+      if (Edition.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(Edition);
+      }
       if (_unknownFields != null) {
         _unknownFields.WriteTo(output);
       }
@@ -1332,6 +1399,10 @@
         output.WriteRawTag(40);
         output.WriteEnum((int) Syntax);
       }
+      if (Edition.Length != 0) {
+        output.WriteRawTag(50);
+        output.WriteString(Edition);
+      }
       if (_unknownFields != null) {
         _unknownFields.WriteTo(ref output);
       }
@@ -1353,6 +1424,9 @@
       if (Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
         size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Syntax);
       }
+      if (Edition.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Edition);
+      }
       if (_unknownFields != null) {
         size += _unknownFields.CalculateSize();
       }
@@ -1379,6 +1453,9 @@
       if (other.Syntax != global::Google.Protobuf.WellKnownTypes.Syntax.Proto2) {
         Syntax = other.Syntax;
       }
+      if (other.Edition.Length != 0) {
+        Edition = other.Edition;
+      }
       _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
     }
 
@@ -1417,6 +1494,10 @@
             Syntax = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
             break;
           }
+          case 50: {
+            Edition = input.ReadString();
+            break;
+          }
         }
       }
     #endif
@@ -1455,6 +1536,10 @@
             Syntax = (global::Google.Protobuf.WellKnownTypes.Syntax) input.ReadEnum();
             break;
           }
+          case 50: {
+            Edition = input.ReadString();
+            break;
+          }
         }
       }
     }
diff --git a/examples/Makefile b/examples/Makefile
index ef7a4ef..f7ee9b1 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -10,11 +10,12 @@
 gotest: add_person_gotest list_people_gotest
 java:   add_person_java   list_people_java
 python: add_person_python list_people_python
+ruby:   add_person_ruby   list_people_ruby
 
 clean:
-	rm -f add_person_cpp list_people_cpp add_person_java list_people_java add_person_python list_people_python
+	rm -f add_person_cpp list_people_cpp add_person_java list_people_java add_person_python list_people_python add_person_ruby list_people_ruby
 	rm -f javac_middleman AddPerson*.class ListPeople*.class com/example/tutorial/protos/*.class
-	rm -f protoc_middleman addressbook.pb.cc addressbook.pb.h addressbook_pb2.py com/example/tutorial/protos/*.java
+	rm -f protoc_middleman protoc_middleman_ruby addressbook.pb.cc addressbook.pb.h addressbook_pb2.py addressbook_pb.rb com/example/tutorial/protos/*.java
 	rm -f *.pyc
 	rm -f go/tutorialpb/*.pb.go add_person_go list_people_go
 	rm -f protoc_middleman_dart dart_tutorial/*.pb*.dart
@@ -39,6 +40,10 @@
 	pub get
 	@touch protoc_middleman_dart
 
+protoc_middleman_ruby: addressbook.proto
+	protoc $$PROTO_PATH --ruby_out=. addressbook.proto
+	@touch protoc_middleman_ruby
+
 add_person_cpp: add_person.cc protoc_middleman
 	pkg-config --cflags protobuf  # fails if protobuf is not installed
 	c++ -std=c++14 add_person.cc addressbook.pb.cc -o add_person_cpp `pkg-config --cflags --libs protobuf`
@@ -90,3 +95,15 @@
 	@echo '#! /bin/sh' > list_people_python
 	@echo './list_people.py "$$@"' >> list_people_python
 	@chmod +x list_people_python
+
+add_person_ruby: add_person.rb protoc_middleman_ruby
+	@echo "Writing shortcut script add_person_ruby..."
+	@echo '#! /bin/sh' > add_person_ruby
+	@echo './add_person.rb "$$@"' >> add_person_ruby
+	@chmod +x add_person_ruby
+
+list_people_ruby: list_people.rb protoc_middleman_ruby
+	@echo "Writing shortcut script list_people_ruby..."
+	@echo '#! /bin/sh' > list_people_ruby
+	@echo './list_people.rb "$$@"' >> list_people_ruby
+	@chmod +x list_people_ruby
diff --git a/examples/add_person.rb b/examples/add_person.rb
new file mode 100755
index 0000000..35b6db9
--- /dev/null
+++ b/examples/add_person.rb
@@ -0,0 +1,77 @@
+#! /usr/bin/env ruby
+
+# See README.md for information and build instructions.
+
+require './addressbook_pb'
+require 'pry'
+
+# creates Person object and fills it with data from user input
+def prompt_for_address()
+  person = Tutorial::Person.newlD()
+
+  puts "Enter person ID number:"
+  person.id = STDIN.gets.chomp.to_i
+  puts "Enter name:"
+  person.name = STDIN.gets.chomp
+
+  puts "Enter email address (blank for none):"
+  email = STDIN.gets.chomp
+
+  if email != ""
+    person.email = email
+  end
+
+  loop do
+    puts "Enter a phone number (or leave blank to finish):"
+    number = STDIN.gets.chomp
+
+    if number == ""
+      break
+    end
+
+    phone_number = Tutorial::Person::PhoneNumber.new(number: number)
+    puts "Is this a mobile, home or work phone?"
+    type = STDIN.gets.chomp
+
+    case type
+    when "mobile"
+      phone_number.type = :MOBILE
+    when "home"
+      phone_number.type = :HOME
+    when "work"
+      phone_number.type = :WORK
+    else
+      puts "Unknown phone type; leaving as default value."
+    end
+    person.phones.push(phone_number)
+  end
+  person
+end
+
+# Main procedure:  Reads the entire address book from a file,
+#   adds one person based on user input, then writes it back out to the same
+#   file.
+if ARGV.length != 1
+  puts "Usage: #{$0} ADDRESS_BOOK_FILE"
+  exit(-1)
+end
+
+address_book = Tutorial::AddressBook.new()
+if File.exist?(ARGV[0])
+  # Read the existing address book if it exists
+  f = File.open(ARGV[0], "rb")
+  address_book = Tutorial::AddressBook.decode(f.read)
+  f.close
+else
+  puts "#{$PROGRAM_NAME}: File not found. Creating new file."
+end
+
+person = prompt_for_address
+
+# Add an address.
+address_book.people.push(person)
+
+# Write the new address book back to disk.
+f = File.open(ARGV[0], "wb")
+f.write(Tutorial::AddressBook.encode(address_book))
+f.close
diff --git a/examples/list_people.rb b/examples/list_people.rb
new file mode 100755
index 0000000..fa63361
--- /dev/null
+++ b/examples/list_people.rb
@@ -0,0 +1,42 @@
+#! /usr/bin/env ruby
+
+require './addressbook_pb'
+require 'pry'
+
+# Iterates though all people in the AddressBook and prints info about them.
+def list_people(address_book)
+  address_book.people.each do |person|
+    puts "Person ID: #{person.id}"
+    puts "  Name: #{person.name}"
+    if person.email != ""
+      puts "  Email: #{person.email}"
+    end
+
+    person.phones.each do |phone_number|
+      type =
+        case phone_number.type
+        when :MOBILE
+          "Mobile phone"
+        when :HOME
+          "Home phone"
+        when :WORK
+          "Work phone"
+        end
+      puts "  #{type} #: #{phone_number.number}"
+    end
+  end
+end
+
+# Main procedure:  Reads the entire address book from a file and prints all
+#   the information inside.
+if ARGV.length != 1
+  puts "Usage: #{$PROGRAM_NAME} ADDRESS_BOOK_FILE"
+  exit(-1)
+end
+
+# Read the existing address book.
+f = File.open(ARGV[0], "rb")
+address_book = Tutorial::AddressBook.decode(f.read)
+f.close
+
+list_people(address_book)
diff --git a/java/core/src/main/java/com/google/protobuf/AbstractProtobufList.java b/java/core/src/main/java/com/google/protobuf/AbstractProtobufList.java
index e792d7d..4491977 100644
--- a/java/core/src/main/java/com/google/protobuf/AbstractProtobufList.java
+++ b/java/core/src/main/java/com/google/protobuf/AbstractProtobufList.java
@@ -52,7 +52,12 @@
 
   /** Constructs a mutable list by default. */
   AbstractProtobufList() {
-    isMutable = true;
+    this(true);
+  }
+
+  /** Constructs an immutable list for EMPTY lists */
+  AbstractProtobufList(boolean isMutable) {
+    this.isMutable = isMutable;
   }
 
   @Override
@@ -130,7 +135,9 @@
 
   @Override
   public final void makeImmutable() {
-    isMutable = false;
+    if (isMutable) {
+      isMutable = false;
+    }
   }
 
   @Override
diff --git a/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java b/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java
index 451fce1..bd1e9fa 100644
--- a/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java
@@ -45,10 +45,7 @@
 final class BooleanArrayList extends AbstractProtobufList<Boolean>
     implements BooleanList, RandomAccess, PrimitiveNonBoxingCollection {
 
-  private static final BooleanArrayList EMPTY_LIST = new BooleanArrayList(new boolean[0], 0);
-  static {
-    EMPTY_LIST.makeImmutable();
-  }
+  private static final BooleanArrayList EMPTY_LIST = new BooleanArrayList(new boolean[0], 0, false);
 
   public static BooleanArrayList emptyList() {
     return EMPTY_LIST;
@@ -65,15 +62,16 @@
 
   /** Constructs a new mutable {@code BooleanArrayList} with default capacity. */
   BooleanArrayList() {
-    this(new boolean[DEFAULT_CAPACITY], 0);
+    this(new boolean[DEFAULT_CAPACITY], 0, true);
   }
 
   /**
    * Constructs a new mutable {@code BooleanArrayList} containing the same elements as {@code
    * other}.
    */
-  private BooleanArrayList(boolean[] other, int size) {
-    array = other;
+  private BooleanArrayList(boolean[] other, int size, boolean isMutable) {
+    super(isMutable);
+    this.array = other;
     this.size = size;
   }
 
@@ -126,7 +124,7 @@
     if (capacity < size) {
       throw new IllegalArgumentException();
     }
-    return new BooleanArrayList(Arrays.copyOf(array, capacity), size);
+    return new BooleanArrayList(Arrays.copyOf(array, capacity), size, true);
   }
 
   @Override
diff --git a/java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java b/java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java
index 272c5a1..9537ccb 100644
--- a/java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java
+++ b/java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java
@@ -315,80 +315,81 @@
       if (fd.getContainingOneof() != null) {
         // Build a oneof member field.
         builder.withField(buildOneofMember(messageType, fd, oneofState, enforceUtf8, enumVerifier));
-      } else {
-        Field field = field(messageType, fd);
-        int number = fd.getNumber();
-        FieldType type = getFieldType(fd);
-
-        if (fd.isMapField()) {
-          // Map field points to an auto-generated message entry type with the definition:
-          //   message MapEntry {
-          //     K key = 1;
-          //     V value = 2;
-          //   }
-          final FieldDescriptor valueField = fd.getMessageType().findFieldByNumber(2);
-          if (valueField.getJavaType() == Descriptors.FieldDescriptor.JavaType.ENUM) {
-            enumVerifier =
-                new Internal.EnumVerifier() {
-                  @Override
-                  public boolean isInRange(int number) {
-                    return valueField.getEnumType().findValueByNumber(number) != null;
-                  }
-                };
-          }
-          builder.withField(
-              forMapField(
-                  field,
-                  number,
-                  SchemaUtil.getMapDefaultEntry(messageType, fd.getName()),
-                  enumVerifier));
-          continue;
-        }
-
-        if (fd.isRepeated()) {
-          // Repeated fields are not presence-checked.
-          if (enumVerifier != null) {
-            if (fd.isPacked()) {
-              builder.withField(
-                  forPackedFieldWithEnumVerifier(
-                      field, number, type, enumVerifier, cachedSizeField(messageType, fd)));
-            } else {
-              builder.withField(forFieldWithEnumVerifier(field, number, type, enumVerifier));
-            }
-          } else if (fd.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
-            builder.withField(
-                forRepeatedMessageField(
-                    field, number, type, getTypeForRepeatedMessageField(messageType, fd)));
-          } else {
-            if (fd.isPacked()) {
-              builder.withField(
-                  forPackedField(field, number, type, cachedSizeField(messageType, fd)));
-            } else {
-              builder.withField(forField(field, number, type, enforceUtf8));
-            }
-          }
-          continue;
-        }
-
-        if (bitField == null) {
-          // Lazy-create the next bitfield since we know it must exist.
-          bitField = bitField(messageType, bitFieldIndex);
-        }
-
-        // It's a presence-checked field.
-        if (fd.isRequired()) {
-          builder.withField(
-              forProto2RequiredField(
-                  field, number, type, bitField, presenceMask, enforceUtf8, enumVerifier));
-        } else {
-          builder.withField(
-              forProto2OptionalField(
-                  field, number, type, bitField, presenceMask, enforceUtf8, enumVerifier));
-        }
+        continue;
       }
 
-      // Update the presence mask for the next iteration. If the shift clears out the mask, we will
-      // go to the next bitField.
+      Field field = field(messageType, fd);
+      int number = fd.getNumber();
+      FieldType type = getFieldType(fd);
+
+      if (fd.isMapField()) {
+        // Map field points to an auto-generated message entry type with the definition:
+        //   message MapEntry {
+        //     K key = 1;
+        //     V value = 2;
+        //   }
+        final FieldDescriptor valueField = fd.getMessageType().findFieldByNumber(2);
+        if (valueField.getJavaType() == Descriptors.FieldDescriptor.JavaType.ENUM) {
+          enumVerifier =
+              new Internal.EnumVerifier() {
+                @Override
+                public boolean isInRange(int number) {
+                  return valueField.getEnumType().findValueByNumber(number) != null;
+                }
+              };
+        }
+        builder.withField(
+            forMapField(
+                field,
+                number,
+                SchemaUtil.getMapDefaultEntry(messageType, fd.getName()),
+                enumVerifier));
+        continue;
+      }
+
+      if (fd.isRepeated()) {
+        // Repeated fields are not presence-checked.
+        if (enumVerifier != null) {
+          if (fd.isPacked()) {
+            builder.withField(
+                forPackedFieldWithEnumVerifier(
+                    field, number, type, enumVerifier, cachedSizeField(messageType, fd)));
+          } else {
+            builder.withField(forFieldWithEnumVerifier(field, number, type, enumVerifier));
+          }
+        } else if (fd.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
+          builder.withField(
+              forRepeatedMessageField(
+                  field, number, type, getTypeForRepeatedMessageField(messageType, fd)));
+        } else {
+          if (fd.isPacked()) {
+            builder.withField(
+                forPackedField(field, number, type, cachedSizeField(messageType, fd)));
+          } else {
+            builder.withField(forField(field, number, type, enforceUtf8));
+          }
+        }
+        continue;
+      }
+
+      if (bitField == null) {
+        // Lazy-create the next bitfield since we know it must exist.
+        bitField = bitField(messageType, bitFieldIndex);
+      }
+
+      // It's a presence-checked field.
+      if (fd.isRequired()) {
+        builder.withField(
+            forProto2RequiredField(
+                field, number, type, bitField, presenceMask, enforceUtf8, enumVerifier));
+      } else {
+        builder.withField(
+            forProto2OptionalField(
+                field, number, type, bitField, presenceMask, enforceUtf8, enumVerifier));
+      }
+
+      // Update the presence mask for the next iteration. If the shift clears out the mask, we
+      // will go to the next bitField.
       presenceMask <<= 1;
       if (presenceMask == 0) {
         bitField = null;
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 e5b973f..d22b504 100644
--- a/java/core/src/main/java/com/google/protobuf/Descriptors.java
+++ b/java/core/src/main/java/com/google/protobuf/Descriptors.java
@@ -634,10 +634,6 @@
         extensions[i].setProto(proto.getExtension(i));
       }
     }
-
-    boolean supportsUnknownEnumValue() {
-      return getSyntax() == Syntax.PROTO3;
-    }
   }
 
   // =================================================================
@@ -1329,6 +1325,30 @@
     }
 
     /**
+     * Determines if the given enum field is treated as closed based on legacy non-conformant
+     * behavior.
+     *
+     * <p>Conformant behavior determines closedness based on the enum and can be queried using
+     * {@code EnumDescriptor.isClosed()}.
+     *
+     * <p>Some runtimes currently have a quirk where non-closed enums are treated as closed when
+     * used as the type of fields defined in a `syntax = proto2;` file. This quirk is not present in
+     * all runtimes; as of writing, we know that:
+     *
+     * <ul>
+     *   <li>C++, Java, and C++-based Python share this quirk.
+     *   <li>UPB and UPB-based Python do not.
+     *   <li>PHP and Ruby treat all enums as open regardless of declaration.
+     * </ul>
+     *
+     * <p>Care should be taken when using this function to respect the target runtime's enum
+     * handling quirks.
+     */
+    public boolean legacyEnumFieldTreatedAsClosed() {
+      return getType() == Type.ENUM && getFile().getSyntax() == Syntax.PROTO2;
+    }
+
+    /**
      * Compare with another {@code FieldDescriptor}. This orders fields in "canonical" order, which
      * simply means ascending order by field number. {@code other} must be a field of the same type.
      * That is, {@code getContainingType()} must return the same {@code Descriptor} for both fields.
diff --git a/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java b/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java
index 4085653..40a3e79 100644
--- a/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java
@@ -45,10 +45,7 @@
 final class DoubleArrayList extends AbstractProtobufList<Double>
     implements DoubleList, RandomAccess, PrimitiveNonBoxingCollection {
 
-  private static final DoubleArrayList EMPTY_LIST = new DoubleArrayList(new double[0], 0);
-  static {
-    EMPTY_LIST.makeImmutable();
-  }
+  private static final DoubleArrayList EMPTY_LIST = new DoubleArrayList(new double[0], 0, false);
 
   public static DoubleArrayList emptyList() {
     return EMPTY_LIST;
@@ -65,14 +62,15 @@
 
   /** Constructs a new mutable {@code DoubleArrayList} with default capacity. */
   DoubleArrayList() {
-    this(new double[DEFAULT_CAPACITY], 0);
+    this(new double[DEFAULT_CAPACITY], 0, true);
   }
 
   /**
    * Constructs a new mutable {@code DoubleArrayList} containing the same elements as {@code other}.
    */
-  private DoubleArrayList(double[] other, int size) {
-    array = other;
+  private DoubleArrayList(double[] other, int size, boolean isMutable) {
+    super(isMutable);
+    this.array = other;
     this.size = size;
   }
 
@@ -126,7 +124,7 @@
     if (capacity < size) {
       throw new IllegalArgumentException();
     }
-    return new DoubleArrayList(Arrays.copyOf(array, capacity), size);
+    return new DoubleArrayList(Arrays.copyOf(array, capacity), size, true);
   }
 
   @Override
diff --git a/java/core/src/main/java/com/google/protobuf/DynamicMessage.java b/java/core/src/main/java/com/google/protobuf/DynamicMessage.java
index 738c303..e564a63 100644
--- a/java/core/src/main/java/com/google/protobuf/DynamicMessage.java
+++ b/java/core/src/main/java/com/google/protobuf/DynamicMessage.java
@@ -535,11 +535,10 @@
           fields.clearField(oldField);
         }
         oneofCases[index] = field;
-      } else if (field.getFile().getSyntax() == Descriptors.FileDescriptor.Syntax.PROTO3) {
-        if (!field.isRepeated()
-            && field.getJavaType() != FieldDescriptor.JavaType.MESSAGE
-            && value.equals(field.getDefaultValue())) {
-          // In proto3, setting a field to its default value is equivalent to clearing the field.
+      } else if (!field.hasPresence()) {
+        if (!field.isRepeated() && value.equals(field.getDefaultValue())) {
+          // Setting a field without presence to its default value is equivalent to clearing the
+          // field.
           fields.clearField(field);
           return this;
         }
diff --git a/java/core/src/main/java/com/google/protobuf/FloatArrayList.java b/java/core/src/main/java/com/google/protobuf/FloatArrayList.java
index e6feba8..023fd6c 100644
--- a/java/core/src/main/java/com/google/protobuf/FloatArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/FloatArrayList.java
@@ -45,10 +45,7 @@
 final class FloatArrayList extends AbstractProtobufList<Float>
     implements FloatList, RandomAccess, PrimitiveNonBoxingCollection {
 
-  private static final FloatArrayList EMPTY_LIST = new FloatArrayList(new float[0], 0);
-  static {
-    EMPTY_LIST.makeImmutable();
-  }
+  private static final FloatArrayList EMPTY_LIST = new FloatArrayList(new float[0], 0, false);
 
   public static FloatArrayList emptyList() {
     return EMPTY_LIST;
@@ -65,14 +62,15 @@
 
   /** Constructs a new mutable {@code FloatArrayList} with default capacity. */
   FloatArrayList() {
-    this(new float[DEFAULT_CAPACITY], 0);
+    this(new float[DEFAULT_CAPACITY], 0, true);
   }
 
   /**
    * Constructs a new mutable {@code FloatArrayList} containing the same elements as {@code other}.
    */
-  private FloatArrayList(float[] other, int size) {
-    array = other;
+  private FloatArrayList(float[] other, int size, boolean isMutable) {
+    super(isMutable);
+    this.array = other;
     this.size = size;
   }
 
@@ -125,7 +123,7 @@
     if (capacity < size) {
       throw new IllegalArgumentException();
     }
-    return new FloatArrayList(Arrays.copyOf(array, capacity), size);
+    return new FloatArrayList(Arrays.copyOf(array, capacity), size, true);
   }
 
   @Override
diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java
index 26cc5bb..e62a382 100644
--- a/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java
+++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java
@@ -2646,7 +2646,7 @@
         valueOfMethod = getMethodOrDie(type, "valueOf", EnumValueDescriptor.class);
         getValueDescriptorMethod = getMethodOrDie(type, "getValueDescriptor");
 
-        supportUnknownEnumValue = descriptor.getFile().supportsUnknownEnumValue();
+        supportUnknownEnumValue = !descriptor.legacyEnumFieldTreatedAsClosed();
         if (supportUnknownEnumValue) {
           getValueMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "Value");
           getValueMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName + "Value");
@@ -2705,7 +2705,7 @@
         valueOfMethod = getMethodOrDie(type, "valueOf", EnumValueDescriptor.class);
         getValueDescriptorMethod = getMethodOrDie(type, "getValueDescriptor");
 
-        supportUnknownEnumValue = descriptor.getFile().supportsUnknownEnumValue();
+        supportUnknownEnumValue = !descriptor.legacyEnumFieldTreatedAsClosed();
         if (supportUnknownEnumValue) {
           getRepeatedValueMethod =
               getMethodOrDie(messageClass, "get" + camelCaseName + "Value", int.class);
diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java
index ab9581a..3fb6ea2 100644
--- a/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java
+++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java
@@ -423,6 +423,26 @@
     return makeMutableCopy(list);
   }
 
+  // TODO(b/258340024): Redundant with makeMutableCopy(). Remove.
+  protected static LongList mutableCopy(LongList list) {
+    return makeMutableCopy(list);
+  }
+
+  // TODO(b/258340024): Redundant with makeMutableCopy(). Remove.
+  protected static FloatList mutableCopy(FloatList list) {
+    return makeMutableCopy(list);
+  }
+
+  // TODO(b/258340024): Redundant with makeMutableCopy(). Remove.
+  protected static DoubleList mutableCopy(DoubleList list) {
+    return makeMutableCopy(list);
+  }
+
+  // TODO(b/258340024): Redundant with makeMutableCopy(). Remove.
+  protected static BooleanList mutableCopy(BooleanList list) {
+    return makeMutableCopy(list);
+  }
+
   protected static LongList emptyLongList() {
     return LongArrayList.emptyList();
   }
@@ -432,11 +452,6 @@
     return new LongArrayList();
   }
 
-  // TODO(b/258340024): Redundant with makeMutableCopy(). Remove.
-  protected static LongList mutableCopy(LongList list) {
-    return makeMutableCopy(list);
-  }
-
   protected static FloatList emptyFloatList() {
     return FloatArrayList.emptyList();
   }
@@ -446,11 +461,6 @@
     return new FloatArrayList();
   }
 
-  // TODO(b/258340024): Redundant with makeMutableCopy(). Remove.
-  protected static FloatList mutableCopy(FloatList list) {
-    return makeMutableCopy(list);
-  }
-
   protected static DoubleList emptyDoubleList() {
     return DoubleArrayList.emptyList();
   }
@@ -460,11 +470,6 @@
     return new DoubleArrayList();
   }
 
-  // TODO(b/258340024): Redundant with makeMutableCopy(). Remove.
-  protected static DoubleList mutableCopy(DoubleList list) {
-    return makeMutableCopy(list);
-  }
-
   protected static BooleanList emptyBooleanList() {
     return BooleanArrayList.emptyList();
   }
@@ -474,11 +479,6 @@
     return new BooleanArrayList();
   }
 
-  // TODO(b/258340024): Redundant with makeMutableCopy(). Remove.
-  protected static BooleanList mutableCopy(BooleanList list) {
-    return makeMutableCopy(list);
-  }
-
   @SuppressWarnings("unchecked") // Guaranteed by proto runtime.
   protected static <ListT extends ProtobufList<?>> ListT makeMutableCopy(ListT list) {
     int size = list.size();
@@ -2407,10 +2407,7 @@
         isOneofField =
             descriptor.getContainingOneof() != null
                 && !descriptor.getContainingOneof().isSynthetic();
-        hasHasMethod =
-            descriptor.getFile().getSyntax() == FileDescriptor.Syntax.PROTO2
-                || descriptor.hasOptionalKeyword()
-                || (!isOneofField && descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE);
+        hasHasMethod = descriptor.hasPresence();
         ReflectionInvoker reflectionInvoker =
             new ReflectionInvoker(
                 descriptor,
@@ -2892,7 +2889,7 @@
         valueOfMethod = getMethodOrDie(type, "valueOf", EnumValueDescriptor.class);
         getValueDescriptorMethod = getMethodOrDie(type, "getValueDescriptor");
 
-        supportUnknownEnumValue = descriptor.getFile().supportsUnknownEnumValue();
+        supportUnknownEnumValue = !descriptor.legacyEnumFieldTreatedAsClosed();
         if (supportUnknownEnumValue) {
           getValueMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "Value");
           getValueMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName + "Value");
@@ -2953,7 +2950,7 @@
         valueOfMethod = getMethodOrDie(type, "valueOf", EnumValueDescriptor.class);
         getValueDescriptorMethod = getMethodOrDie(type, "getValueDescriptor");
 
-        supportUnknownEnumValue = descriptor.getFile().supportsUnknownEnumValue();
+        supportUnknownEnumValue = !descriptor.legacyEnumFieldTreatedAsClosed();
         if (supportUnknownEnumValue) {
           getRepeatedValueMethod =
               getMethodOrDie(messageClass, "get" + camelCaseName + "Value", int.class);
diff --git a/java/core/src/main/java/com/google/protobuf/IntArrayList.java b/java/core/src/main/java/com/google/protobuf/IntArrayList.java
index 9daeebe..692da9c 100644
--- a/java/core/src/main/java/com/google/protobuf/IntArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/IntArrayList.java
@@ -45,10 +45,7 @@
 final class IntArrayList extends AbstractProtobufList<Integer>
     implements IntList, RandomAccess, PrimitiveNonBoxingCollection {
 
-  private static final IntArrayList EMPTY_LIST = new IntArrayList(new int[0], 0);
-  static {
-    EMPTY_LIST.makeImmutable();
-  }
+  private static final IntArrayList EMPTY_LIST = new IntArrayList(new int[0], 0, false);
 
   public static IntArrayList emptyList() {
     return EMPTY_LIST;
@@ -65,13 +62,14 @@
 
   /** Constructs a new mutable {@code IntArrayList} with default capacity. */
   IntArrayList() {
-    this(new int[DEFAULT_CAPACITY], 0);
+    this(new int[DEFAULT_CAPACITY], 0, true);
   }
 
   /**
    * Constructs a new mutable {@code IntArrayList} containing the same elements as {@code other}.
    */
-  private IntArrayList(int[] other, int size) {
+  private IntArrayList(int[] other, int size, boolean isMutable) {
+    super(isMutable);
     array = other;
     this.size = size;
   }
@@ -125,7 +123,7 @@
     if (capacity < size) {
       throw new IllegalArgumentException();
     }
-    return new IntArrayList(Arrays.copyOf(array, capacity), size);
+    return new IntArrayList(Arrays.copyOf(array, capacity), size, true);
   }
 
   @Override
diff --git a/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java b/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java
index 7ef9687..3ff5a3b 100644
--- a/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java
@@ -61,11 +61,7 @@
 public class LazyStringArrayList extends AbstractProtobufList<String>
     implements LazyStringList, RandomAccess {
 
-  private static final LazyStringArrayList EMPTY_LIST = new LazyStringArrayList();
-
-  static {
-    EMPTY_LIST.makeImmutable();
-  }
+  private static final LazyStringArrayList EMPTY_LIST = new LazyStringArrayList(false);
 
   /** Returns an empty immutable {@code LazyStringArrayList} instance */
   public static LazyStringArrayList emptyList() {
@@ -87,6 +83,11 @@
     this(DEFAULT_CAPACITY);
   }
 
+  private LazyStringArrayList(boolean isMutable) {
+    super(isMutable);
+    this.list = Collections.emptyList();
+  }
+
   public LazyStringArrayList(int initialCapacity) {
     this(new ArrayList<Object>(initialCapacity));
   }
diff --git a/java/core/src/main/java/com/google/protobuf/LongArrayList.java b/java/core/src/main/java/com/google/protobuf/LongArrayList.java
index bda43a4..b5ca4de 100644
--- a/java/core/src/main/java/com/google/protobuf/LongArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/LongArrayList.java
@@ -45,10 +45,7 @@
 final class LongArrayList extends AbstractProtobufList<Long>
     implements LongList, RandomAccess, PrimitiveNonBoxingCollection {
 
-  private static final LongArrayList EMPTY_LIST = new LongArrayList(new long[0], 0);
-  static {
-    EMPTY_LIST.makeImmutable();
-  }
+  private static final LongArrayList EMPTY_LIST = new LongArrayList(new long[0], 0, false);
 
   public static LongArrayList emptyList() {
     return EMPTY_LIST;
@@ -65,14 +62,15 @@
 
   /** Constructs a new mutable {@code LongArrayList} with default capacity. */
   LongArrayList() {
-    this(new long[DEFAULT_CAPACITY], 0);
+    this(new long[DEFAULT_CAPACITY], 0, true);
   }
 
   /**
    * Constructs a new mutable {@code LongArrayList} containing the same elements as {@code other}.
    */
-  private LongArrayList(long[] other, int size) {
-    array = other;
+  private LongArrayList(long[] other, int size, boolean isMutable) {
+    super(isMutable);
+    this.array = other;
     this.size = size;
   }
 
@@ -125,7 +123,7 @@
     if (capacity < size) {
       throw new IllegalArgumentException();
     }
-    return new LongArrayList(Arrays.copyOf(array, capacity), size);
+    return new LongArrayList(Arrays.copyOf(array, capacity), size, true);
   }
 
   @Override
diff --git a/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java b/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java
index 2a4d867..2675070 100644
--- a/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java
+++ b/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java
@@ -95,7 +95,8 @@
 
   /**
    * Returns true if the given field is set. This is exactly equivalent to calling the generated
-   * "has" accessor method corresponding to the field.
+   * "has" accessor method corresponding to the field. The return value of hasField() is
+   * semantically meaningful only for fields where field.hasPresence() == true.
    *
    * @throws IllegalArgumentException The field is a repeated field, or {@code
    *     field.getContainingType() != getDescriptorForType()}.
diff --git a/java/core/src/main/java/com/google/protobuf/MessageReflection.java b/java/core/src/main/java/com/google/protobuf/MessageReflection.java
index 0404042..06cb7cd 100644
--- a/java/core/src/main/java/com/google/protobuf/MessageReflection.java
+++ b/java/core/src/main/java/com/google/protobuf/MessageReflection.java
@@ -1199,10 +1199,7 @@
       if (field.getLiteType() == WireFormat.FieldType.ENUM) {
         while (input.getBytesUntilLimit() > 0) {
           final int rawValue = input.readEnum();
-          if (field.getFile().supportsUnknownEnumValue()) {
-            target.addRepeatedField(
-                field, field.getEnumType().findValueByNumberCreatingIfUnknown(rawValue));
-          } else {
+          if (field.legacyEnumFieldTreatedAsClosed()) {
             final Object value = field.getEnumType().findValueByNumber(rawValue);
             // If the number isn't recognized as a valid value for this enum,
             // add it to the unknown fields.
@@ -1213,6 +1210,9 @@
             } else {
               target.addRepeatedField(field, value);
             }
+          } else {
+            target.addRepeatedField(
+                field, field.getEnumType().findValueByNumberCreatingIfUnknown(rawValue));
           }
         }
       } else {
@@ -1239,9 +1239,7 @@
           }
         case ENUM:
           final int rawValue = input.readEnum();
-          if (field.getFile().supportsUnknownEnumValue()) {
-            value = field.getEnumType().findValueByNumberCreatingIfUnknown(rawValue);
-          } else {
+          if (field.legacyEnumFieldTreatedAsClosed()) {
             value = field.getEnumType().findValueByNumber(rawValue);
             // If the number isn't recognized as a valid value for this enum,
             // add it to the unknown fields.
@@ -1251,6 +1249,8 @@
               }
               return true;
             }
+          } else {
+            value = field.getEnumType().findValueByNumberCreatingIfUnknown(rawValue);
           }
           break;
         default:
diff --git a/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java b/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java
index 33e4bd5..28b547c 100644
--- a/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java
@@ -38,11 +38,7 @@
 final class ProtobufArrayList<E> extends AbstractProtobufList<E> implements RandomAccess {
 
   private static final ProtobufArrayList<Object> EMPTY_LIST =
-      new ProtobufArrayList<Object>(new Object[0], 0);
-
-  static {
-    EMPTY_LIST.makeImmutable();
-  }
+      new ProtobufArrayList<Object>(new Object[0], 0, false);
 
   @SuppressWarnings("unchecked") // Guaranteed safe by runtime.
   public static <E> ProtobufArrayList<E> emptyList() {
@@ -54,10 +50,11 @@
 
   @SuppressWarnings("unchecked")
   ProtobufArrayList() {
-    this((E[]) new Object[DEFAULT_CAPACITY], 0);
+    this((E[]) new Object[DEFAULT_CAPACITY], 0, true);
   }
 
-  private ProtobufArrayList(E[] array, int size) {
+  private ProtobufArrayList(E[] array, int size, boolean isMutable) {
+    super(isMutable);
     this.array = array;
     this.size = size;
   }
@@ -70,7 +67,7 @@
 
     E[] newArray = Arrays.copyOf(array, capacity);
 
-    return new ProtobufArrayList<E>(newArray, size);
+    return new ProtobufArrayList<E>(newArray, size, true);
   }
 
   @Override
diff --git a/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java b/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java
index 38e547c..79c710a 100644
--- a/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java
+++ b/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java
@@ -99,6 +99,7 @@
     FileDescriptor file = UnittestProto.getDescriptor();
 
     assertThat(file.getName()).isEqualTo("google/protobuf/unittest.proto");
+    assertThat(file.getSyntax()).isEqualTo(Descriptors.FileDescriptor.Syntax.PROTO2);
     assertThat(file.getPackage()).isEqualTo("protobuf_unittest");
     assertThat(file.getOptions().getJavaOuterClassname()).isEqualTo("UnittestProto");
     assertThat(file.toProto().getName()).isEqualTo("google/protobuf/unittest.proto");
@@ -148,6 +149,17 @@
   }
 
   @Test
+  public void testFileDescriptorGetSyntax() throws Exception {
+    FileDescriptorProto proto2 = FileDescriptorProto.newBuilder().setSyntax("proto2").build();
+    FileDescriptor file2 = Descriptors.FileDescriptor.buildFrom(proto2, new FileDescriptor[0]);
+    assertThat(file2.getSyntax()).isEqualTo(Descriptors.FileDescriptor.Syntax.PROTO2);
+
+    FileDescriptorProto proto3 = FileDescriptorProto.newBuilder().setSyntax("proto3").build();
+    FileDescriptor file3 = Descriptors.FileDescriptor.buildFrom(proto3, new FileDescriptor[0]);
+    assertThat(file3.getSyntax()).isEqualTo(Descriptors.FileDescriptor.Syntax.PROTO3);
+  }
+
+  @Test
   public void testDescriptor() throws Exception {
     Descriptor messageType = TestAllTypes.getDescriptor();
     Descriptor nestedType = TestAllTypes.NestedMessage.getDescriptor();
@@ -290,6 +302,130 @@
   }
 
   @Test
+  public void testFieldDescriptorLegacyEnumFieldTreatedAsClosed() throws Exception {
+    // Make an open enum definition.
+    FileDescriptorProto openEnumFile =
+        FileDescriptorProto.newBuilder()
+            .setName("open_enum.proto")
+            .setSyntax("proto3")
+            .addEnumType(
+                EnumDescriptorProto.newBuilder()
+                    .setName("TestEnumOpen")
+                    .addValue(
+                        EnumValueDescriptorProto.newBuilder()
+                            .setName("TestEnumOpen_VALUE0")
+                            .setNumber(0)
+                            .build())
+                    .build())
+            .build();
+    FileDescriptor openFileDescriptor =
+        Descriptors.FileDescriptor.buildFrom(openEnumFile, new FileDescriptor[0]);
+    EnumDescriptor openEnum = openFileDescriptor.getEnumTypes().get(0);
+    assertThat(openEnum.isClosed()).isFalse();
+
+    // Create a message that treats enum fields as closed.
+    FileDescriptorProto closedEnumFile =
+        FileDescriptorProto.newBuilder()
+            .setName("closed_enum_field.proto")
+            .addDependency("open_enum.proto")
+            .setSyntax("proto2")
+            .addEnumType(
+                EnumDescriptorProto.newBuilder()
+                    .setName("TestEnum")
+                    .addValue(
+                        EnumValueDescriptorProto.newBuilder()
+                            .setName("TestEnum_VALUE0")
+                            .setNumber(0)
+                            .build())
+                    .build())
+            .addMessageType(
+                DescriptorProto.newBuilder()
+                    .setName("TestClosedEnumField")
+                    .addField(
+                        FieldDescriptorProto.newBuilder()
+                            .setName("int_field")
+                            .setNumber(1)
+                            .setType(FieldDescriptorProto.Type.TYPE_INT32)
+                            .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+                            .build())
+                    .addField(
+                        FieldDescriptorProto.newBuilder()
+                            .setName("open_enum")
+                            .setNumber(2)
+                            .setType(FieldDescriptorProto.Type.TYPE_ENUM)
+                            .setTypeName("TestEnumOpen")
+                            .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+                            .build())
+                    .addField(
+                        FieldDescriptorProto.newBuilder()
+                            .setName("closed_enum")
+                            .setNumber(3)
+                            .setType(FieldDescriptorProto.Type.TYPE_ENUM)
+                            .setTypeName("TestEnum")
+                            .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+                            .build())
+                    .build())
+            .build();
+    Descriptor closedMessage =
+        Descriptors.FileDescriptor.buildFrom(
+                closedEnumFile, new FileDescriptor[] {openFileDescriptor})
+            .getMessageTypes()
+            .get(0);
+    assertThat(closedMessage.findFieldByName("int_field").legacyEnumFieldTreatedAsClosed())
+        .isFalse();
+
+    assertThat(closedMessage.findFieldByName("closed_enum").legacyEnumFieldTreatedAsClosed())
+        .isTrue();
+    assertThat(closedMessage.findFieldByName("open_enum").legacyEnumFieldTreatedAsClosed())
+        .isTrue();
+  }
+
+  @Test
+  public void testFieldDescriptorLegacyEnumFieldTreatedAsOpen() throws Exception {
+    // Make an open enum definition and message that treats enum fields as open.
+    FileDescriptorProto openEnumFile =
+        FileDescriptorProto.newBuilder()
+            .setName("open_enum.proto")
+            .setSyntax("proto3")
+            .addEnumType(
+                EnumDescriptorProto.newBuilder()
+                    .setName("TestEnumOpen")
+                    .addValue(
+                        EnumValueDescriptorProto.newBuilder()
+                            .setName("TestEnumOpen_VALUE0")
+                            .setNumber(0)
+                            .build())
+                    .build())
+            .addMessageType(
+                DescriptorProto.newBuilder()
+                    .setName("TestOpenEnumField")
+                    .addField(
+                        FieldDescriptorProto.newBuilder()
+                            .setName("int_field")
+                            .setNumber(1)
+                            .setType(FieldDescriptorProto.Type.TYPE_INT32)
+                            .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+                            .build())
+                    .addField(
+                        FieldDescriptorProto.newBuilder()
+                            .setName("open_enum")
+                            .setNumber(2)
+                            .setType(FieldDescriptorProto.Type.TYPE_ENUM)
+                            .setTypeName("TestEnumOpen")
+                            .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+                            .build())
+                    .build())
+            .build();
+    FileDescriptor openEnumFileDescriptor =
+        Descriptors.FileDescriptor.buildFrom(openEnumFile, new FileDescriptor[0]);
+    Descriptor openMessage = openEnumFileDescriptor.getMessageTypes().get(0);
+    EnumDescriptor openEnum = openEnumFileDescriptor.findEnumTypeByName("TestEnumOpen");
+    assertThat(openEnum.isClosed()).isFalse();
+    assertThat(openMessage.findFieldByName("int_field").legacyEnumFieldTreatedAsClosed()).isFalse();
+    assertThat(openMessage.findFieldByName("open_enum").legacyEnumFieldTreatedAsClosed()).isFalse();
+  }
+
+  @Test
   public void testEnumDescriptor() throws Exception {
     EnumDescriptor enumType = ForeignEnum.getDescriptor();
     EnumDescriptor nestedType = TestAllTypes.NestedEnum.getDescriptor();
diff --git a/java/core/src/test/java/com/google/protobuf/TextFormatTest.java b/java/core/src/test/java/com/google/protobuf/TextFormatTest.java
index cf0061e..c933504 100644
--- a/java/core/src/test/java/com/google/protobuf/TextFormatTest.java
+++ b/java/core/src/test/java/com/google/protobuf/TextFormatTest.java
@@ -57,6 +57,7 @@
 import protobuf_unittest.UnittestProto.TestEmptyMessage;
 import protobuf_unittest.UnittestProto.TestOneof2;
 import protobuf_unittest.UnittestProto.TestRequired;
+import protobuf_unittest.UnittestProto.TestReservedFields;
 import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet;
 import java.io.StringReader;
 import java.util.Arrays;
diff --git a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
index 96bc388..719980a 100644
--- a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
+++ b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
@@ -1925,10 +1925,10 @@
         // Try to interpret the value as a number.
         try {
           int numericValue = parseInt32(json);
-          if (enumDescriptor.getFile().getSyntax() == FileDescriptor.Syntax.PROTO3) {
-            result = enumDescriptor.findValueByNumberCreatingIfUnknown(numericValue);
-          } else {
+          if (enumDescriptor.isClosed()) {
             result = enumDescriptor.findValueByNumber(numericValue);
+          } else {
+            result = enumDescriptor.findValueByNumberCreatingIfUnknown(numericValue);
           }
         } catch (InvalidProtocolBufferException e) {
           // Fall through. This exception is about invalid int32 value we get from parseInt32() but
diff --git a/objectivec/BUILD.bazel b/objectivec/BUILD.bazel
index 1c45357..0965de5 100644
--- a/objectivec/BUILD.bazel
+++ b/objectivec/BUILD.bazel
@@ -1,20 +1,66 @@
 load("@rules_cc//cc:defs.bzl", "objc_library")
 load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix")
+load("@upb//cmake:build_defs.bzl", "staleness_test")
 load("//conformance:defs.bzl", "conformance_test")
+load(":defs.bzl", "objc_proto_camel_case_name")
+
+# The WKTs have to be checked in to support the CocoaPods and Xcode builds. This
+# generule and test ensure the source are current.
+#
+# Within the ":objectivec" target, the outputs of the genrule are then used to
+# ensure they are always "current". This implementation is basically the same
+# has how the WKTs are handled in src/google/protobuf/BUILD.bazel for the C++
+# version. They share the potential downsides around layer checks and that
+# someone could #include the header with the `wkt/` prefix on the name.
+
+_WELL_KNOWN_TYPES = [
+    "any",
+    "api",
+    "duration",
+    "empty",
+    "field_mask",
+    "source_context",
+    "struct",
+    "timestamp",
+    "type",
+    "wrappers",
+]
+
+_OBJC_WKT_NAMES = [objc_proto_camel_case_name(x) for x in _WELL_KNOWN_TYPES]
+
+_OBJC_EXTS = [
+    ".pbobjc.h",
+    ".pbobjc.m",
+]
+
+genrule(
+    name = "gen_wkt_sources",
+    srcs = ["//src/google/protobuf:well_known_type_protos"],
+    outs = ["wkt/GPB" + wkt + ext for wkt in _OBJC_WKT_NAMES for ext in _OBJC_EXTS],
+    cmd = " && ".join([
+        "$(execpath //:protoc) --objc_out=$(RULEDIR)/wkt --proto_path=src $(SRCS)",
+    ] + [
+        "mv $(RULEDIR)/wkt/google/protobuf/" + wkt + ext + " $(RULEDIR)/wkt/GPB" + wkt + ext
+        for wkt in _OBJC_WKT_NAMES
+        for ext in _OBJC_EXTS
+    ]),
+    exec_tools = ["//:protoc"],
+)
+
+staleness_test(
+    name = "well_known_types_staleness_test",
+    outs = ["GPB" + wkt + ext for wkt in _OBJC_WKT_NAMES for ext in _OBJC_EXTS],
+    generated_pattern = "wkt/%s",
+    tags = ["manual"],
+)
+
+################################################################################
+# Objective-C Runtime Library
+################################################################################
 
 objc_library(
     name = "objectivec",
     hdrs = [
-        "GPBAny.pbobjc.h",
-        "GPBApi.pbobjc.h",
-        "GPBDuration.pbobjc.h",
-        "GPBEmpty.pbobjc.h",
-        "GPBFieldMask.pbobjc.h",
-        "GPBSourceContext.pbobjc.h",
-        "GPBStruct.pbobjc.h",
-        "GPBTimestamp.pbobjc.h",
-        "GPBType.pbobjc.h",
-        "GPBWrappers.pbobjc.h",
         "GPBArray.h",
         "GPBBootstrap.h",
         "GPBCodedInputStream.h",
@@ -55,39 +101,30 @@
         "GPBUnknownFieldSet_PackagePrivate.h",
         "GPBUnknownField_PackagePrivate.h",
         "GPBUtilities_PackagePrivate.h",
-    ],
+    ] + ["wkt/GPB" + wkt + ".pbobjc.h" for wkt in _OBJC_WKT_NAMES],
     copts = [
         "-Wno-vla",
     ],
     includes = [
         ".",
+        "wkt",
     ],
     non_arc_srcs = [
-        "GPBAny.pbobjc.m",
-        "GPBApi.pbobjc.m",
         "GPBArray.m",
         "GPBCodedInputStream.m",
         "GPBCodedOutputStream.m",
         "GPBDescriptor.m",
         "GPBDictionary.m",
-        "GPBDuration.pbobjc.m",
-        "GPBEmpty.pbobjc.m",
         "GPBExtensionInternals.m",
         "GPBExtensionRegistry.m",
-        "GPBFieldMask.pbobjc.m",
         "GPBMessage.m",
         "GPBRootObject.m",
-        "GPBSourceContext.pbobjc.m",
-        "GPBStruct.pbobjc.m",
-        "GPBTimestamp.pbobjc.m",
-        "GPBType.pbobjc.m",
         "GPBUnknownField.m",
         "GPBUnknownFieldSet.m",
         "GPBUtilities.m",
         "GPBWellKnownTypes.m",
         "GPBWireFormat.m",
-        "GPBWrappers.pbobjc.m",
-    ],
+    ] + ["wkt/GPB" + wkt + ".pbobjc.m" for wkt in _OBJC_WKT_NAMES],
     target_compatible_with = select({
         "@platforms//os:macos": [],
         "@platforms//os:ios": [],
@@ -105,8 +142,8 @@
 conformance_test(
     name = "conformance_test",
     failure_list = "//conformance:failure_list_objc.txt",
-    testee = "//conformance:conformance_objc",
     target_compatible_with = ["@platforms//os:macos"],
+    testee = "//conformance:conformance_objc",
 )
 
 # -------------------------------------------------------------------
@@ -121,6 +158,34 @@
     ],
 )
 
+# -------------------------------------------------------------------
+# Validation of pddm expansion.
+
+py_binary(
+    name = "pddm",
+    srcs = ["DevTools/pddm.py"],
+)
+
+py_test(
+    name = "pddm_tests",
+    size = "small",
+    srcs = [
+        "DevTools/pddm.py",
+        "DevTools/pddm_tests.py",
+    ],
+)
+
+sh_test(
+    name = "sources_pddm_expansion_test",
+    size = "small",
+    srcs = ["DevTools/sources_pddm_expansion_test.sh"],
+    data = [":pddm"] + glob([
+        "**/*.h",
+        "**/*.m",
+        "**/*.pddm",
+    ]),
+)
+
 ################################################################################
 # Distribution files
 ################################################################################
diff --git a/objectivec/DevTools/full_mac_build.sh b/objectivec/DevTools/full_mac_build.sh
index d091a95..16981f9 100755
--- a/objectivec/DevTools/full_mac_build.sh
+++ b/objectivec/DevTools/full_mac_build.sh
@@ -2,8 +2,6 @@
 #
 # Helper to do build so you don't have to remember all the steps/args.
 
-echo "::group::Run full mac build"
-
 set -eu
 
 # Some base locations.
@@ -12,7 +10,7 @@
 readonly BazelFlags="${BAZEL_FLAGS:---announce_rc --macos_minimum_os=10.9}"
 
 # Invoke with BAZEL=bazelisk to use that instead.
-readonly BazelBin="${BAZEL:-bazel} ${BAZEL_STARTUP_FLAGS:-}"
+readonly BazelBin="${BAZEL:-bazel}"
 
 printUsage() {
   NAME=$(basename "${0}")
@@ -29,9 +27,6 @@
          Show this message
    -c, --clean
          Issue a clean before the normal build.
-   -r, --regenerate-descriptors
-         Run generate_descriptor_proto.sh to regenerate all the checked in
-         proto sources.
    --full-build
          By default only protoc is built within protobuf, this option will
          enable a full build/test of the entire protobuf project.
@@ -76,7 +71,6 @@
 fi
 
 DO_CLEAN=no
-REGEN_DESCRIPTORS=no
 FULL_BUILD=no
 DO_XCODE_IOS_TESTS=yes
 DO_XCODE_OSX_TESTS=yes
@@ -94,9 +88,6 @@
     -c | --clean )
       DO_CLEAN=yes
       ;;
-    -r | --regenerate-descriptors )
-      REGEN_DESCRIPTORS=yes
-      ;;
     --full-build )
       FULL_BUILD=yes
       ;;
@@ -190,21 +181,16 @@
   fi
 fi
 
-if [[ "${REGEN_DESCRIPTORS}" == "yes" ]] ; then
-  header "Regenerating the descriptor sources."
-  ./generate_descriptor_proto.sh
-fi
-
 if [[ "${FULL_BUILD}" == "yes" ]] ; then
   header "Build/Test: everything"
-  time ${BazelBin} test //:protoc //:protobuf //src/... $BazelFlags
+  ${BazelBin} test //:protoc //:protobuf //src/... $BazelFlags
 else
   header "Building: protoc"
-  time ${BazelBin} build //:protoc $BazelFlags
+  ${BazelBin} build //:protoc $BazelFlags
 fi
 
 # Ensure the WKT sources checked in are current.
-time objectivec/generate_well_known_types.sh --check-only $BazelFlags
+objectivec/generate_well_known_types.sh --check-only
 
 header "Checking on the ObjC Runtime Code"
 # Some of the kokoro machines don't have python3 yet, so fall back to python if need be.
@@ -233,8 +219,7 @@
   if [[ "${XCODE_QUIET}" == "yes" ]] ; then
     XCODEBUILD_TEST_BASE_IOS+=( -quiet )
   fi
-  # Don't need to worry about form factors or retina/non retina;
-  # just pick a mix of OS Versions and 32/64 bit.
+  # Don't need to worry about form factors or retina/non retina.
   # NOTE: Different Xcode have different simulated hardware/os support.
   case "${XCODE_VERSION}" in
     [6-9].* | 1[0-2].* )
@@ -242,9 +227,8 @@
       exit 11
       ;;
     13.* | 14.*)
-      # Dropped 32bit as Apple doesn't seem support the simulators either.
       XCODEBUILD_TEST_BASE_IOS+=(
-          -destination "platform=iOS Simulator,name=iPhone 8,OS=latest" # 64bit
+          -destination "platform=iOS Simulator,name=iPhone 13,OS=latest"
       )
       ;;
     * )
@@ -271,8 +255,7 @@
     "${XCODEBUILD}"
       -project objectivec/ProtocolBuffers_OSX.xcodeproj
       -scheme ProtocolBuffers
-      # Since the ObjC 2.0 Runtime is required, 32bit OS X isn't supported.
-      -destination "platform=OS X,arch=x86_64" # 64bit
+      -destination "platform=macOS"
   )
   if [[ "${XCODE_QUIET}" == "yes" ]] ; then
     XCODEBUILD_TEST_BASE_OSX+=( -quiet )
@@ -331,10 +314,8 @@
 
 if [[ "${DO_OBJC_CONFORMANCE_TESTS}" == "yes" ]] ; then
   header "Running ObjC Conformance Tests"
-  time ${BazelBin} test //objectivec:conformance_test $BazelFlags
+  ${BazelBin} test //objectivec:conformance_test $BazelFlags
 fi
 
 echo ""
 echo "$(basename "${0}"): Success!"
-
-echo "::endgroup::"
diff --git a/objectivec/DevTools/sources_pddm_expansion_test.sh b/objectivec/DevTools/sources_pddm_expansion_test.sh
new file mode 100755
index 0000000..99e01a1
--- /dev/null
+++ b/objectivec/DevTools/sources_pddm_expansion_test.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+${TEST_SRCDIR}/google3/third_party/protobuf/objectivec/pddm \
+  --dry-run \
+  ${TEST_SRCDIR}/google3/third_party/protobuf/objectivec/*.[hm] \
+  ${TEST_SRCDIR}/google3/third_party/protobuf/objectivec/Tests/*.[hm] \
+  || die "Update by running: objectivec/DevTools/pddm.py objectivec/*.[hm] objectivec/Tests/*.[hm]"
+
+echo "PASS"
diff --git a/objectivec/GPBCodedInputStream.m b/objectivec/GPBCodedInputStream.m
index ad5c75e..c8464a2 100644
--- a/objectivec/GPBCodedInputStream.m
+++ b/objectivec/GPBCodedInputStream.m
@@ -65,12 +65,24 @@
                          userInfo:exceptionInfo] raise];
 }
 
-static void CheckRecursionLimit(GPBCodedInputStreamState *state) {
+GPB_INLINE void CheckRecursionLimit(GPBCodedInputStreamState *state) {
   if (state->recursionDepth >= kDefaultRecursionLimit) {
     RaiseException(GPBCodedInputStreamErrorRecursionDepthExceeded, nil);
   }
 }
 
+GPB_INLINE void CheckFieldSize(uint64_t size) {
+  // Bytes and Strings have a max size of 2GB. And since messages are on the wire as bytes/length
+  // delimited, they also have a 2GB size limit. The C++ does the same sort of enforcement (see
+  // parse_context, delimited_message_util, message_lite, etc.).
+  // https://protobuf.dev/programming-guides/encoding/#cheat-sheet
+  if (size > 0x7fffffff) {
+    // TODO(thomasvl): Maybe a different error code for this, but adding one is a breaking
+    // change so reuse an existing one.
+    RaiseException(GPBCodedInputStreamErrorInvalidSize, nil);
+  }
+}
+
 static void CheckSize(GPBCodedInputStreamState *state, size_t size) {
   size_t newSize = state->bufferPos + size;
   if (newSize > state->bufferSize) {
@@ -223,14 +235,16 @@
 }
 
 NSString *GPBCodedInputStreamReadRetainedString(GPBCodedInputStreamState *state) {
-  int32_t size = ReadRawVarint32(state);
+  uint64_t size = GPBCodedInputStreamReadUInt64(state);
+  CheckFieldSize(size);
+  NSUInteger ns_size = (NSUInteger)size;
   NSString *result;
   if (size == 0) {
     result = @"";
   } else {
     CheckSize(state, size);
     result = [[NSString alloc] initWithBytes:&state->bytes[state->bufferPos]
-                                      length:size
+                                      length:ns_size
                                     encoding:NSUTF8StringEncoding];
     state->bufferPos += size;
     if (!result) {
@@ -246,21 +260,23 @@
 }
 
 NSData *GPBCodedInputStreamReadRetainedBytes(GPBCodedInputStreamState *state) {
-  int32_t size = ReadRawVarint32(state);
-  if (size < 0) return nil;
+  uint64_t size = GPBCodedInputStreamReadUInt64(state);
+  CheckFieldSize(size);
+  NSUInteger ns_size = (NSUInteger)size;
   CheckSize(state, size);
-  NSData *result = [[NSData alloc] initWithBytes:state->bytes + state->bufferPos length:size];
+  NSData *result = [[NSData alloc] initWithBytes:state->bytes + state->bufferPos length:ns_size];
   state->bufferPos += size;
   return result;
 }
 
 NSData *GPBCodedInputStreamReadRetainedBytesNoCopy(GPBCodedInputStreamState *state) {
-  int32_t size = ReadRawVarint32(state);
-  if (size < 0) return nil;
+  uint64_t size = GPBCodedInputStreamReadUInt64(state);
+  CheckFieldSize(size);
+  NSUInteger ns_size = (NSUInteger)size;
   CheckSize(state, size);
   // Cast is safe because freeWhenDone is NO.
   NSData *result = [[NSData alloc] initWithBytesNoCopy:(void *)(state->bytes + state->bufferPos)
-                                                length:size
+                                                length:ns_size
                                           freeWhenDone:NO];
   state->bufferPos += size;
   return result;
@@ -342,9 +358,12 @@
     case GPBWireFormatFixed64:
       SkipRawData(&state_, sizeof(int64_t));
       return YES;
-    case GPBWireFormatLengthDelimited:
-      SkipRawData(&state_, ReadRawVarint32(&state_));
+    case GPBWireFormatLengthDelimited: {
+      uint64_t size = GPBCodedInputStreamReadUInt64(&state_);
+      CheckFieldSize(size);
+      SkipRawData(&state_, size);
       return YES;
+    }
     case GPBWireFormatStartGroup:
       [self skipMessage];
       GPBCodedInputStreamCheckLastTagWas(
@@ -443,7 +462,8 @@
 - (void)readMessage:(GPBMessage *)message
     extensionRegistry:(id<GPBExtensionRegistry>)extensionRegistry {
   CheckRecursionLimit(&state_);
-  int32_t length = ReadRawVarint32(&state_);
+  uint64_t length = GPBCodedInputStreamReadUInt64(&state_);
+  CheckFieldSize(length);
   size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length);
   ++state_.recursionDepth;
   [message mergeFromCodedInputStream:self extensionRegistry:extensionRegistry];
@@ -457,7 +477,8 @@
                 field:(GPBFieldDescriptor *)field
         parentMessage:(GPBMessage *)parentMessage {
   CheckRecursionLimit(&state_);
-  int32_t length = ReadRawVarint32(&state_);
+  uint64_t length = GPBCodedInputStreamReadUInt64(&state_);
+  CheckFieldSize(length);
   size_t oldLimit = GPBCodedInputStreamPushLimit(&state_, length);
   ++state_.recursionDepth;
   GPBDictionaryReadEntry(mapDictionary, self, extensionRegistry, field, parentMessage);
diff --git a/objectivec/GPBCodedOutputStream.h b/objectivec/GPBCodedOutputStream.h
index 42eac77..4969e5a 100644
--- a/objectivec/GPBCodedOutputStream.h
+++ b/objectivec/GPBCodedOutputStream.h
@@ -110,6 +110,11 @@
 - (void)flush;
 
 /**
+ * @return The number of bytes written out. Includes bytes not yet flused.
+ **/
+- (size_t)bytesWritten;
+
+/**
  * Write the raw byte out.
  *
  * @param value The value to write out.
diff --git a/objectivec/GPBCodedOutputStream.m b/objectivec/GPBCodedOutputStream.m
index c21e7d2..da62715 100644
--- a/objectivec/GPBCodedOutputStream.m
+++ b/objectivec/GPBCodedOutputStream.m
@@ -48,6 +48,7 @@
   uint8_t *bytes;
   size_t size;
   size_t position;
+  size_t bytesFlushed;
   NSOutputStream *output;
 } GPBOutputBufferState;
 
@@ -71,6 +72,7 @@
     if (written != (NSInteger)state->position) {
       [NSException raise:GPBCodedOutputStreamException_WriteFailed format:@""];
     }
+    state->bytesFlushed += written;
     state->position = 0;
   }
 }
@@ -192,6 +194,13 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wdirect-ivar-access"
 
+- (size_t)bytesWritten {
+  // Could use NSStreamFileCurrentOffsetKey on state_.output if there is a stream, that could be
+  // expensive, manually tracking what is flush keeps things faster so message serialization can
+  // check it.
+  return state_.bytesFlushed + state_.position;
+}
+
 - (void)writeDoubleNoTag:(double)value {
   GPBWriteRawLittleEndian64(&state_, GPBConvertDoubleToInt64(value));
 }
@@ -886,6 +895,7 @@
       if (written != (NSInteger)length) {
         [NSException raise:GPBCodedOutputStreamException_WriteFailed format:@""];
       }
+      state_.bytesFlushed += written;
     }
   }
 }
diff --git a/objectivec/GPBMessage.h b/objectivec/GPBMessage.h
index 5b7ce0c..5d55df1 100644
--- a/objectivec/GPBMessage.h
+++ b/objectivec/GPBMessage.h
@@ -61,6 +61,12 @@
  **/
 extern NSString *const GPBErrorReasonKey;
 
+/**
+ * An exception name raised during serialization when the message would be
+ * larger than the 2GB limit.
+ **/
+extern NSString *const GPBMessageExceptionMessageTooLarge;
+
 CF_EXTERN_C_END
 
 /**
@@ -276,7 +282,25 @@
  *                                         unsuccessful.
  **/
 - (void)mergeFromData:(NSData *)data
-    extensionRegistry:(nullable id<GPBExtensionRegistry>)extensionRegistry;
+    extensionRegistry:(nullable id<GPBExtensionRegistry>)extensionRegistry
+    __attribute__((deprecated(
+        "Use -mergeFromData:extensionRegistry:error: instead, especaily if calling from Swift.")));
+
+/**
+ * Parses the given data as this message's class, and merges those values into
+ * this message.
+ *
+ * @param data              The binary representation of the message to merge.
+ * @param extensionRegistry The extension registry to use to look up extensions.
+ * @param errorPtr          An optional error pointer to fill in with a failure
+ *                          reason if the data can not be parsed. Will only be
+ *                          filled in if the data failed to be parsed.
+ *
+ * @return Boolean indicating success. errorPtr will only be fill in on failure.
+ **/
+- (BOOL)mergeFromData:(NSData *)data
+    extensionRegistry:(nullable id<GPBExtensionRegistry>)extensionRegistry
+                error:(NSError **)errorPtr;
 
 /**
  * Merges the fields from another message (of the same type) into this
@@ -293,6 +317,10 @@
  *
  * @note This can raise the GPBCodedOutputStreamException_* exceptions.
  *
+ * @note The most common cause of this failing is from one thread calling this
+ *       while another thread has a reference to this message or a message used
+ *       within a field and that other thread mutating the message while this
+ *       serialization is taking place.
  **/
 - (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output;
 
@@ -302,6 +330,11 @@
  * @param output The output stream into which to write the message.
  *
  * @note This can raise the GPBCodedOutputStreamException_* exceptions.
+ *
+ * @note The most common cause of this failing is from one thread calling this
+ *       while another thread has a reference to this message or a message used
+ *       within a field and that other thread mutating the message while this
+ *       serialization is taking place.
  **/
 - (void)writeToOutputStream:(NSOutputStream *)output;
 
@@ -312,6 +345,11 @@
  * @param output The coded output stream into which to write the message.
  *
  * @note This can raise the GPBCodedOutputStreamException_* exceptions.
+ *
+ * @note The most common cause of this failing is from one thread calling this
+ *       while another thread has a reference to this message or a message used
+ *       within a field and that other thread mutating the message while this
+ *       serialization is taking place.
  **/
 - (void)writeDelimitedToCodedOutputStream:(GPBCodedOutputStream *)output;
 
@@ -322,6 +360,11 @@
  * @param output The output stream into which to write the message.
  *
  * @note This can raise the GPBCodedOutputStreamException_* exceptions.
+ *
+ * @note The most common cause of this failing is from one thread calling this
+ *       while another thread has a reference to this message or a message used
+ *       within a field and that other thread mutating the message while this
+ *       serialization is taking place.
  **/
 - (void)writeDelimitedToOutputStream:(NSOutputStream *)output;
 
@@ -336,6 +379,11 @@
  * @note In DEBUG ONLY, the message is also checked for all required field,
  *       if one is missing, nil will be returned.
  *
+ * @note The most common cause of this failing is from one thread calling this
+ *       while another thread has a reference to this message or a message used
+ *       within a field and that other thread mutating the message while this
+ *       serialization is taking place.
+ *
  * @return The binary representation of the message.
  **/
 - (nullable NSData *)data;
@@ -347,6 +395,11 @@
  * @note This value is not cached, so if you are using it repeatedly, it is
  *       recommended to keep a local copy.
  *
+ * @note The most common cause of this failing is from one thread calling this
+ *       while another thread has a reference to this message or a message used
+ *       within a field and that other thread mutating the message while this
+ *       serialization is taking place.
+ *
  * @return The binary representation of the size along with the message.
  **/
 - (NSData *)delimitedData;
diff --git a/objectivec/GPBMessage.m b/objectivec/GPBMessage.m
index 3342179..a6bedeb 100644
--- a/objectivec/GPBMessage.m
+++ b/objectivec/GPBMessage.m
@@ -28,6 +28,7 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+#import <Foundation/Foundation.h>
 #import "GPBMessage_PackagePrivate.h"
 
 #import <objc/message.h>
@@ -46,6 +47,12 @@
 #import "GPBUnknownFieldSet_PackagePrivate.h"
 #import "GPBUtilities_PackagePrivate.h"
 
+// Returns a new instance that was automatically created by |autocreator| for
+// its field |field|.
+static GPBMessage *GPBCreateMessageWithAutocreator(Class msgClass, GPBMessage *autocreator,
+                                                   GPBFieldDescriptor *field)
+    __attribute__((ns_returns_retained));
+
 // Direct access is use for speed, to avoid even internally declaring things
 // read/write, etc. The warning is enabled in the project to ensure code calling
 // protos can turn on -Wdirect-ivar-access without issues.
@@ -58,6 +65,16 @@
 
 static NSString *const kGPBDataCoderKey = @"GPBData";
 
+// Length-delimited has a max size of 2GB, and thus messages do also.
+// src/google/protobuf/message_lite also does this enforcement on the C++ side. Validation for
+// parsing is done with GPBCodedInputStream; but for messages, it is less checks to do it within
+// the message side since the input stream code calls these same bottlenecks.
+// https://protobuf.dev/programming-guides/encoding/#cheat-sheet
+static const size_t kMaximumMessageSize = 0x7fffffff;
+
+NSString *const GPBMessageExceptionMessageTooLarge =
+    GPBNSStringifySymbol(GPBMessageExceptionMessageTooLarge);
+
 //
 // PLEASE REMEMBER:
 //
@@ -110,7 +127,7 @@
     __attribute__((ns_returns_retained));
 static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self);
 
-#ifdef DEBUG
+#if defined(DEBUG) && DEBUG
 static NSError *MessageError(NSInteger code, NSDictionary *userInfo) {
   return [NSError errorWithDomain:GPBMessageErrorDomain code:code userInfo:userInfo];
 }
@@ -638,52 +655,93 @@
 
 #endif  // !defined(__clang_analyzer__)
 
-static id NewSingleValueFromInputStream(GPBExtensionDescriptor *extension,
-                                        GPBMessage *messageToGetExtension,
-                                        GPBCodedInputStream *input,
-                                        id<GPBExtensionRegistry> extensionRegistry,
-                                        GPBMessage *existingValue)
-    __attribute__((ns_returns_retained));
-
-// Note that this returns a retained value intentionally.
-static id NewSingleValueFromInputStream(GPBExtensionDescriptor *extension,
-                                        GPBMessage *messageToGetExtension,
-                                        GPBCodedInputStream *input,
-                                        id<GPBExtensionRegistry> extensionRegistry,
-                                        GPBMessage *existingValue) {
+static void DecodeSingleValueFromInputStream(GPBExtensionDescriptor *extension,
+                                             GPBMessage *messageToGetExtension,
+                                             GPBCodedInputStream *input,
+                                             id<GPBExtensionRegistry> extensionRegistry,
+                                             BOOL isRepeated, GPBMessage *targetMessage) {
   GPBExtensionDescription *description = extension->description_;
+#if defined(DEBUG) && DEBUG && !defined(NS_BLOCK_ASSERTIONS)
+  if (GPBDataTypeIsMessage(description->dataType)) {
+    NSCAssert(targetMessage != nil, @"Internal error: must have a target message");
+  } else {
+    NSCAssert(targetMessage == nil, @"Internal error: should not have a target message");
+  }
+#endif
   GPBCodedInputStreamState *state = &input->state_;
+  id nsValue;
   switch (description->dataType) {
-    case GPBDataTypeBool:
-      return [[NSNumber alloc] initWithBool:GPBCodedInputStreamReadBool(state)];
-    case GPBDataTypeFixed32:
-      return [[NSNumber alloc] initWithUnsignedInt:GPBCodedInputStreamReadFixed32(state)];
-    case GPBDataTypeSFixed32:
-      return [[NSNumber alloc] initWithInt:GPBCodedInputStreamReadSFixed32(state)];
-    case GPBDataTypeFloat:
-      return [[NSNumber alloc] initWithFloat:GPBCodedInputStreamReadFloat(state)];
-    case GPBDataTypeFixed64:
-      return [[NSNumber alloc] initWithUnsignedLongLong:GPBCodedInputStreamReadFixed64(state)];
-    case GPBDataTypeSFixed64:
-      return [[NSNumber alloc] initWithLongLong:GPBCodedInputStreamReadSFixed64(state)];
-    case GPBDataTypeDouble:
-      return [[NSNumber alloc] initWithDouble:GPBCodedInputStreamReadDouble(state)];
-    case GPBDataTypeInt32:
-      return [[NSNumber alloc] initWithInt:GPBCodedInputStreamReadInt32(state)];
-    case GPBDataTypeInt64:
-      return [[NSNumber alloc] initWithLongLong:GPBCodedInputStreamReadInt64(state)];
-    case GPBDataTypeSInt32:
-      return [[NSNumber alloc] initWithInt:GPBCodedInputStreamReadSInt32(state)];
-    case GPBDataTypeSInt64:
-      return [[NSNumber alloc] initWithLongLong:GPBCodedInputStreamReadSInt64(state)];
-    case GPBDataTypeUInt32:
-      return [[NSNumber alloc] initWithUnsignedInt:GPBCodedInputStreamReadUInt32(state)];
-    case GPBDataTypeUInt64:
-      return [[NSNumber alloc] initWithUnsignedLongLong:GPBCodedInputStreamReadUInt64(state)];
+    case GPBDataTypeBool: {
+      BOOL value = GPBCodedInputStreamReadBool(state);
+      nsValue = [[NSNumber alloc] initWithBool:value];
+      break;
+    }
+    case GPBDataTypeFixed32: {
+      uint32_t value = GPBCodedInputStreamReadFixed32(state);
+      nsValue = [[NSNumber alloc] initWithUnsignedInt:value];
+      break;
+    }
+    case GPBDataTypeSFixed32: {
+      int32_t value = GPBCodedInputStreamReadSFixed32(state);
+      nsValue = [[NSNumber alloc] initWithInt:value];
+      break;
+    }
+    case GPBDataTypeFloat: {
+      float value = GPBCodedInputStreamReadFloat(state);
+      nsValue = [[NSNumber alloc] initWithFloat:value];
+      break;
+    }
+    case GPBDataTypeFixed64: {
+      uint64_t value = GPBCodedInputStreamReadFixed64(state);
+      nsValue = [[NSNumber alloc] initWithUnsignedLongLong:value];
+      break;
+    }
+    case GPBDataTypeSFixed64: {
+      int64_t value = GPBCodedInputStreamReadSFixed64(state);
+      nsValue = [[NSNumber alloc] initWithLongLong:value];
+      break;
+    }
+    case GPBDataTypeDouble: {
+      double value = GPBCodedInputStreamReadDouble(state);
+      nsValue = [[NSNumber alloc] initWithDouble:value];
+      break;
+    }
+    case GPBDataTypeInt32: {
+      int32_t value = GPBCodedInputStreamReadInt32(state);
+      nsValue = [[NSNumber alloc] initWithInt:value];
+      break;
+    }
+    case GPBDataTypeInt64: {
+      int64_t value = GPBCodedInputStreamReadInt64(state);
+      nsValue = [[NSNumber alloc] initWithLongLong:value];
+      break;
+    }
+    case GPBDataTypeSInt32: {
+      int32_t value = GPBCodedInputStreamReadSInt32(state);
+      nsValue = [[NSNumber alloc] initWithInt:value];
+      break;
+    }
+    case GPBDataTypeSInt64: {
+      int64_t value = GPBCodedInputStreamReadSInt64(state);
+      nsValue = [[NSNumber alloc] initWithLongLong:value];
+      break;
+    }
+    case GPBDataTypeUInt32: {
+      uint32_t value = GPBCodedInputStreamReadUInt32(state);
+      nsValue = [[NSNumber alloc] initWithUnsignedInt:value];
+      break;
+    }
+    case GPBDataTypeUInt64: {
+      uint64_t value = GPBCodedInputStreamReadUInt64(state);
+      nsValue = [[NSNumber alloc] initWithUnsignedLongLong:value];
+      break;
+    }
     case GPBDataTypeBytes:
-      return GPBCodedInputStreamReadRetainedBytes(state);
+      nsValue = GPBCodedInputStreamReadRetainedBytes(state);
+      break;
     case GPBDataTypeString:
-      return GPBCodedInputStreamReadRetainedString(state);
+      nsValue = GPBCodedInputStreamReadRetainedString(state);
+      break;
     case GPBDataTypeEnum: {
       int32_t val = GPBCodedInputStreamReadEnum(&input->state_);
       GPBEnumDescriptor *enumDescriptor = extension.enumDescriptor;
@@ -691,43 +749,44 @@
       // will be considers not closed, so casing to the enum type for a switch
       // could cause things to fall off the end of a switch.
       if (!enumDescriptor.isClosed || enumDescriptor.enumVerifier(val)) {
-        return [[NSNumber alloc] initWithInt:val];
+        nsValue = [[NSNumber alloc] initWithInt:val];
       } else {
         GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(messageToGetExtension);
         [unknownFields mergeVarintField:extension->description_->fieldNumber value:val];
-        return nil;
+        nsValue = nil;
       }
+      break;
     }
     case GPBDataTypeGroup:
     case GPBDataTypeMessage: {
-      GPBMessage *message;
-      if (existingValue) {
-        message = [existingValue retain];
-      } else {
-        GPBDescriptor *descriptor = [extension.msgClass descriptor];
-        message = [[descriptor.messageClass alloc] init];
-      }
-
       if (description->dataType == GPBDataTypeGroup) {
         [input readGroup:description->fieldNumber
-                      message:message
+                      message:targetMessage
             extensionRegistry:extensionRegistry];
       } else {
         // description->dataType == GPBDataTypeMessage
         if (GPBExtensionIsWireFormat(description)) {
           // For MessageSet fields the message length will have already been
           // read.
-          [message mergeFromCodedInputStream:input extensionRegistry:extensionRegistry];
+          [targetMessage mergeFromCodedInputStream:input extensionRegistry:extensionRegistry];
         } else {
-          [input readMessage:message extensionRegistry:extensionRegistry];
+          [input readMessage:targetMessage extensionRegistry:extensionRegistry];
         }
       }
-
-      return message;
+      // Nothing to add below since the caller provided the message (and added it).
+      nsValue = nil;
+      break;
     }
-  }
+  }  // switch
 
-  return nil;
+  if (nsValue) {
+    if (isRepeated) {
+      [messageToGetExtension addExtension:extension value:nsValue];
+    } else {
+      [messageToGetExtension setExtension:extension value:nsValue];
+    }
+    [nsValue release];
+  }
 }
 
 static void ExtensionMergeFromInputStream(GPBExtensionDescriptor *extension, BOOL isPackedOnStream,
@@ -737,38 +796,45 @@
   GPBExtensionDescription *description = extension->description_;
   GPBCodedInputStreamState *state = &input->state_;
   if (isPackedOnStream) {
+#if defined(DEBUG) && DEBUG && !defined(NS_BLOCK_ASSERTIONS)
     NSCAssert(GPBExtensionIsRepeated(description), @"How was it packed if it isn't repeated?");
+#endif
     int32_t length = GPBCodedInputStreamReadInt32(state);
     size_t limit = GPBCodedInputStreamPushLimit(state, length);
     while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
-      id value = NewSingleValueFromInputStream(extension, message, input, extensionRegistry, nil);
-      if (value) {
-        [message addExtension:extension value:value];
-        [value release];
-      }
+      DecodeSingleValueFromInputStream(extension, message, input, extensionRegistry,
+                                       /*isRepeated=*/YES, nil);
     }
     GPBCodedInputStreamPopLimit(state, limit);
   } else {
-    id existingValue = nil;
     BOOL isRepeated = GPBExtensionIsRepeated(description);
-    if (!isRepeated && GPBDataTypeIsMessage(description->dataType)) {
-      existingValue = [message getExistingExtension:extension];
-    }
-    id value =
-        NewSingleValueFromInputStream(extension, message, input, extensionRegistry, existingValue);
-    if (value) {
+    GPBMessage *targetMessage = nil;
+    if (GPBDataTypeIsMessage(description->dataType)) {
+      // For messages/groups create the targetMessage out here and add it to the objects graph in
+      // advance, that way if DecodeSingleValueFromInputStream() throw for a parsing issue, the
+      // object won't be leaked.
       if (isRepeated) {
-        [message addExtension:extension value:value];
+        GPBDescriptor *descriptor = [extension.msgClass descriptor];
+        targetMessage = [[descriptor.messageClass alloc] init];
+        [message addExtension:extension value:targetMessage];
+        [targetMessage release];
       } else {
-        [message setExtension:extension value:value];
+        targetMessage = [message getExistingExtension:extension];
+        if (!targetMessage) {
+          GPBDescriptor *descriptor = [extension.msgClass descriptor];
+          targetMessage = [[descriptor.messageClass alloc] init];
+          [message setExtension:extension value:targetMessage];
+          [targetMessage release];
+        }
       }
-      [value release];
     }
+    DecodeSingleValueFromInputStream(extension, message, input, extensionRegistry, isRepeated,
+                                     targetMessage);
   }
 }
 
-GPBMessage *GPBCreateMessageWithAutocreator(Class msgClass, GPBMessage *autocreator,
-                                            GPBFieldDescriptor *field) {
+static GPBMessage *GPBCreateMessageWithAutocreator(Class msgClass, GPBMessage *autocreator,
+                                                   GPBFieldDescriptor *field) {
   GPBMessage *message = [[msgClass alloc] init];
   message->autocreator_ = autocreator;
   message->autocreatorField_ = [field retain];
@@ -964,27 +1030,18 @@
            extensionRegistry:(id<GPBExtensionRegistry>)extensionRegistry
                        error:(NSError **)errorPtr {
   if ((self = [self init])) {
-    @try {
-      [self mergeFromData:data extensionRegistry:extensionRegistry];
-      if (errorPtr) {
-        *errorPtr = nil;
-      }
-    } @catch (NSException *exception) {
+    if (![self mergeFromData:data extensionRegistry:extensionRegistry error:errorPtr]) {
       [self release];
       self = nil;
-      if (errorPtr) {
-        *errorPtr = ErrorFromException(exception);
-      }
-    }
-#ifdef DEBUG
-    if (self && !self.initialized) {
+#if defined(DEBUG) && DEBUG
+    } else if (!self.initialized) {
       [self release];
       self = nil;
       if (errorPtr) {
         *errorPtr = MessageError(GPBMessageErrorCodeMissingRequiredField, nil);
       }
-    }
 #endif
+    }
   }
   return self;
 }
@@ -1005,7 +1062,7 @@
         *errorPtr = ErrorFromException(exception);
       }
     }
-#ifdef DEBUG
+#if defined(DEBUG) && DEBUG
     if (self && !self.initialized) {
       [self release];
       self = nil;
@@ -1298,24 +1355,34 @@
 }
 
 - (NSData *)data {
-#ifdef DEBUG
+#if defined(DEBUG) && DEBUG
   if (!self.initialized) {
     return nil;
   }
 #endif
-  NSMutableData *data = [NSMutableData dataWithLength:[self serializedSize]];
+  size_t expectedSize = [self serializedSize];
+  if (expectedSize > kMaximumMessageSize) {
+    return nil;
+  }
+  NSMutableData *data = [NSMutableData dataWithLength:expectedSize];
   GPBCodedOutputStream *stream = [[GPBCodedOutputStream alloc] initWithData:data];
   @try {
     [self writeToCodedOutputStream:stream];
   } @catch (NSException *exception) {
-    // This really shouldn't happen. The only way writeToCodedOutputStream:
-    // could throw is if something in the library has a bug and the
-    // serializedSize was wrong.
-#ifdef DEBUG
+    // This really shouldn't happen. Normally, this could mean there was a bug in the library and it
+    // failed to match between computing the size and writing out the bytes. However, the more
+    // common cause is while one thread was writing out the data, some other thread had a reference
+    // to this message or a message used as a nested field, and that other thread mutated that
+    // message, causing the pre computed serializedSize to no longer match the final size after
+    // serialization. It is not safe to mutate a message while accessing it from another thread.
+#if defined(DEBUG) && DEBUG
     NSLog(@"%@: Internal exception while building message data: %@", [self class], exception);
 #endif
     data = nil;
   }
+#if defined(DEBUG) && DEBUG
+  NSAssert(!data || [stream bytesWritten] == expectedSize, @"Internal error within the library");
+#endif
   [stream release];
   return data;
 }
@@ -1328,10 +1395,13 @@
   @try {
     [self writeDelimitedToCodedOutputStream:stream];
   } @catch (NSException *exception) {
-    // This really shouldn't happen.  The only way writeToCodedOutputStream:
-    // could throw is if something in the library has a bug and the
-    // serializedSize was wrong.
-#ifdef DEBUG
+    // This really shouldn't happen. Normally, this could mean there was a bug in the library and it
+    // failed to match between computing the size and writing out the bytes. However, the more
+    // common cause is while one thread was writing out the data, some other thread had a reference
+    // to this message or a message used as a nested field, and that other thread mutated that
+    // message, causing the pre computed serializedSize to no longer match the final size after
+    // serialization. It is not safe to mutate a message while accessing it from another thread.
+#if defined(DEBUG) && DEBUG
     NSLog(@"%@: Internal exception while building message delimitedData: %@", [self class],
           exception);
 #endif
@@ -1344,8 +1414,16 @@
 
 - (void)writeToOutputStream:(NSOutputStream *)output {
   GPBCodedOutputStream *stream = [[GPBCodedOutputStream alloc] initWithOutputStream:output];
-  [self writeToCodedOutputStream:stream];
-  [stream release];
+  @try {
+    [self writeToCodedOutputStream:stream];
+    size_t bytesWritten = [stream bytesWritten];
+    if (bytesWritten > kMaximumMessageSize) {
+      [NSException raise:GPBMessageExceptionMessageTooLarge
+                  format:@"Message would have been %zu bytes", bytesWritten];
+    }
+  } @finally {
+    [stream release];
+  }
 }
 
 - (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output {
@@ -1379,13 +1457,28 @@
 
 - (void)writeDelimitedToOutputStream:(NSOutputStream *)output {
   GPBCodedOutputStream *codedOutput = [[GPBCodedOutputStream alloc] initWithOutputStream:output];
-  [self writeDelimitedToCodedOutputStream:codedOutput];
-  [codedOutput release];
+  @try {
+    [self writeDelimitedToCodedOutputStream:codedOutput];
+  } @finally {
+    [codedOutput release];
+  }
 }
 
 - (void)writeDelimitedToCodedOutputStream:(GPBCodedOutputStream *)output {
-  [output writeRawVarintSizeTAs32:[self serializedSize]];
+  size_t expectedSize = [self serializedSize];
+  if (expectedSize > kMaximumMessageSize) {
+    [NSException raise:GPBMessageExceptionMessageTooLarge
+                format:@"Message would have been %zu bytes", expectedSize];
+  }
+  [output writeRawVarintSizeTAs32:expectedSize];
+#if defined(DEBUG) && DEBUG && !defined(NS_BLOCK_ASSERTIONS)
+  size_t initialSize = [output bytesWritten];
+#endif
   [self writeToCodedOutputStream:output];
+#if defined(DEBUG) && DEBUG && !defined(NS_BLOCK_ASSERTIONS)
+  NSAssert(([output bytesWritten] - initialSize) == expectedSize,
+           @"Internal error within the library");
+#endif
 }
 
 - (void)writeField:(GPBFieldDescriptor *)field toCodedOutputStream:(GPBCodedOutputStream *)output {
@@ -1821,7 +1914,7 @@
 
 //%PDDM-EXPAND-END (18 expansions)
 
-// clang-format off
+      // clang-format on
   }
 }
 
@@ -1848,8 +1941,7 @@
   value = [autocreatedExtensionMap_ objectForKey:extension];
   if (!value) {
     // Auto create the message extensions to match normal fields.
-    value = CreateMessageWithAutocreatorForExtension(extension.msgClass, self,
-                                                     extension);
+    value = CreateMessageWithAutocreatorForExtension(extension.msgClass, self, extension);
 
     if (autocreatedExtensionMap_ == nil) {
       autocreatedExtensionMap_ = [[NSMutableDictionary alloc] init];
@@ -1926,8 +2018,7 @@
   GPBExtensionDescriptor *descriptor = extension;
 
   if (GPBExtensionIsMessage(descriptor) && !descriptor.isRepeated) {
-    GPBMessage *autocreatedValue =
-        [[autocreatedExtensionMap_ objectForKey:extension] retain];
+    GPBMessage *autocreatedValue = [[autocreatedExtensionMap_ objectForKey:extension] retain];
     // Must remove from the map before calling GPBClearMessageAutocreator() so
     // that GPBClearMessageAutocreator() knows its safe to clear.
     [autocreatedExtensionMap_ removeObjectForKey:extension];
@@ -1959,9 +2050,7 @@
   GPBBecomeVisibleToAutocreator(self);
 }
 
-- (void)setExtension:(GPBExtensionDescriptor *)extension
-               index:(NSUInteger)idx
-               value:(id)value {
+- (void)setExtension:(GPBExtensionDescriptor *)extension index:(NSUInteger)idx value:(id)value {
   CheckExtension(self, extension);
 
   if (!extension.repeated) {
@@ -1991,28 +2080,35 @@
 
 #pragma mark - mergeFrom
 
-- (void)mergeFromData:(NSData *)data
-    extensionRegistry:(id<GPBExtensionRegistry>)extensionRegistry {
+- (void)mergeFromData:(NSData *)data extensionRegistry:(id<GPBExtensionRegistry>)extensionRegistry {
   GPBCodedInputStream *input = [[GPBCodedInputStream alloc] initWithData:data];
-  [self mergeFromCodedInputStream:input extensionRegistry:extensionRegistry];
-  [input checkLastTagWas:0];
-  [input release];
+  @try {
+    [self mergeFromCodedInputStream:input extensionRegistry:extensionRegistry];
+    [input checkLastTagWas:0];
+  } @finally {
+    [input release];
+  }
 }
 
-#pragma mark - mergeDelimitedFrom
-
-- (void)mergeDelimitedFromCodedInputStream:(GPBCodedInputStream *)input
-                         extensionRegistry:(id<GPBExtensionRegistry>)extensionRegistry {
-  GPBCodedInputStreamState *state = &input->state_;
-  if (GPBCodedInputStreamIsAtEnd(state)) {
-    return;
+- (BOOL)mergeFromData:(NSData *)data
+    extensionRegistry:(nullable id<GPBExtensionRegistry>)extensionRegistry
+                error:(NSError **)errorPtr {
+  GPBCodedInputStream *input = [[GPBCodedInputStream alloc] initWithData:data];
+  @try {
+    [self mergeFromCodedInputStream:input extensionRegistry:extensionRegistry];
+    [input checkLastTagWas:0];
+    if (errorPtr) {
+      *errorPtr = nil;
+    }
+  } @catch (NSException *exception) {
+    [input release];
+    if (errorPtr) {
+      *errorPtr = ErrorFromException(exception);
+    }
+    return NO;
   }
-  NSData *data = GPBCodedInputStreamReadRetainedBytesNoCopy(state);
-  if (data == nil) {
-    return;
-  }
-  [self mergeFromData:data extensionRegistry:extensionRegistry];
-  [data release];
+  [input release];
+  return YES;
 }
 
 #pragma mark - Parse From Data Support
@@ -2024,49 +2120,53 @@
 + (instancetype)parseFromData:(NSData *)data
             extensionRegistry:(id<GPBExtensionRegistry>)extensionRegistry
                         error:(NSError **)errorPtr {
-  return [[[self alloc] initWithData:data
-                   extensionRegistry:extensionRegistry
+  return [[[self alloc] initWithData:data extensionRegistry:extensionRegistry
                                error:errorPtr] autorelease];
 }
 
 + (instancetype)parseFromCodedInputStream:(GPBCodedInputStream *)input
                         extensionRegistry:(id<GPBExtensionRegistry>)extensionRegistry
                                     error:(NSError **)errorPtr {
-  return
-      [[[self alloc] initWithCodedInputStream:input
-                            extensionRegistry:extensionRegistry
-                                        error:errorPtr] autorelease];
+  return [[[self alloc] initWithCodedInputStream:input
+                               extensionRegistry:extensionRegistry
+                                           error:errorPtr] autorelease];
 }
 
 #pragma mark - Parse Delimited From Data Support
 
 + (instancetype)parseDelimitedFromCodedInputStream:(GPBCodedInputStream *)input
-                                 extensionRegistry:
-                                     (id<GPBExtensionRegistry>)extensionRegistry
+                                 extensionRegistry:(id<GPBExtensionRegistry>)extensionRegistry
                                              error:(NSError **)errorPtr {
-  GPBMessage *message = [[[self alloc] init] autorelease];
-  @try {
-    [message mergeDelimitedFromCodedInputStream:input
-                              extensionRegistry:extensionRegistry];
-    if (errorPtr) {
-      *errorPtr = nil;
-    }
+  GPBCodedInputStreamState *state = &input->state_;
+  // This doesn't completely match the C++, but if the stream has nothing, just make an empty
+  // message.
+  if (GPBCodedInputStreamIsAtEnd(state)) {
+    return [[[self alloc] init] autorelease];
   }
-  @catch (NSException *exception) {
-    message = nil;
+
+  // Manually extract the data and parse it. If we read a varint and push a limit, that consumes
+  // some of the recursion buffer which isn't correct, it also can result in a change in error
+  // codes for attempts to parse partial data; and there are projects sensitive to that, so this
+  // maintains existing error flows.
+
+  // Extract the data, but in a "no copy" mode since we will immediately parse it so this NSData
+  // is transient.
+  NSData *data = nil;
+  @try {
+    data = GPBCodedInputStreamReadRetainedBytesNoCopy(state);
+  } @catch (NSException *exception) {
     if (errorPtr) {
       *errorPtr = ErrorFromException(exception);
     }
+    return nil;
   }
-#ifdef DEBUG
-  if (message && !message.initialized) {
-    message = nil;
-    if (errorPtr) {
-      *errorPtr = MessageError(GPBMessageErrorCodeMissingRequiredField, nil);
-    }
+
+  GPBMessage *result = [self parseFromData:data extensionRegistry:extensionRegistry error:errorPtr];
+  [data release];
+  if (result && errorPtr) {
+    *errorPtr = nil;
   }
-#endif
-  return message;
+  return result;
 }
 
 #pragma mark - Unknown Field Support
@@ -2098,12 +2198,10 @@
     if (tag == GPBWireFormatMessageSetTypeIdTag) {
       typeId = GPBCodedInputStreamReadUInt32(state);
       if (typeId != 0) {
-        extension = [extensionRegistry extensionForDescriptor:[self descriptor]
-                                                  fieldNumber:typeId];
+        extension = [extensionRegistry extensionForDescriptor:[self descriptor] fieldNumber:typeId];
       }
     } else if (tag == GPBWireFormatMessageSetMessageTag) {
-      rawBytes =
-          [GPBCodedInputStreamReadRetainedBytesNoCopy(state) autorelease];
+      rawBytes = [GPBCodedInputStreamReadRetainedBytesNoCopy(state) autorelease];
     } else {
       if (![input skipField:tag]) {
         break;
@@ -2115,14 +2213,13 @@
 
   if (rawBytes != nil && typeId != 0) {
     if (extension != nil) {
-      GPBCodedInputStream *newInput =
-          [[GPBCodedInputStream alloc] initWithData:rawBytes];
-      ExtensionMergeFromInputStream(extension,
-                                    extension.packable,
-                                    newInput,
-                                    extensionRegistry,
-                                    self);
-      [newInput release];
+      GPBCodedInputStream *newInput = [[GPBCodedInputStream alloc] initWithData:rawBytes];
+      @try {
+        ExtensionMergeFromInputStream(extension, extension.packable, newInput, extensionRegistry,
+                                      self);
+      } @finally {
+        [newInput release];
+      }
     } else {
       GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self);
       // rawBytes was created via a NoCopy, so it can be reusing a
@@ -2142,9 +2239,8 @@
   int32_t fieldNumber = GPBWireFormatGetTagFieldNumber(tag);
 
   GPBDescriptor *descriptor = [self descriptor];
-  GPBExtensionDescriptor *extension =
-      [extensionRegistry extensionForDescriptor:descriptor
-                                    fieldNumber:fieldNumber];
+  GPBExtensionDescriptor *extension = [extensionRegistry extensionForDescriptor:descriptor
+                                                                    fieldNumber:fieldNumber];
   if (extension == nil) {
     if (descriptor.wireFormat && GPBWireFormatMessageSetItemTag == tag) {
       [self parseMessageSet:input extensionRegistry:extensionRegistry];
@@ -2152,23 +2248,14 @@
     }
   } else {
     if (extension.wireType == wireType) {
-      ExtensionMergeFromInputStream(extension,
-                                    extension.packable,
-                                    input,
-                                    extensionRegistry,
-                                    self);
+      ExtensionMergeFromInputStream(extension, extension.packable, input, extensionRegistry, self);
       return YES;
     }
     // Primitive, repeated types can be packed on unpacked on the wire, and are
     // parsed either way.
-    if ([extension isRepeated] &&
-        !GPBDataTypeIsObject(extension->description_->dataType) &&
+    if ([extension isRepeated] && !GPBDataTypeIsObject(extension->description_->dataType) &&
         (extension.alternateWireType == wireType)) {
-      ExtensionMergeFromInputStream(extension,
-                                    !extension.packable,
-                                    input,
-                                    extensionRegistry,
-                                    self);
+      ExtensionMergeFromInputStream(extension, !extension.packable, input, extensionRegistry, self);
       return YES;
     }
   }
@@ -2187,38 +2274,38 @@
 
 #pragma mark - MergeFromCodedInputStream Support
 
-static void MergeSingleFieldFromCodedInputStream(
-    GPBMessage *self, GPBFieldDescriptor *field,
-    GPBCodedInputStream *input, id<GPBExtensionRegistry>extensionRegistry) {
+static void MergeSingleFieldFromCodedInputStream(GPBMessage *self, GPBFieldDescriptor *field,
+                                                 GPBCodedInputStream *input,
+                                                 id<GPBExtensionRegistry> extensionRegistry) {
   GPBDataType fieldDataType = GPBGetFieldDataType(field);
   switch (fieldDataType) {
-#define CASE_SINGLE_POD(NAME, TYPE, FUNC_TYPE)                             \
-    case GPBDataType##NAME: {                                              \
-      TYPE val = GPBCodedInputStreamRead##NAME(&input->state_);            \
-      GPBSet##FUNC_TYPE##IvarWithFieldPrivate(self, field, val);           \
-      break;                                                               \
-            }
-#define CASE_SINGLE_OBJECT(NAME)                                           \
-    case GPBDataType##NAME: {                                              \
-      id val = GPBCodedInputStreamReadRetained##NAME(&input->state_);      \
-      GPBSetRetainedObjectIvarWithFieldPrivate(self, field, val);          \
-      break;                                                               \
-    }
-      CASE_SINGLE_POD(Bool, BOOL, Bool)
-      CASE_SINGLE_POD(Fixed32, uint32_t, UInt32)
-      CASE_SINGLE_POD(SFixed32, int32_t, Int32)
-      CASE_SINGLE_POD(Float, float, Float)
-      CASE_SINGLE_POD(Fixed64, uint64_t, UInt64)
-      CASE_SINGLE_POD(SFixed64, int64_t, Int64)
-      CASE_SINGLE_POD(Double, double, Double)
-      CASE_SINGLE_POD(Int32, int32_t, Int32)
-      CASE_SINGLE_POD(Int64, int64_t, Int64)
-      CASE_SINGLE_POD(SInt32, int32_t, Int32)
-      CASE_SINGLE_POD(SInt64, int64_t, Int64)
-      CASE_SINGLE_POD(UInt32, uint32_t, UInt32)
-      CASE_SINGLE_POD(UInt64, uint64_t, UInt64)
-      CASE_SINGLE_OBJECT(Bytes)
-      CASE_SINGLE_OBJECT(String)
+#define CASE_SINGLE_POD(NAME, TYPE, FUNC_TYPE)                 \
+  case GPBDataType##NAME: {                                    \
+    TYPE val = GPBCodedInputStreamRead##NAME(&input->state_);  \
+    GPBSet##FUNC_TYPE##IvarWithFieldPrivate(self, field, val); \
+    break;                                                     \
+  }
+#define CASE_SINGLE_OBJECT(NAME)                                    \
+  case GPBDataType##NAME: {                                         \
+    id val = GPBCodedInputStreamReadRetained##NAME(&input->state_); \
+    GPBSetRetainedObjectIvarWithFieldPrivate(self, field, val);     \
+    break;                                                          \
+  }
+    CASE_SINGLE_POD(Bool, BOOL, Bool)
+    CASE_SINGLE_POD(Fixed32, uint32_t, UInt32)
+    CASE_SINGLE_POD(SFixed32, int32_t, Int32)
+    CASE_SINGLE_POD(Float, float, Float)
+    CASE_SINGLE_POD(Fixed64, uint64_t, UInt64)
+    CASE_SINGLE_POD(SFixed64, int64_t, Int64)
+    CASE_SINGLE_POD(Double, double, Double)
+    CASE_SINGLE_POD(Int32, int32_t, Int32)
+    CASE_SINGLE_POD(Int64, int64_t, Int64)
+    CASE_SINGLE_POD(SInt32, int32_t, Int32)
+    CASE_SINGLE_POD(SInt64, int64_t, Int64)
+    CASE_SINGLE_POD(UInt32, uint32_t, UInt32)
+    CASE_SINGLE_POD(UInt64, uint64_t, UInt64)
+    CASE_SINGLE_OBJECT(Bytes)
+    CASE_SINGLE_OBJECT(String)
 #undef CASE_SINGLE_POD
 #undef CASE_SINGLE_OBJECT
 
@@ -2226,13 +2313,12 @@
       if (GPBGetHasIvarField(self, field)) {
         // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has
         // check again.
-        GPBMessage *message =
-            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        GPBMessage *message = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
         [input readMessage:message extensionRegistry:extensionRegistry];
       } else {
         GPBMessage *message = [[field.msgClass alloc] init];
-        [input readMessage:message extensionRegistry:extensionRegistry];
         GPBSetRetainedObjectIvarWithFieldPrivate(self, field, message);
+        [input readMessage:message extensionRegistry:extensionRegistry];
       }
       break;
     }
@@ -2241,17 +2327,12 @@
       if (GPBGetHasIvarField(self, field)) {
         // GPBGetObjectIvarWithFieldNoAutocreate() avoids doing the has
         // check again.
-        GPBMessage *message =
-            GPBGetObjectIvarWithFieldNoAutocreate(self, field);
-        [input readGroup:GPBFieldNumber(field)
-                      message:message
-            extensionRegistry:extensionRegistry];
+        GPBMessage *message = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+        [input readGroup:GPBFieldNumber(field) message:message extensionRegistry:extensionRegistry];
       } else {
         GPBMessage *message = [[field.msgClass alloc] init];
-        [input readGroup:GPBFieldNumber(field)
-                      message:message
-            extensionRegistry:extensionRegistry];
         GPBSetRetainedObjectIvarWithFieldPrivate(self, field, message);
+        [input readGroup:GPBFieldNumber(field) message:message extensionRegistry:extensionRegistry];
       }
       break;
     }
@@ -2268,9 +2349,9 @@
   }  // switch
 }
 
-static void MergeRepeatedPackedFieldFromCodedInputStream(
-    GPBMessage *self, GPBFieldDescriptor *field,
-    GPBCodedInputStream *input) {
+static void MergeRepeatedPackedFieldFromCodedInputStream(GPBMessage *self,
+                                                         GPBFieldDescriptor *field,
+                                                         GPBCodedInputStream *input) {
   GPBDataType fieldDataType = GPBGetFieldDataType(field);
   GPBCodedInputStreamState *state = &input->state_;
   id genericArray = GetOrCreateArrayIvarWithField(self, field);
@@ -2278,25 +2359,25 @@
   size_t limit = GPBCodedInputStreamPushLimit(state, length);
   while (GPBCodedInputStreamBytesUntilLimit(state) > 0) {
     switch (fieldDataType) {
-#define CASE_REPEATED_PACKED_POD(NAME, TYPE, ARRAY_TYPE)      \
-     case GPBDataType##NAME: {                                \
-       TYPE val = GPBCodedInputStreamRead##NAME(state);       \
-       [(GPB##ARRAY_TYPE##Array *)genericArray addValue:val]; \
-       break;                                                 \
-     }
-        CASE_REPEATED_PACKED_POD(Bool, BOOL, Bool)
-        CASE_REPEATED_PACKED_POD(Fixed32, uint32_t, UInt32)
-        CASE_REPEATED_PACKED_POD(SFixed32, int32_t, Int32)
-        CASE_REPEATED_PACKED_POD(Float, float, Float)
-        CASE_REPEATED_PACKED_POD(Fixed64, uint64_t, UInt64)
-        CASE_REPEATED_PACKED_POD(SFixed64, int64_t, Int64)
-        CASE_REPEATED_PACKED_POD(Double, double, Double)
-        CASE_REPEATED_PACKED_POD(Int32, int32_t, Int32)
-        CASE_REPEATED_PACKED_POD(Int64, int64_t, Int64)
-        CASE_REPEATED_PACKED_POD(SInt32, int32_t, Int32)
-        CASE_REPEATED_PACKED_POD(SInt64, int64_t, Int64)
-        CASE_REPEATED_PACKED_POD(UInt32, uint32_t, UInt32)
-        CASE_REPEATED_PACKED_POD(UInt64, uint64_t, UInt64)
+#define CASE_REPEATED_PACKED_POD(NAME, TYPE, ARRAY_TYPE)   \
+  case GPBDataType##NAME: {                                \
+    TYPE val = GPBCodedInputStreamRead##NAME(state);       \
+    [(GPB##ARRAY_TYPE##Array *)genericArray addValue:val]; \
+    break;                                                 \
+  }
+      CASE_REPEATED_PACKED_POD(Bool, BOOL, Bool)
+      CASE_REPEATED_PACKED_POD(Fixed32, uint32_t, UInt32)
+      CASE_REPEATED_PACKED_POD(SFixed32, int32_t, Int32)
+      CASE_REPEATED_PACKED_POD(Float, float, Float)
+      CASE_REPEATED_PACKED_POD(Fixed64, uint64_t, UInt64)
+      CASE_REPEATED_PACKED_POD(SFixed64, int64_t, Int64)
+      CASE_REPEATED_PACKED_POD(Double, double, Double)
+      CASE_REPEATED_PACKED_POD(Int32, int32_t, Int32)
+      CASE_REPEATED_PACKED_POD(Int64, int64_t, Int64)
+      CASE_REPEATED_PACKED_POD(SInt32, int32_t, Int32)
+      CASE_REPEATED_PACKED_POD(SInt64, int64_t, Int64)
+      CASE_REPEATED_PACKED_POD(UInt32, uint32_t, UInt32)
+      CASE_REPEATED_PACKED_POD(UInt64, uint64_t, UInt64)
 #undef CASE_REPEATED_PACKED_POD
 
       case GPBDataTypeBytes:
@@ -2309,7 +2390,7 @@
       case GPBDataTypeEnum: {
         int32_t val = GPBCodedInputStreamReadEnum(state);
         if (!GPBFieldIsClosedEnum(field) || [field isValidEnumValue:val]) {
-          [(GPBEnumArray*)genericArray addRawValue:val];
+          [(GPBEnumArray *)genericArray addRawValue:val];
         } else {
           GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self);
           [unknownFields mergeVarintField:GPBFieldNumber(field) value:val];
@@ -2317,66 +2398,68 @@
         break;
       }
     }  // switch
-  }  // while(BytesUntilLimit() > 0)
+  }    // while(BytesUntilLimit() > 0)
   GPBCodedInputStreamPopLimit(state, limit);
 }
 
 static void MergeRepeatedNotPackedFieldFromCodedInputStream(
-    GPBMessage *self, GPBFieldDescriptor *field,
-    GPBCodedInputStream *input, id<GPBExtensionRegistry>extensionRegistry) {
+    GPBMessage *self, GPBFieldDescriptor *field, GPBCodedInputStream *input,
+    id<GPBExtensionRegistry> extensionRegistry) {
   GPBCodedInputStreamState *state = &input->state_;
   id genericArray = GetOrCreateArrayIvarWithField(self, field);
   switch (GPBGetFieldDataType(field)) {
 #define CASE_REPEATED_NOT_PACKED_POD(NAME, TYPE, ARRAY_TYPE) \
-   case GPBDataType##NAME: {                                 \
-     TYPE val = GPBCodedInputStreamRead##NAME(state);        \
-     [(GPB##ARRAY_TYPE##Array *)genericArray addValue:val];  \
-     break;                                                  \
-   }
-#define CASE_REPEATED_NOT_PACKED_OBJECT(NAME)                \
-   case GPBDataType##NAME: {                                 \
-     id val = GPBCodedInputStreamReadRetained##NAME(state);  \
-     [(NSMutableArray*)genericArray addObject:val];          \
-     [val release];                                          \
-     break;                                                  \
-   }
-      CASE_REPEATED_NOT_PACKED_POD(Bool, BOOL, Bool)
-      CASE_REPEATED_NOT_PACKED_POD(Fixed32, uint32_t, UInt32)
-      CASE_REPEATED_NOT_PACKED_POD(SFixed32, int32_t, Int32)
-      CASE_REPEATED_NOT_PACKED_POD(Float, float, Float)
-      CASE_REPEATED_NOT_PACKED_POD(Fixed64, uint64_t, UInt64)
-      CASE_REPEATED_NOT_PACKED_POD(SFixed64, int64_t, Int64)
-      CASE_REPEATED_NOT_PACKED_POD(Double, double, Double)
-      CASE_REPEATED_NOT_PACKED_POD(Int32, int32_t, Int32)
-      CASE_REPEATED_NOT_PACKED_POD(Int64, int64_t, Int64)
-      CASE_REPEATED_NOT_PACKED_POD(SInt32, int32_t, Int32)
-      CASE_REPEATED_NOT_PACKED_POD(SInt64, int64_t, Int64)
-      CASE_REPEATED_NOT_PACKED_POD(UInt32, uint32_t, UInt32)
-      CASE_REPEATED_NOT_PACKED_POD(UInt64, uint64_t, UInt64)
-      CASE_REPEATED_NOT_PACKED_OBJECT(Bytes)
-      CASE_REPEATED_NOT_PACKED_OBJECT(String)
+  case GPBDataType##NAME: {                                  \
+    TYPE val = GPBCodedInputStreamRead##NAME(state);         \
+    [(GPB##ARRAY_TYPE##Array *)genericArray addValue:val];   \
+    break;                                                   \
+  }
+#define CASE_REPEATED_NOT_PACKED_OBJECT(NAME)              \
+  case GPBDataType##NAME: {                                \
+    id val = GPBCodedInputStreamReadRetained##NAME(state); \
+    [(NSMutableArray *)genericArray addObject:val];        \
+    [val release];                                         \
+    break;                                                 \
+  }
+    CASE_REPEATED_NOT_PACKED_POD(Bool, BOOL, Bool)
+    CASE_REPEATED_NOT_PACKED_POD(Fixed32, uint32_t, UInt32)
+    CASE_REPEATED_NOT_PACKED_POD(SFixed32, int32_t, Int32)
+    CASE_REPEATED_NOT_PACKED_POD(Float, float, Float)
+    CASE_REPEATED_NOT_PACKED_POD(Fixed64, uint64_t, UInt64)
+    CASE_REPEATED_NOT_PACKED_POD(SFixed64, int64_t, Int64)
+    CASE_REPEATED_NOT_PACKED_POD(Double, double, Double)
+    CASE_REPEATED_NOT_PACKED_POD(Int32, int32_t, Int32)
+    CASE_REPEATED_NOT_PACKED_POD(Int64, int64_t, Int64)
+    CASE_REPEATED_NOT_PACKED_POD(SInt32, int32_t, Int32)
+    CASE_REPEATED_NOT_PACKED_POD(SInt64, int64_t, Int64)
+    CASE_REPEATED_NOT_PACKED_POD(UInt32, uint32_t, UInt32)
+    CASE_REPEATED_NOT_PACKED_POD(UInt64, uint64_t, UInt64)
+    CASE_REPEATED_NOT_PACKED_OBJECT(Bytes)
+    CASE_REPEATED_NOT_PACKED_OBJECT(String)
 #undef CASE_REPEATED_NOT_PACKED_POD
 #undef CASE_NOT_PACKED_OBJECT
     case GPBDataTypeMessage: {
       GPBMessage *message = [[field.msgClass alloc] init];
-      [input readMessage:message extensionRegistry:extensionRegistry];
-      [(NSMutableArray*)genericArray addObject:message];
+      [(NSMutableArray *)genericArray addObject:message];
+      // The array will now retain message, so go ahead and release it in case
+      // -readMessage:extensionRegistry: throws so it won't be leaked.
       [message release];
+      [input readMessage:message extensionRegistry:extensionRegistry];
       break;
     }
     case GPBDataTypeGroup: {
       GPBMessage *message = [[field.msgClass alloc] init];
-      [input readGroup:GPBFieldNumber(field)
-                    message:message
-          extensionRegistry:extensionRegistry];
-      [(NSMutableArray*)genericArray addObject:message];
+      [(NSMutableArray *)genericArray addObject:message];
+      // The array will now retain message, so go ahead and release it in case
+      // -readGroup:extensionRegistry: throws so it won't be leaked.
       [message release];
+      [input readGroup:GPBFieldNumber(field) message:message extensionRegistry:extensionRegistry];
       break;
     }
     case GPBDataTypeEnum: {
       int32_t val = GPBCodedInputStreamReadEnum(state);
       if (!GPBFieldIsClosedEnum(field) || [field isValidEnumValue:val]) {
-        [(GPBEnumArray*)genericArray addRawValue:val];
+        [(GPBEnumArray *)genericArray addRawValue:val];
       } else {
         GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self);
         [unknownFields mergeVarintField:GPBFieldNumber(field) value:val];
@@ -2406,30 +2489,28 @@
       if (GPBFieldTag(fieldDescriptor) == tag) {
         GPBFieldType fieldType = fieldDescriptor.fieldType;
         if (fieldType == GPBFieldTypeSingle) {
-          MergeSingleFieldFromCodedInputStream(self, fieldDescriptor,
-                                               input, extensionRegistry);
+          MergeSingleFieldFromCodedInputStream(self, fieldDescriptor, input, extensionRegistry);
           // Well formed protos will only have a single field once, advance
           // the starting index to the next field.
           startingIndex += 1;
         } else if (fieldType == GPBFieldTypeRepeated) {
           if (fieldDescriptor.isPackable) {
-            MergeRepeatedPackedFieldFromCodedInputStream(
-                self, fieldDescriptor, input);
+            MergeRepeatedPackedFieldFromCodedInputStream(self, fieldDescriptor, input);
             // Well formed protos will only have a repeated field that is
             // packed once, advance the starting index to the next field.
             startingIndex += 1;
           } else {
-            MergeRepeatedNotPackedFieldFromCodedInputStream(
-                self, fieldDescriptor, input, extensionRegistry);
+            MergeRepeatedNotPackedFieldFromCodedInputStream(self, fieldDescriptor, input,
+                                                            extensionRegistry);
           }
         } else {  // fieldType == GPBFieldTypeMap
           // GPB*Dictionary or NSDictionary, exact type doesn't matter at this
           // point.
           id map = GetOrCreateMapIvarWithField(self, fieldDescriptor);
           [input readMapEntry:map
-            extensionRegistry:extensionRegistry
-                        field:fieldDescriptor
-                parentMessage:self];
+              extensionRegistry:extensionRegistry
+                          field:fieldDescriptor
+                  parentMessage:self];
         }
         merged = YES;
         break;
@@ -2450,14 +2531,13 @@
             (GPBFieldAlternateTag(fieldDescriptor) == tag)) {
           BOOL alternateIsPacked = !fieldDescriptor.isPackable;
           if (alternateIsPacked) {
-            MergeRepeatedPackedFieldFromCodedInputStream(
-                self, fieldDescriptor, input);
+            MergeRepeatedPackedFieldFromCodedInputStream(self, fieldDescriptor, input);
             // Well formed protos will only have a repeated field that is
             // packed once, advance the starting index to the next field.
             startingIndex += 1;
           } else {
-            MergeRepeatedNotPackedFieldFromCodedInputStream(
-                self, fieldDescriptor, input, extensionRegistry);
+            MergeRepeatedNotPackedFieldFromCodedInputStream(self, fieldDescriptor, input,
+                                                            extensionRegistry);
           }
           merged = YES;
           break;
@@ -2472,9 +2552,7 @@
         // zero signals EOF / limit reached
         return;
       } else {
-        if (![self parseUnknownField:input
-                   extensionRegistry:extensionRegistry
-                                 tag:tag]) {
+        if (![self parseUnknownField:input extensionRegistry:extensionRegistry tag:tag]) {
           // it's an endgroup tag
           return;
         }
@@ -2489,8 +2567,7 @@
 - (void)mergeFrom:(GPBMessage *)other {
   Class selfClass = [self class];
   Class otherClass = [other class];
-  if (!([selfClass isSubclassOfClass:otherClass] ||
-        [otherClass isSubclassOfClass:selfClass])) {
+  if (!([selfClass isSubclassOfClass:otherClass] || [otherClass isSubclassOfClass:selfClass])) {
     [NSException raise:NSInvalidArgumentException
                 format:@"Classes must match %@ != %@", selfClass, otherClass];
   }
@@ -2512,39 +2589,32 @@
       GPBDataType fieldDataType = GPBGetFieldDataType(field);
       switch (fieldDataType) {
         case GPBDataTypeBool:
-          GPBSetBoolIvarWithFieldPrivate(
-              self, field, GPBGetMessageBoolField(other, field));
+          GPBSetBoolIvarWithFieldPrivate(self, field, GPBGetMessageBoolField(other, field));
           break;
         case GPBDataTypeSFixed32:
         case GPBDataTypeEnum:
         case GPBDataTypeInt32:
         case GPBDataTypeSInt32:
-          GPBSetInt32IvarWithFieldPrivate(
-              self, field, GPBGetMessageInt32Field(other, field));
+          GPBSetInt32IvarWithFieldPrivate(self, field, GPBGetMessageInt32Field(other, field));
           break;
         case GPBDataTypeFixed32:
         case GPBDataTypeUInt32:
-          GPBSetUInt32IvarWithFieldPrivate(
-              self, field, GPBGetMessageUInt32Field(other, field));
+          GPBSetUInt32IvarWithFieldPrivate(self, field, GPBGetMessageUInt32Field(other, field));
           break;
         case GPBDataTypeSFixed64:
         case GPBDataTypeInt64:
         case GPBDataTypeSInt64:
-          GPBSetInt64IvarWithFieldPrivate(
-              self, field, GPBGetMessageInt64Field(other, field));
+          GPBSetInt64IvarWithFieldPrivate(self, field, GPBGetMessageInt64Field(other, field));
           break;
         case GPBDataTypeFixed64:
         case GPBDataTypeUInt64:
-          GPBSetUInt64IvarWithFieldPrivate(
-              self, field, GPBGetMessageUInt64Field(other, field));
+          GPBSetUInt64IvarWithFieldPrivate(self, field, GPBGetMessageUInt64Field(other, field));
           break;
         case GPBDataTypeFloat:
-          GPBSetFloatIvarWithFieldPrivate(
-              self, field, GPBGetMessageFloatField(other, field));
+          GPBSetFloatIvarWithFieldPrivate(self, field, GPBGetMessageFloatField(other, field));
           break;
         case GPBDataTypeDouble:
-          GPBSetDoubleIvarWithFieldPrivate(
-              self, field, GPBGetMessageDoubleField(other, field));
+          GPBSetDoubleIvarWithFieldPrivate(self, field, GPBGetMessageDoubleField(other, field));
           break;
         case GPBDataTypeBytes:
         case GPBDataTypeString: {
@@ -2556,8 +2626,7 @@
         case GPBDataTypeGroup: {
           id otherVal = GPBGetObjectIvarWithFieldNoAutocreate(other, field);
           if (GPBGetHasIvar(self, hasIndex, fieldNumber)) {
-            GPBMessage *message =
-                GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+            GPBMessage *message = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
             [message mergeFrom:otherVal];
           } else {
             GPBMessage *message = [otherVal copy];
@@ -2565,27 +2634,23 @@
           }
           break;
         }
-      } // switch()
+      }  // switch()
     } else if (fieldType == GPBFieldTypeRepeated) {
       // In the case of a list, they need to be appended, and there is no
       // _hasIvar to worry about setting.
-      id otherArray =
-          GPBGetObjectIvarWithFieldNoAutocreate(other, field);
+      id otherArray = GPBGetObjectIvarWithFieldNoAutocreate(other, field);
       if (otherArray) {
         GPBDataType fieldDataType = field->description_->dataType;
         if (GPBDataTypeIsObject(fieldDataType)) {
-          NSMutableArray *resultArray =
-              GetOrCreateArrayIvarWithField(self, field);
+          NSMutableArray *resultArray = GetOrCreateArrayIvarWithField(self, field);
           [resultArray addObjectsFromArray:otherArray];
         } else if (fieldDataType == GPBDataTypeEnum) {
-          GPBEnumArray *resultArray =
-              GetOrCreateArrayIvarWithField(self, field);
+          GPBEnumArray *resultArray = GetOrCreateArrayIvarWithField(self, field);
           [resultArray addRawValuesFromArray:otherArray];
         } else {
           // The array type doesn't matter, that all implement
           // -addValuesFromArray:.
-          GPBInt32Array *resultArray =
-              GetOrCreateArrayIvarWithField(self, field);
+          GPBInt32Array *resultArray = GetOrCreateArrayIvarWithField(self, field);
           [resultArray addValuesFromArray:otherArray];
         }
       }
@@ -2596,27 +2661,23 @@
       if (otherDict) {
         GPBDataType keyDataType = field.mapKeyDataType;
         GPBDataType valueDataType = field->description_->dataType;
-        if (GPBDataTypeIsObject(keyDataType) &&
-            GPBDataTypeIsObject(valueDataType)) {
-          NSMutableDictionary *resultDict =
-              GetOrCreateMapIvarWithField(self, field);
+        if (GPBDataTypeIsObject(keyDataType) && GPBDataTypeIsObject(valueDataType)) {
+          NSMutableDictionary *resultDict = GetOrCreateMapIvarWithField(self, field);
           [resultDict addEntriesFromDictionary:otherDict];
         } else if (valueDataType == GPBDataTypeEnum) {
           // The exact type doesn't matter, just need to know it is a
           // GPB*EnumDictionary.
-          GPBInt32EnumDictionary *resultDict =
-              GetOrCreateMapIvarWithField(self, field);
+          GPBInt32EnumDictionary *resultDict = GetOrCreateMapIvarWithField(self, field);
           [resultDict addRawEntriesFromDictionary:otherDict];
         } else {
           // The exact type doesn't matter, they all implement
           // -addEntriesFromDictionary:.
-          GPBInt32Int32Dictionary *resultDict =
-              GetOrCreateMapIvarWithField(self, field);
+          GPBInt32Int32Dictionary *resultDict = GetOrCreateMapIvarWithField(self, field);
           [resultDict addEntriesFromDictionary:otherDict];
         }
       }
     }  // if (fieldType)..else if...else
-  }  // for(fields)
+  }    // for(fields)
 
   // Unknown fields.
   if (!unknownFields_) {
@@ -2632,8 +2693,7 @@
   }
 
   if (extensionMap_ == nil) {
-    extensionMap_ =
-        CloneExtensionMap(other->extensionMap_, NSZoneFromPointer(self));
+    extensionMap_ = CloneExtensionMap(other->extensionMap_, NSZoneFromPointer(self));
   } else {
     for (GPBExtensionDescriptor *extension in other->extensionMap_) {
       id otherValue = [other->extensionMap_ objectForKey:extension];
@@ -2671,8 +2731,7 @@
       }
 
       if (isMessageExtension && !extension.isRepeated) {
-        GPBMessage *autocreatedValue =
-            [[autocreatedExtensionMap_ objectForKey:extension] retain];
+        GPBMessage *autocreatedValue = [[autocreatedExtensionMap_ objectForKey:extension] retain];
         // Must remove from the map before calling GPBClearMessageAutocreator()
         // so that GPBClearMessageAutocreator() knows its safe to clear.
         [autocreatedExtensionMap_ removeObjectForKey:extension];
@@ -2706,10 +2765,8 @@
       // NOTE: These are NSArray/GPB*Array or NSDictionary/GPB*Dictionary, but
       // the type doesn't really matter as the objects all support -count and
       // -isEqual:.
-      NSArray *resultMapOrArray =
-          GPBGetObjectIvarWithFieldNoAutocreate(self, field);
-      NSArray *otherMapOrArray =
-          GPBGetObjectIvarWithFieldNoAutocreate(other, field);
+      NSArray *resultMapOrArray = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
+      NSArray *otherMapOrArray = GPBGetObjectIvarWithFieldNoAutocreate(other, field);
       // nil and empty are equal
       if (resultMapOrArray.count != 0 || otherMapOrArray.count != 0) {
         if (![resultMapOrArray isEqual:otherMapOrArray]) {
@@ -2787,9 +2844,9 @@
           }
           break;
         }
-      } // switch()
-    }   // if(mapOrArray)...else
-  }  // for(fields)
+      }  // switch()
+    }    // if(mapOrArray)...else
+  }      // for(fields)
 
   // nil and empty are equal
   if (extensionMap_.count != 0 || otherMsg->extensionMap_.count != 0) {
@@ -2800,8 +2857,7 @@
 
   // nil and empty are equal
   GPBUnknownFieldSet *otherUnknowns = otherMsg->unknownFields_;
-  if ([unknownFields_ countOfFields] != 0 ||
-      [otherUnknowns countOfFields] != 0) {
+  if ([unknownFields_ countOfFields] != 0 || [otherUnknowns countOfFields] != 0) {
     if (![unknownFields_ isEqual:otherUnknowns]) {
       return NO;
     }
@@ -2904,7 +2960,7 @@
           result = prime * result + (NSUInteger)[[*valPtr class] descriptor];
           break;
         }
-      } // switch()
+      }  // switch()
     }
   }
 
@@ -2917,8 +2973,8 @@
 
 - (NSString *)description {
   NSString *textFormat = GPBTextFormatForMessage(self, @"    ");
-  NSString *description = [NSString
-      stringWithFormat:@"<%@ %p>: {\n%@}", [self class], self, textFormat];
+  NSString *description =
+      [NSString stringWithFormat:@"<%@ %p>: {\n%@}", [self class], self, textFormat];
   return description;
 }
 
@@ -2956,44 +3012,43 @@
       uint32_t fieldNumber = GPBFieldNumber(fieldDescriptor);
 
       switch (fieldDataType) {
-#define CASE_SINGLE_POD(NAME, TYPE, FUNC_TYPE)                                \
-        case GPBDataType##NAME: {                                             \
-          TYPE fieldVal = GPBGetMessage##FUNC_TYPE##Field(self, fieldDescriptor); \
-          result += GPBCompute##NAME##Size(fieldNumber, fieldVal);            \
-          break;                                                              \
-        }
-#define CASE_SINGLE_OBJECT(NAME)                                              \
-        case GPBDataType##NAME: {                                             \
-          id fieldVal = GPBGetObjectIvarWithFieldNoAutocreate(self, fieldDescriptor); \
-          result += GPBCompute##NAME##Size(fieldNumber, fieldVal);            \
-          break;                                                              \
-        }
-          CASE_SINGLE_POD(Bool, BOOL, Bool)
-          CASE_SINGLE_POD(Fixed32, uint32_t, UInt32)
-          CASE_SINGLE_POD(SFixed32, int32_t, Int32)
-          CASE_SINGLE_POD(Float, float, Float)
-          CASE_SINGLE_POD(Fixed64, uint64_t, UInt64)
-          CASE_SINGLE_POD(SFixed64, int64_t, Int64)
-          CASE_SINGLE_POD(Double, double, Double)
-          CASE_SINGLE_POD(Int32, int32_t, Int32)
-          CASE_SINGLE_POD(Int64, int64_t, Int64)
-          CASE_SINGLE_POD(SInt32, int32_t, Int32)
-          CASE_SINGLE_POD(SInt64, int64_t, Int64)
-          CASE_SINGLE_POD(UInt32, uint32_t, UInt32)
-          CASE_SINGLE_POD(UInt64, uint64_t, UInt64)
-          CASE_SINGLE_OBJECT(Bytes)
-          CASE_SINGLE_OBJECT(String)
-          CASE_SINGLE_OBJECT(Message)
-          CASE_SINGLE_OBJECT(Group)
-          CASE_SINGLE_POD(Enum, int32_t, Int32)
+#define CASE_SINGLE_POD(NAME, TYPE, FUNC_TYPE)                              \
+  case GPBDataType##NAME: {                                                 \
+    TYPE fieldVal = GPBGetMessage##FUNC_TYPE##Field(self, fieldDescriptor); \
+    result += GPBCompute##NAME##Size(fieldNumber, fieldVal);                \
+    break;                                                                  \
+  }
+#define CASE_SINGLE_OBJECT(NAME)                                                \
+  case GPBDataType##NAME: {                                                     \
+    id fieldVal = GPBGetObjectIvarWithFieldNoAutocreate(self, fieldDescriptor); \
+    result += GPBCompute##NAME##Size(fieldNumber, fieldVal);                    \
+    break;                                                                      \
+  }
+        CASE_SINGLE_POD(Bool, BOOL, Bool)
+        CASE_SINGLE_POD(Fixed32, uint32_t, UInt32)
+        CASE_SINGLE_POD(SFixed32, int32_t, Int32)
+        CASE_SINGLE_POD(Float, float, Float)
+        CASE_SINGLE_POD(Fixed64, uint64_t, UInt64)
+        CASE_SINGLE_POD(SFixed64, int64_t, Int64)
+        CASE_SINGLE_POD(Double, double, Double)
+        CASE_SINGLE_POD(Int32, int32_t, Int32)
+        CASE_SINGLE_POD(Int64, int64_t, Int64)
+        CASE_SINGLE_POD(SInt32, int32_t, Int32)
+        CASE_SINGLE_POD(SInt64, int64_t, Int64)
+        CASE_SINGLE_POD(UInt32, uint32_t, UInt32)
+        CASE_SINGLE_POD(UInt64, uint64_t, UInt64)
+        CASE_SINGLE_OBJECT(Bytes)
+        CASE_SINGLE_OBJECT(String)
+        CASE_SINGLE_OBJECT(Message)
+        CASE_SINGLE_OBJECT(Group)
+        CASE_SINGLE_POD(Enum, int32_t, Int32)
 #undef CASE_SINGLE_POD
 #undef CASE_SINGLE_OBJECT
       }
 
-    // Repeated Fields
+      // Repeated Fields
     } else if (fieldType == GPBFieldTypeRepeated) {
-      id genericArray =
-          GPBGetObjectIvarWithFieldNoAutocreate(self, fieldDescriptor);
+      id genericArray = GPBGetObjectIvarWithFieldNoAutocreate(self, fieldDescriptor);
       NSUInteger count = [genericArray count];
       if (count == 0) {
         continue;  // Nothing to add.
@@ -3001,41 +3056,41 @@
       __block size_t dataSize = 0;
 
       switch (fieldDataType) {
-#define CASE_REPEATED_POD(NAME, TYPE, ARRAY_TYPE)                             \
-    CASE_REPEATED_POD_EXTRA(NAME, TYPE, ARRAY_TYPE, )
-#define CASE_REPEATED_POD_EXTRA(NAME, TYPE, ARRAY_TYPE, ARRAY_ACCESSOR_NAME)  \
-        case GPBDataType##NAME: {                                             \
-          GPB##ARRAY_TYPE##Array *array = genericArray;                       \
-          [array enumerate##ARRAY_ACCESSOR_NAME##ValuesWithBlock:^(TYPE value, __unused NSUInteger idx, __unused BOOL *stop) { \
-            dataSize += GPBCompute##NAME##SizeNoTag(value);                   \
-          }];                                                                 \
-          break;                                                              \
-        }
-#define CASE_REPEATED_OBJECT(NAME)                                            \
-        case GPBDataType##NAME: {                                             \
-          for (id value in genericArray) {                                    \
-            dataSize += GPBCompute##NAME##SizeNoTag(value);                   \
-          }                                                                   \
-          break;                                                              \
-        }
-          CASE_REPEATED_POD(Bool, BOOL, Bool)
-          CASE_REPEATED_POD(Fixed32, uint32_t, UInt32)
-          CASE_REPEATED_POD(SFixed32, int32_t, Int32)
-          CASE_REPEATED_POD(Float, float, Float)
-          CASE_REPEATED_POD(Fixed64, uint64_t, UInt64)
-          CASE_REPEATED_POD(SFixed64, int64_t, Int64)
-          CASE_REPEATED_POD(Double, double, Double)
-          CASE_REPEATED_POD(Int32, int32_t, Int32)
-          CASE_REPEATED_POD(Int64, int64_t, Int64)
-          CASE_REPEATED_POD(SInt32, int32_t, Int32)
-          CASE_REPEATED_POD(SInt64, int64_t, Int64)
-          CASE_REPEATED_POD(UInt32, uint32_t, UInt32)
-          CASE_REPEATED_POD(UInt64, uint64_t, UInt64)
-          CASE_REPEATED_OBJECT(Bytes)
-          CASE_REPEATED_OBJECT(String)
-          CASE_REPEATED_OBJECT(Message)
-          CASE_REPEATED_OBJECT(Group)
-          CASE_REPEATED_POD_EXTRA(Enum, int32_t, Enum, Raw)
+#define CASE_REPEATED_POD(NAME, TYPE, ARRAY_TYPE) CASE_REPEATED_POD_EXTRA(NAME, TYPE, ARRAY_TYPE, )
+#define CASE_REPEATED_POD_EXTRA(NAME, TYPE, ARRAY_TYPE, ARRAY_ACCESSOR_NAME)           \
+  case GPBDataType##NAME: {                                                            \
+    GPB##ARRAY_TYPE##Array *array = genericArray;                                      \
+    [array enumerate##ARRAY_ACCESSOR_NAME##                                            \
+        ValuesWithBlock:^(TYPE value, __unused NSUInteger idx, __unused BOOL * stop) { \
+          dataSize += GPBCompute##NAME##SizeNoTag(value);                              \
+        }];                                                                            \
+    break;                                                                             \
+  }
+#define CASE_REPEATED_OBJECT(NAME)                    \
+  case GPBDataType##NAME: {                           \
+    for (id value in genericArray) {                  \
+      dataSize += GPBCompute##NAME##SizeNoTag(value); \
+    }                                                 \
+    break;                                            \
+  }
+        CASE_REPEATED_POD(Bool, BOOL, Bool)
+        CASE_REPEATED_POD(Fixed32, uint32_t, UInt32)
+        CASE_REPEATED_POD(SFixed32, int32_t, Int32)
+        CASE_REPEATED_POD(Float, float, Float)
+        CASE_REPEATED_POD(Fixed64, uint64_t, UInt64)
+        CASE_REPEATED_POD(SFixed64, int64_t, Int64)
+        CASE_REPEATED_POD(Double, double, Double)
+        CASE_REPEATED_POD(Int32, int32_t, Int32)
+        CASE_REPEATED_POD(Int64, int64_t, Int64)
+        CASE_REPEATED_POD(SInt32, int32_t, Int32)
+        CASE_REPEATED_POD(SInt64, int64_t, Int64)
+        CASE_REPEATED_POD(UInt32, uint32_t, UInt32)
+        CASE_REPEATED_POD(UInt64, uint64_t, UInt64)
+        CASE_REPEATED_OBJECT(Bytes)
+        CASE_REPEATED_OBJECT(String)
+        CASE_REPEATED_OBJECT(Message)
+        CASE_REPEATED_OBJECT(Group)
+        CASE_REPEATED_POD_EXTRA(Enum, int32_t, Enum, Raw)
 #undef CASE_REPEATED_POD
 #undef CASE_REPEATED_POD_EXTRA
 #undef CASE_REPEATED_OBJECT
@@ -3053,20 +3108,18 @@
         result += count * tagSize;
       }
 
-    // Map<> Fields
+      // Map<> Fields
     } else {  // fieldType == GPBFieldTypeMap
       if (GPBDataTypeIsObject(fieldDataType) &&
           (fieldDescriptor.mapKeyDataType == GPBDataTypeString)) {
         // If key type was string, then the map is an NSDictionary.
-        NSDictionary *map =
-            GPBGetObjectIvarWithFieldNoAutocreate(self, fieldDescriptor);
+        NSDictionary *map = GPBGetObjectIvarWithFieldNoAutocreate(self, fieldDescriptor);
         if (map) {
           result += GPBDictionaryComputeSizeInternalHelper(map, fieldDescriptor);
         }
       } else {
         // Type will be GPB*GroupDictionary, exact type doesn't matter.
-        GPBInt32Int32Dictionary *map =
-            GPBGetObjectIvarWithFieldNoAutocreate(self, fieldDescriptor);
+        GPBInt32Int32Dictionary *map = GPBGetObjectIvarWithFieldNoAutocreate(self, fieldDescriptor);
         result += [map computeSerializedSizeAsField:fieldDescriptor];
       }
     }
@@ -3102,40 +3155,40 @@
                            ResolveIvarAccessorMethodResult *result) {
   GPBDataType fieldDataType = GPBGetFieldDataType(field);
   switch (fieldDataType) {
-#define CASE_GET(NAME, TYPE, TRUE_NAME)                          \
-    case GPBDataType##NAME: {                                    \
-      result->impToAdd = imp_implementationWithBlock(^(id obj) { \
-        return GPBGetMessage##TRUE_NAME##Field(obj, field);      \
-       });                                                       \
-      result->encodingSelector = @selector(get##NAME);           \
-      break;                                                     \
-    }
-#define CASE_GET_OBJECT(NAME, TYPE, TRUE_NAME)                   \
-    case GPBDataType##NAME: {                                    \
-      result->impToAdd = imp_implementationWithBlock(^(id obj) { \
-        return GPBGetObjectIvarWithField(obj, field);            \
-       });                                                       \
-      result->encodingSelector = @selector(get##NAME);           \
-      break;                                                     \
-    }
-      CASE_GET(Bool, BOOL, Bool)
-      CASE_GET(Fixed32, uint32_t, UInt32)
-      CASE_GET(SFixed32, int32_t, Int32)
-      CASE_GET(Float, float, Float)
-      CASE_GET(Fixed64, uint64_t, UInt64)
-      CASE_GET(SFixed64, int64_t, Int64)
-      CASE_GET(Double, double, Double)
-      CASE_GET(Int32, int32_t, Int32)
-      CASE_GET(Int64, int64_t, Int64)
-      CASE_GET(SInt32, int32_t, Int32)
-      CASE_GET(SInt64, int64_t, Int64)
-      CASE_GET(UInt32, uint32_t, UInt32)
-      CASE_GET(UInt64, uint64_t, UInt64)
-      CASE_GET_OBJECT(Bytes, id, Object)
-      CASE_GET_OBJECT(String, id, Object)
-      CASE_GET_OBJECT(Message, id, Object)
-      CASE_GET_OBJECT(Group, id, Object)
-      CASE_GET(Enum, int32_t, Enum)
+#define CASE_GET(NAME, TYPE, TRUE_NAME)                        \
+  case GPBDataType##NAME: {                                    \
+    result->impToAdd = imp_implementationWithBlock(^(id obj) { \
+      return GPBGetMessage##TRUE_NAME##Field(obj, field);      \
+    });                                                        \
+    result->encodingSelector = @selector(get##NAME);           \
+    break;                                                     \
+  }
+#define CASE_GET_OBJECT(NAME, TYPE, TRUE_NAME)                 \
+  case GPBDataType##NAME: {                                    \
+    result->impToAdd = imp_implementationWithBlock(^(id obj) { \
+      return GPBGetObjectIvarWithField(obj, field);            \
+    });                                                        \
+    result->encodingSelector = @selector(get##NAME);           \
+    break;                                                     \
+  }
+    CASE_GET(Bool, BOOL, Bool)
+    CASE_GET(Fixed32, uint32_t, UInt32)
+    CASE_GET(SFixed32, int32_t, Int32)
+    CASE_GET(Float, float, Float)
+    CASE_GET(Fixed64, uint64_t, UInt64)
+    CASE_GET(SFixed64, int64_t, Int64)
+    CASE_GET(Double, double, Double)
+    CASE_GET(Int32, int32_t, Int32)
+    CASE_GET(Int64, int64_t, Int64)
+    CASE_GET(SInt32, int32_t, Int32)
+    CASE_GET(SInt64, int64_t, Int64)
+    CASE_GET(UInt32, uint32_t, UInt32)
+    CASE_GET(UInt64, uint64_t, UInt64)
+    CASE_GET_OBJECT(Bytes, id, Object)
+    CASE_GET_OBJECT(String, id, Object)
+    CASE_GET_OBJECT(Message, id, Object)
+    CASE_GET_OBJECT(Group, id, Object)
+    CASE_GET(Enum, int32_t, Enum)
 #undef CASE_GET
   }
 }
@@ -3145,40 +3198,40 @@
                            ResolveIvarAccessorMethodResult *result) {
   GPBDataType fieldDataType = GPBGetFieldDataType(field);
   switch (fieldDataType) {
-#define CASE_SET(NAME, TYPE, TRUE_NAME)                                       \
-    case GPBDataType##NAME: {                                                 \
-      result->impToAdd = imp_implementationWithBlock(^(id obj, TYPE value) {  \
-        return GPBSet##TRUE_NAME##IvarWithFieldPrivate(obj, field, value);    \
-      });                                                                     \
-      result->encodingSelector = @selector(set##NAME:);                       \
-      break;                                                                  \
-    }
-#define CASE_SET_COPY(NAME)                                                   \
-    case GPBDataType##NAME: {                                                 \
-      result->impToAdd = imp_implementationWithBlock(^(id obj, id value) {    \
-        return GPBSetRetainedObjectIvarWithFieldPrivate(obj, field, [value copy]); \
-      });                                                                     \
-      result->encodingSelector = @selector(set##NAME:);                       \
-      break;                                                                  \
-    }
-      CASE_SET(Bool, BOOL, Bool)
-      CASE_SET(Fixed32, uint32_t, UInt32)
-      CASE_SET(SFixed32, int32_t, Int32)
-      CASE_SET(Float, float, Float)
-      CASE_SET(Fixed64, uint64_t, UInt64)
-      CASE_SET(SFixed64, int64_t, Int64)
-      CASE_SET(Double, double, Double)
-      CASE_SET(Int32, int32_t, Int32)
-      CASE_SET(Int64, int64_t, Int64)
-      CASE_SET(SInt32, int32_t, Int32)
-      CASE_SET(SInt64, int64_t, Int64)
-      CASE_SET(UInt32, uint32_t, UInt32)
-      CASE_SET(UInt64, uint64_t, UInt64)
-      CASE_SET_COPY(Bytes)
-      CASE_SET_COPY(String)
-      CASE_SET(Message, id, Object)
-      CASE_SET(Group, id, Object)
-      CASE_SET(Enum, int32_t, Enum)
+#define CASE_SET(NAME, TYPE, TRUE_NAME)                                    \
+  case GPBDataType##NAME: {                                                \
+    result->impToAdd = imp_implementationWithBlock(^(id obj, TYPE value) { \
+      return GPBSet##TRUE_NAME##IvarWithFieldPrivate(obj, field, value);   \
+    });                                                                    \
+    result->encodingSelector = @selector(set##NAME:);                      \
+    break;                                                                 \
+  }
+#define CASE_SET_COPY(NAME)                                                      \
+  case GPBDataType##NAME: {                                                      \
+    result->impToAdd = imp_implementationWithBlock(^(id obj, id value) {         \
+      return GPBSetRetainedObjectIvarWithFieldPrivate(obj, field, [value copy]); \
+    });                                                                          \
+    result->encodingSelector = @selector(set##NAME:);                            \
+    break;                                                                       \
+  }
+    CASE_SET(Bool, BOOL, Bool)
+    CASE_SET(Fixed32, uint32_t, UInt32)
+    CASE_SET(SFixed32, int32_t, Int32)
+    CASE_SET(Float, float, Float)
+    CASE_SET(Fixed64, uint64_t, UInt64)
+    CASE_SET(SFixed64, int64_t, Int64)
+    CASE_SET(Double, double, Double)
+    CASE_SET(Int32, int32_t, Int32)
+    CASE_SET(Int64, int64_t, Int64)
+    CASE_SET(SInt32, int32_t, Int32)
+    CASE_SET(SInt64, int64_t, Int64)
+    CASE_SET(UInt32, uint32_t, UInt32)
+    CASE_SET(UInt64, uint64_t, UInt64)
+    CASE_SET_COPY(Bytes)
+    CASE_SET_COPY(String)
+    CASE_SET(Message, id, Object)
+    CASE_SET(Group, id, Object)
+    CASE_SET(Enum, int32_t, Enum)
 #undef CASE_SET
   }
 }
@@ -3217,8 +3270,7 @@
         result.impToAdd = imp_implementationWithBlock(^(id obj, BOOL value) {
           if (value) {
             [NSException raise:NSInvalidArgumentException
-                        format:@"%@: %@ can only be set to NO (to clear field).",
-                               [obj class],
+                        format:@"%@: %@ can only be set to NO (to clear field).", [obj class],
                                NSStringFromSelector(field->setHasSel_)];
           }
           GPBClearMessageField(obj, field);
@@ -3262,8 +3314,7 @@
         result.impToAdd = imp_implementationWithBlock(^(id obj) {
           // Type doesn't matter, all *Array and *Dictionary types support
           // -count.
-          NSArray *arrayOrMap =
-              GPBGetObjectIvarWithFieldNoAutocreate(obj, field);
+          NSArray *arrayOrMap = GPBGetObjectIvarWithFieldNoAutocreate(obj, field);
           return [arrayOrMap count];
         });
         result.encodingSelector = @selector(getArrayCount);
@@ -3272,8 +3323,7 @@
     }
   }
   if (result.impToAdd) {
-    const char *encoding =
-        GPBMessageEncodingForSelector(result.encodingSelector, YES);
+    const char *encoding = GPBMessageEncodingForSelector(result.encodingSelector, YES);
     Class msgClass = descriptor.messageClass;
     BOOL methodAdded = class_addMethod(msgClass, sel, result.impToAdd, encoding);
     // class_addMethod() is documented as also failing if the method was already
@@ -3307,10 +3357,12 @@
 - (instancetype)initWithCoder:(NSCoder *)aDecoder {
   self = [self init];
   if (self) {
-    NSData *data =
-        [aDecoder decodeObjectOfClass:[NSData class] forKey:kGPBDataCoderKey];
+    NSData *data = [aDecoder decodeObjectOfClass:[NSData class] forKey:kGPBDataCoderKey];
     if (data.length) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
       [self mergeFromData:data extensionRegistry:nil];
+#pragma clang diagnostic pop
     }
   }
   return self;
@@ -3355,8 +3407,7 @@
 #if defined(DEBUG) && DEBUG
   if (field.fieldType != GPBFieldTypeRepeated) {
     [NSException raise:NSInvalidArgumentException
-                format:@"%@.%@ is not a repeated field.",
-     [self class], field.name];
+                format:@"%@.%@ is not a repeated field.", [self class], field.name];
   }
 #endif
   return GetOrCreateArrayIvarWithField(self, field);
@@ -3367,8 +3418,7 @@
 #if defined(DEBUG) && DEBUG
   if (field.fieldType != GPBFieldTypeMap) {
     [NSException raise:NSInvalidArgumentException
-                format:@"%@.%@ is not a map<> field.",
-     [self class], field.name];
+                format:@"%@.%@ is not a map<> field.", [self class], field.name];
   }
 #endif
   return GetOrCreateMapIvarWithField(self, field);
diff --git a/objectivec/GPBMessage_PackagePrivate.h b/objectivec/GPBMessage_PackagePrivate.h
index 70b47a5..8dc82e4 100644
--- a/objectivec/GPBMessage_PackagePrivate.h
+++ b/objectivec/GPBMessage_PackagePrivate.h
@@ -80,26 +80,12 @@
 - (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input
                 extensionRegistry:(id<GPBExtensionRegistry>)extensionRegistry;
 
-// Parses the next delimited message of this type from the input and merges it
-// with this message.
-- (void)mergeDelimitedFromCodedInputStream:(GPBCodedInputStream *)input
-                         extensionRegistry:(id<GPBExtensionRegistry>)extensionRegistry;
-
 - (void)addUnknownMapEntry:(int32_t)fieldNum value:(NSData *)data;
 
 @end
 
 CF_EXTERN_C_BEGIN
 
-// Call this before using the readOnlySemaphore_. This ensures it is created only once.
-void GPBPrepareReadOnlySemaphore(GPBMessage *self);
-
-// Returns a new instance that was automatically created by |autocreator| for
-// its field |field|.
-GPBMessage *GPBCreateMessageWithAutocreator(Class msgClass, GPBMessage *autocreator,
-                                            GPBFieldDescriptor *field)
-    __attribute__((ns_returns_retained));
-
 // Returns whether |message| autocreated this message. This is NO if the message
 // was not autocreated by |message| or if it has been mutated since
 // autocreation.
diff --git a/objectivec/GPBProtocolBuffers_RuntimeSupport.h b/objectivec/GPBProtocolBuffers_RuntimeSupport.h
index 804a68a..297173d 100644
--- a/objectivec/GPBProtocolBuffers_RuntimeSupport.h
+++ b/objectivec/GPBProtocolBuffers_RuntimeSupport.h
@@ -36,7 +36,6 @@
 // clang-format on
 
 #import "GPBDescriptor_PackagePrivate.h"
-#import "GPBExtensionInternals.h"
-#import "GPBMessage_PackagePrivate.h"
+#import "GPBMessage.h"
 #import "GPBRootObject_PackagePrivate.h"
 #import "GPBUtilities_PackagePrivate.h"
diff --git a/objectivec/GPBStruct.pbobjc.h b/objectivec/GPBStruct.pbobjc.h
index b0cc299..8f2f12c 100644
--- a/objectivec/GPBStruct.pbobjc.h
+++ b/objectivec/GPBStruct.pbobjc.h
@@ -32,7 +32,7 @@
  * `NullValue` is a singleton enumeration to represent the null value for the
  * `Value` type union.
  *
- *  The JSON representation for `NullValue` is JSON `null`.
+ * The JSON representation for `NullValue` is JSON `null`.
  **/
 typedef GPB_ENUM(GPBNullValue) {
   /**
diff --git a/objectivec/GPBType.pbobjc.h b/objectivec/GPBType.pbobjc.h
index 35a03f9..28638fa 100644
--- a/objectivec/GPBType.pbobjc.h
+++ b/objectivec/GPBType.pbobjc.h
@@ -43,6 +43,9 @@
 
   /** Syntax `proto3`. */
   GPBSyntax_SyntaxProto3 = 1,
+
+  /** Syntax `editions`. */
+  GPBSyntax_SyntaxEditions = 2,
 };
 
 GPBEnumDescriptor *GPBSyntax_EnumDescriptor(void);
@@ -184,6 +187,7 @@
   GPBType_FieldNumber_OptionsArray = 4,
   GPBType_FieldNumber_SourceContext = 5,
   GPBType_FieldNumber_Syntax = 6,
+  GPBType_FieldNumber_Edition = 7,
 };
 
 /**
@@ -217,6 +221,9 @@
 /** The source syntax. */
 @property(nonatomic, readwrite) GPBSyntax syntax;
 
+/** The source edition string, only valid when syntax is SYNTAX_EDITIONS. */
+@property(nonatomic, readwrite, copy, null_resettable) NSString *edition;
+
 @end
 
 /**
@@ -323,6 +330,7 @@
   GPBEnum_FieldNumber_OptionsArray = 3,
   GPBEnum_FieldNumber_SourceContext = 4,
   GPBEnum_FieldNumber_Syntax = 5,
+  GPBEnum_FieldNumber_Edition = 6,
 };
 
 /**
@@ -351,6 +359,9 @@
 /** The source syntax. */
 @property(nonatomic, readwrite) GPBSyntax syntax;
 
+/** The source edition string, only valid when syntax is SYNTAX_EDITIONS. */
+@property(nonatomic, readwrite, copy, null_resettable) NSString *edition;
+
 @end
 
 /**
diff --git a/objectivec/GPBType.pbobjc.m b/objectivec/GPBType.pbobjc.m
index 04e262c..db7a41f 100644
--- a/objectivec/GPBType.pbobjc.m
+++ b/objectivec/GPBType.pbobjc.m
@@ -54,10 +54,12 @@
   if (!descriptor) {
     GPB_DEBUG_CHECK_RUNTIME_VERSIONS();
     static const char *valueNames =
-        "SyntaxProto2\000SyntaxProto3\000";
+        "SyntaxProto2\000SyntaxProto3\000SyntaxEditions"
+        "\000";
     static const int32_t values[] = {
         GPBSyntax_SyntaxProto2,
         GPBSyntax_SyntaxProto3,
+        GPBSyntax_SyntaxEditions,
     };
     GPBEnumDescriptor *worker =
         [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(GPBSyntax)
@@ -78,6 +80,7 @@
   switch (value__) {
     case GPBSyntax_SyntaxProto2:
     case GPBSyntax_SyntaxProto3:
+    case GPBSyntax_SyntaxEditions:
       return YES;
     default:
       return NO;
@@ -212,6 +215,7 @@
 @dynamic optionsArray, optionsArray_Count;
 @dynamic hasSourceContext, sourceContext;
 @dynamic syntax;
+@dynamic edition;
 
 typedef struct GPBType__storage_ {
   uint32_t _has_storage_[1];
@@ -221,6 +225,7 @@
   NSMutableArray *oneofsArray;
   NSMutableArray *optionsArray;
   GPBSourceContext *sourceContext;
+  NSString *edition;
 } GPBType__storage_;
 
 // This method is threadsafe because it is initially called
@@ -284,6 +289,15 @@
         .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor | GPBFieldClearHasIvarOnZero),
         .dataType = GPBDataTypeEnum,
       },
+      {
+        .name = "edition",
+        .dataTypeSpecific.clazz = Nil,
+        .number = GPBType_FieldNumber_Edition,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GPBType__storage_, edition),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero),
+        .dataType = GPBDataTypeString,
+      },
     };
     GPBDescriptor *localDescriptor =
         [GPBDescriptor allocDescriptorForClass:GPBObjCClass(GPBType)
@@ -497,6 +511,7 @@
 @dynamic optionsArray, optionsArray_Count;
 @dynamic hasSourceContext, sourceContext;
 @dynamic syntax;
+@dynamic edition;
 
 typedef struct GPBEnum__storage_ {
   uint32_t _has_storage_[1];
@@ -505,6 +520,7 @@
   NSMutableArray *enumvalueArray;
   NSMutableArray *optionsArray;
   GPBSourceContext *sourceContext;
+  NSString *edition;
 } GPBEnum__storage_;
 
 // This method is threadsafe because it is initially called
@@ -559,6 +575,15 @@
         .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor | GPBFieldClearHasIvarOnZero),
         .dataType = GPBDataTypeEnum,
       },
+      {
+        .name = "edition",
+        .dataTypeSpecific.clazz = Nil,
+        .number = GPBEnum_FieldNumber_Edition,
+        .hasIndex = 3,
+        .offset = (uint32_t)offsetof(GPBEnum__storage_, edition),
+        .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero),
+        .dataType = GPBDataTypeString,
+      },
     };
     GPBDescriptor *localDescriptor =
         [GPBDescriptor allocDescriptorForClass:GPBObjCClass(GPBEnum)
diff --git a/objectivec/GPBUnknownFieldSet.m b/objectivec/GPBUnknownFieldSet.m
index dcd4b44..fd36935 100644
--- a/objectivec/GPBUnknownFieldSet.m
+++ b/objectivec/GPBUnknownFieldSet.m
@@ -289,13 +289,6 @@
   }
 }
 
-- (void)mergeFromData:(NSData *)data {
-  GPBCodedInputStream *input = [[GPBCodedInputStream alloc] initWithData:data];
-  [self mergeFromCodedInputStream:input];
-  [input checkLastTagWas:0];
-  [input release];
-}
-
 - (void)mergeVarintField:(int32_t)number value:(int32_t)value {
   checkNumber(number);
   [[self mutableFieldForNumber:number create:YES] addVarint:value];
@@ -325,10 +318,12 @@
     }
     case GPBWireFormatStartGroup: {
       GPBUnknownFieldSet *unknownFieldSet = [[GPBUnknownFieldSet alloc] init];
-      [input readUnknownGroup:number message:unknownFieldSet];
       GPBUnknownField *field = [self mutableFieldForNumber:number create:YES];
       [field addGroup:unknownFieldSet];
+      // The field will now retain unknownFieldSet, so go ahead and release it in case
+      // -readUnknownGroup:message: throws so it won't be leaked.
       [unknownFieldSet release];
+      [input readUnknownGroup:number message:unknownFieldSet];
       return YES;
     }
     case GPBWireFormatEndGroup:
diff --git a/objectivec/GPBUnknownFieldSet_PackagePrivate.h b/objectivec/GPBUnknownFieldSet_PackagePrivate.h
index e27127a..b65bdaa 100644
--- a/objectivec/GPBUnknownFieldSet_PackagePrivate.h
+++ b/objectivec/GPBUnknownFieldSet_PackagePrivate.h
@@ -50,7 +50,6 @@
 - (void)mergeUnknownFields:(GPBUnknownFieldSet *)other;
 
 - (void)mergeFromCodedInputStream:(GPBCodedInputStream *)input;
-- (void)mergeFromData:(NSData *)data;
 
 - (void)mergeVarintField:(int32_t)number value:(int32_t)value;
 - (BOOL)mergeFieldFrom:(int32_t)tag input:(GPBCodedInputStream *)input;
diff --git a/objectivec/Tests/GPBCodedInputStreamTests.m b/objectivec/Tests/GPBCodedInputStreamTests.m
index 0504062..0d0d327 100644
--- a/objectivec/Tests/GPBCodedInputStreamTests.m
+++ b/objectivec/Tests/GPBCodedInputStreamTests.m
@@ -28,6 +28,7 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+#import <Foundation/Foundation.h>
 #import "GPBTestUtilities.h"
 
 #import "GPBCodedInputStream.h"
@@ -370,10 +371,23 @@
                                @"should throw a GPBCodedInputStreamException exception ");
 }
 
-- (void)testBytesWithNegativeSize {
-  NSData* data = bytes(0xFF, 0xFF, 0xFF, 0xFF, 0x0F);
+- (void)testBytesOver2GB {
+  NSData* data = bytes(0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x01, 0x02, 0x03);  // don't need all the bytes
   GPBCodedInputStream* input = [GPBCodedInputStream streamWithData:data];
-  XCTAssertNil([input readBytes]);
+  @try {
+    __unused NSData* result = [input readBytes];
+    XCTFail(@"Should have thrown");
+  } @catch (NSException* anException) {
+    // Ensure the correct error within the exception.
+    XCTAssertTrue([anException isKindOfClass:[NSException class]]);
+    XCTAssertEqualObjects(anException.name, GPBCodedInputStreamException);
+    NSDictionary* userInfo = anException.userInfo;
+    XCTAssertNotNil(userInfo);
+    NSError* err = userInfo[GPBCodedInputStreamUnderlyingErrorKey];
+    XCTAssertNotNil(err);
+    XCTAssertEqualObjects(err.domain, GPBCodedInputStreamErrorDomain);
+    XCTAssertEqual(err.code, GPBCodedInputStreamErrorInvalidSize);
+  }
 }
 
 // Verifies fix for b/10315336.
diff --git a/objectivec/Tests/GPBCodedOutputStreamTests.m b/objectivec/Tests/GPBCodedOutputStreamTests.m
index a619cae..6218f51 100644
--- a/objectivec/Tests/GPBCodedOutputStreamTests.m
+++ b/objectivec/Tests/GPBCodedOutputStreamTests.m
@@ -80,7 +80,9 @@
   NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory];
   GPBCodedOutputStream* output = [GPBCodedOutputStream streamWithOutputStream:rawOutput];
   [output writeRawLittleEndian32:(int32_t)value];
+  XCTAssertEqual(output.bytesWritten, data.length);
   [output flush];
+  XCTAssertEqual(output.bytesWritten, data.length);
 
   NSData* actual = [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
   XCTAssertEqualObjects(data, actual);
@@ -90,7 +92,9 @@
     rawOutput = [NSOutputStream outputStreamToMemory];
     output = [GPBCodedOutputStream streamWithOutputStream:rawOutput bufferSize:blockSize];
     [output writeRawLittleEndian32:(int32_t)value];
+    XCTAssertEqual(output.bytesWritten, data.length);
     [output flush];
+    XCTAssertEqual(output.bytesWritten, data.length);
 
     actual = [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
     XCTAssertEqualObjects(data, actual);
@@ -101,7 +105,9 @@
   NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory];
   GPBCodedOutputStream* output = [GPBCodedOutputStream streamWithOutputStream:rawOutput];
   [output writeRawLittleEndian64:value];
+  XCTAssertEqual(output.bytesWritten, data.length);
   [output flush];
+  XCTAssertEqual(output.bytesWritten, data.length);
 
   NSData* actual = [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
   XCTAssertEqualObjects(data, actual);
@@ -111,7 +117,9 @@
     rawOutput = [NSOutputStream outputStreamToMemory];
     output = [GPBCodedOutputStream streamWithOutputStream:rawOutput bufferSize:blockSize];
     [output writeRawLittleEndian64:value];
+    XCTAssertEqual(output.bytesWritten, data.length);
     [output flush];
+    XCTAssertEqual(output.bytesWritten, data.length);
 
     actual = [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
     XCTAssertEqualObjects(data, actual);
@@ -124,7 +132,9 @@
     NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory];
     GPBCodedOutputStream* output = [GPBCodedOutputStream streamWithOutputStream:rawOutput];
     [output writeRawVarint32:(int32_t)value];
+    XCTAssertEqual(output.bytesWritten, data.length);
     [output flush];
+    XCTAssertEqual(output.bytesWritten, data.length);
 
     NSData* actual = [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
     XCTAssertEqualObjects(data, actual);
@@ -137,7 +147,9 @@
     NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory];
     GPBCodedOutputStream* output = [GPBCodedOutputStream streamWithOutputStream:rawOutput];
     [output writeRawVarint64:value];
+    XCTAssertEqual(output.bytesWritten, data.length);
     [output flush];
+    XCTAssertEqual(output.bytesWritten, data.length);
 
     NSData* actual = [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
     XCTAssertEqualObjects(data, actual);
@@ -155,7 +167,9 @@
                                                                        bufferSize:blockSize];
 
       [output writeRawVarint32:(int32_t)value];
+      XCTAssertEqual(output.bytesWritten, data.length);
       [output flush];
+      XCTAssertEqual(output.bytesWritten, data.length);
 
       NSData* actual = [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
       XCTAssertEqualObjects(data, actual);
@@ -167,7 +181,9 @@
                                                                        bufferSize:blockSize];
 
       [output writeRawVarint64:value];
+      XCTAssertEqual(output.bytesWritten, data.length);
       [output flush];
+      XCTAssertEqual(output.bytesWritten, data.length);
 
       NSData* actual = [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
       XCTAssertEqualObjects(data, actual);
@@ -181,7 +197,9 @@
   NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory];
   GPBCodedOutputStream* output = [GPBCodedOutputStream streamWithOutputStream:rawOutput];
   [output writeStringNoTag:value];
+  XCTAssertEqual(output.bytesWritten, data.length);
   [output flush];
+  XCTAssertEqual(output.bytesWritten, data.length);
 
   NSData* actual = [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
   XCTAssertEqualObjects(data, actual, @"%@", contextMessage);
@@ -191,7 +209,9 @@
     rawOutput = [NSOutputStream outputStreamToMemory];
     output = [GPBCodedOutputStream streamWithOutputStream:rawOutput bufferSize:blockSize];
     [output writeStringNoTag:value];
+    XCTAssertEqual(output.bytesWritten, data.length);
     [output flush];
+    XCTAssertEqual(output.bytesWritten, data.length);
 
     actual = [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
     XCTAssertEqualObjects(data, actual, @"%@", contextMessage);
diff --git a/objectivec/Tests/GPBMessageTests+Serialization.m b/objectivec/Tests/GPBMessageTests+Serialization.m
index 3c2381c..5a7dc16 100644
--- a/objectivec/Tests/GPBMessageTests+Serialization.m
+++ b/objectivec/Tests/GPBMessageTests+Serialization.m
@@ -1266,12 +1266,17 @@
   XCTAssertEqual(error.code, GPBCodedInputStreamErrorRecursionDepthExceeded);
 }
 
-- (void)testParseDelimitedDataWithNegativeSize {
-  NSData *data = DataFromCStr("\xFF\xFF\xFF\xFF\x0F");
+- (void)testParseDelimitedDataOver2GB {
+  NSData *data = DataFromCStr("\xFF\xFF\xFF\xFF\x0F\x01\x02\0x3");  // Don't need all the bytes
   GPBCodedInputStream *input = [GPBCodedInputStream streamWithData:data];
   NSError *error;
-  [GPBMessage parseDelimitedFromCodedInputStream:input extensionRegistry:nil error:&error];
-  XCTAssertNil(error);
+  GPBMessage *result = [GPBMessage parseDelimitedFromCodedInputStream:input
+                                                    extensionRegistry:nil
+                                                                error:&error];
+  XCTAssertNil(result);
+  XCTAssertNotNil(error);
+  XCTAssertEqualObjects(error.domain, GPBCodedInputStreamErrorDomain);
+  XCTAssertEqual(error.code, GPBCodedInputStreamErrorInvalidSize);
 }
 
 #ifdef DEBUG
diff --git a/objectivec/Tests/GPBMessageTests.m b/objectivec/Tests/GPBMessageTests.m
index 6a1d48b..2e595f6 100644
--- a/objectivec/Tests/GPBMessageTests.m
+++ b/objectivec/Tests/GPBMessageTests.m
@@ -224,7 +224,11 @@
   result = [self mergeExtensionsDestination];
   NSData *data = [[self mergeExtensionsSource] data];
   XCTAssertNotNil(data);
-  [result mergeFromData:data extensionRegistry:[UnittestRoot extensionRegistry]];
+  NSError *error = nil;
+  XCTAssertTrue([result mergeFromData:data
+                    extensionRegistry:[UnittestRoot extensionRegistry]
+                                error:&error]);
+  XCTAssertNil(error);
   resultData = [result data];
   XCTAssertEqualObjects(resultData, mergeResultData);
   XCTAssertEqualObjects(result, [self mergeExtensionsResult]);
diff --git a/objectivec/Tests/GPBUnknownFieldSetTest.m b/objectivec/Tests/GPBUnknownFieldSetTest.m
index b98f52e..dbb6eb4 100644
--- a/objectivec/Tests/GPBUnknownFieldSetTest.m
+++ b/objectivec/Tests/GPBUnknownFieldSetTest.m
@@ -381,7 +381,8 @@
   NSData* data = [fields data];
 
   GPBUnknownFieldSet* parsed = [[[GPBUnknownFieldSet alloc] init] autorelease];
-  [parsed mergeFromData:data];
+  GPBCodedInputStream* input = [[[GPBCodedInputStream alloc] initWithData:data] autorelease];
+  [parsed mergeFromCodedInputStream:input];
   GPBUnknownField* field2 = [parsed getField:1];
   XCTAssertEqual(field2.varintList.count, (NSUInteger)1);
   XCTAssertEqual(0x7FFFFFFFFFFFFFFFULL, [field2.varintList valueAtIndex:0]);
diff --git a/objectivec/defs.bzl b/objectivec/defs.bzl
new file mode 100644
index 0000000..5535868
--- /dev/null
+++ b/objectivec/defs.bzl
@@ -0,0 +1,68 @@
+"""Starlark helpers for Objective-C protos."""
+
+# State constants for objc_proto_camel_case_name.
+_last_was_other = 0
+_last_was_lowercase = 1
+_last_was_uppercase = 2
+_last_was_number = 3
+
+def objc_proto_camel_case_name(name):
+    """A Starlark version of the ObjC generator's CamelCase name transform.
+
+    This needs to track
+    src/google/protobuf/compiler/objectivec/names.cc's UnderscoresToCamelCase()
+
+    NOTE: This code is written to model the C++ in protoc's ObjC generator so it
+    is easier to confirm that the algorithms between the implementations match.
+    The customizations for starlark performance are:
+    - The cascade within the loop is reordered and special cases "_" to
+      optimize for google3 inputs.
+    - The "last was" state is handled via integers instead of three booleans.
+
+    The `first_capitalized` argument in the C++ code is left off this code and
+    it acts as if the value were `True`.
+
+    Args:
+      name: The proto file name to convert to camel case. The extension should
+        already be removed.
+
+    Returns:
+      The converted proto name to camel case.
+    """
+    segments = []
+    current = ""
+    last_was = _last_was_other
+    for c in name.elems():
+        if c.islower():
+            # lowercase letter can follow a lowercase or uppercase letter.
+            if last_was != _last_was_lowercase and last_was != _last_was_uppercase:
+                segments.append(current)
+                current = c.upper()
+            else:
+                current += c
+            last_was = _last_was_lowercase
+        elif c == "_":  # more common than rest, special case it.
+            last_was = _last_was_other
+        elif c.isdigit():
+            if last_was != _last_was_number:
+                segments.append(current)
+                current = ""
+            current += c
+            last_was = _last_was_number
+        elif c.isupper():
+            if last_was != _last_was_uppercase:
+                segments.append(current)
+                current = c
+            else:
+                current += c.lower()
+            last_was = _last_was_uppercase
+        else:
+            last_was = _last_was_other
+    segments.append(current)
+    result = ""
+    for x in segments:
+        if x in ("Url", "Http", "Https"):
+            result += x.upper()
+        else:
+            result += x
+    return result
diff --git a/objectivec/generate_well_known_types.sh b/objectivec/generate_well_known_types.sh
index 7e23a99..4b77412 100755
--- a/objectivec/generate_well_known_types.sh
+++ b/objectivec/generate_well_known_types.sh
@@ -9,8 +9,7 @@
 readonly ObjCDir="${ScriptDir}"
 readonly ProtoRootDir="${ObjCDir}/.."
 
-# Invoke with BAZEL=bazelisk to use that instead.
-readonly BazelBin="${BAZEL:-bazel} ${BAZEL_STARTUP_FLAGS:-}"
+cd "${ProtoRootDir}"
 
 # Flag for continuous integration to check that everything is current.
 CHECK_ONLY=0
@@ -19,7 +18,11 @@
   shift
 fi
 
-cd "${ProtoRootDir}"
+readonly PROTOC_PATH="${PROTOC:-${ProtoRootDir}/bazel-bin/protoc}"
+if [[ ! -x "${PROTOC_PATH}" ]] ; then
+  echo "Failed to find executable protoc: ${PROTOC_PATH}"
+  exit 1
+fi
 
 if [[ ! -e src/google/protobuf/stubs/common.h ]]; then
   cat >&2 << __EOF__
@@ -29,9 +32,6 @@
   exit 1
 fi
 
-# Make sure the compiler is current.
-${BazelBin} build //:protoc $@
-
 cd src
 declare -a RUNTIME_PROTO_FILES=( \
   google/protobuf/any.proto \
@@ -50,7 +50,7 @@
 # Generate to a temp directory to see if they match.
 TMP_DIR=$(mktemp -d)
 trap "rm -rf ${TMP_DIR}" EXIT
-${ProtoRootDir}/bazel-bin/protoc --objc_out="${TMP_DIR}" ${RUNTIME_PROTO_FILES[@]}
+"${PROTOC_PATH}" --objc_out="${TMP_DIR}" ${RUNTIME_PROTO_FILES[@]}
 
 DID_COPY=0
 for PROTO_FILE in "${RUNTIME_PROTO_FILES[@]}"; do
diff --git a/php/ext/google/protobuf/array.c b/php/ext/google/protobuf/array.c
index 72c7809..09daffa 100644
--- a/php/ext/google/protobuf/array.c
+++ b/php/ext/google/protobuf/array.c
@@ -406,13 +406,13 @@
     return;
   }
 
-  if (size == 0 || index != size - 1) {
+  if (size == 0 || index < 0 || index >= size) {
     php_error_docref(NULL, E_USER_ERROR, "Cannot remove element at %ld.\n",
                      index);
     return;
   }
 
-  upb_Array_Resize(intern->array, size - 1, Arena_Get(&intern->arena));
+  upb_Array_Delete(intern->array, index, 1);
 }
 
 /**
diff --git a/php/ext/google/protobuf/php-upb.c b/php/ext/google/protobuf/php-upb.c
index 2dac3ad..ef95724 100644
--- a/php/ext/google/protobuf/php-upb.c
+++ b/php/ext/google/protobuf/php-upb.c
@@ -641,12 +641,14 @@
     [kUpb_FieldType_Bytes] = _upb_mapsorter_cmpstr,
 };
 
-static bool _upb_mapsorter_resize(_upb_mapsorter* s, _upb_sortedmap* sorted,
-                                  int size) {
+bool _upb_mapsorter_pushmap(_upb_mapsorter* s, upb_FieldType key_type,
+                            const upb_Map* map, _upb_sortedmap* sorted) {
+  int map_size = _upb_Map_Size(map);
   sorted->start = s->size;
   sorted->pos = sorted->start;
-  sorted->end = sorted->start + size;
+  sorted->end = sorted->start + map_size;
 
+  // Grow s->entries if necessary.
   if (sorted->end > s->cap) {
     s->cap = upb_Log2CeilingSize(sorted->end);
     s->entries = realloc(s->entries, s->cap * sizeof(*s->entries));
@@ -654,17 +656,9 @@
   }
 
   s->size = sorted->end;
-  return true;
-}
-
-bool _upb_mapsorter_pushmap(_upb_mapsorter* s, upb_FieldType key_type,
-                            const upb_Map* map, _upb_sortedmap* sorted) {
-  int map_size = _upb_Map_Size(map);
-
-  if (!_upb_mapsorter_resize(s, sorted, map_size)) return false;
 
   // Copy non-empty entries from the table to s->entries.
-  const void** dst = &s->entries[sorted->start];
+  upb_tabent const** dst = &s->entries[sorted->start];
   const upb_tabent* src = map->table.t.entries;
   const upb_tabent* end = src + upb_table_size(&map->table.t);
   for (; src < end; src++) {
@@ -680,29 +674,6 @@
         compar[key_type]);
   return true;
 }
-
-static int _upb_mapsorter_cmpext(const void* _a, const void* _b) {
-  const upb_Message_Extension* const* a = _a;
-  const upb_Message_Extension* const* b = _b;
-  uint32_t a_num = (*a)->ext->field.number;
-  uint32_t b_num = (*b)->ext->field.number;
-  assert(a_num != b_num);
-  return a_num < b_num ? -1 : 1;
-}
-
-bool _upb_mapsorter_pushexts(_upb_mapsorter* s,
-                             const upb_Message_Extension* exts, size_t count,
-                             _upb_sortedmap* sorted) {
-  if (!_upb_mapsorter_resize(s, sorted, count)) return false;
-
-  for (size_t i = 0; i < count; i++) {
-    s->entries[sorted->start + i] = &exts[i];
-  }
-
-  qsort(&s->entries[sorted->start], count, sizeof(*s->entries),
-        _upb_mapsorter_cmpext);
-  return true;
-}
 /* This file was generated by upbc (the upb compiler) from the input
  * file:
  *
@@ -2254,7 +2225,7 @@
   n = len + 1;
   p = upb_Arena_Malloc(a, n);
   if (p) {
-    if (len != 0) memcpy(p, s, len);
+    memcpy(p, s, len);
     p[len] = 0;
   }
   return p;
@@ -7590,27 +7561,9 @@
   _upb_DefBuilder_FailJmp(ctx);
 }
 
-// Verify a relative identifier string. The loop is branchless for speed.
-static void _upb_DefBuilder_CheckIdentNotFull(upb_DefBuilder* ctx,
-                                              upb_StringView name) {
-  bool good = name.size > 0;
-
-  for (size_t i = 0; i < name.size; i++) {
-    const char c = name.data[i];
-    const char d = c | 0x20;  // force lowercase
-    const bool is_alpha = (('a' <= d) & (d <= 'z')) | (c == '_');
-    const bool is_numer = ('0' <= c) & (c <= '9') & (i != 0);
-
-    good &= is_alpha | is_numer;
-  }
-
-  if (!good) _upb_DefBuilder_CheckIdentSlow(ctx, name, false);
-}
-
 const char* _upb_DefBuilder_MakeFullName(upb_DefBuilder* ctx,
                                          const char* prefix,
                                          upb_StringView name) {
-  _upb_DefBuilder_CheckIdentNotFull(ctx, name);
   if (prefix) {
     // ret = prefix + '.' + name;
     size_t n = strlen(prefix);
@@ -7726,7 +7679,7 @@
   return true;
 }
 
-static int TryGetHexDigit(const char** src, const char* end) {
+static char TryGetHexDigit(const char** src, const char* end) {
   char ch;
   if (!TryGetChar(src, end, &ch)) return -1;
   if ('0' <= ch && ch <= '9') {
@@ -7743,10 +7696,10 @@
 static char upb_DefBuilder_ParseHexEscape(upb_DefBuilder* ctx,
                                           const upb_FieldDef* f,
                                           const char** src, const char* end) {
-  int hex_digit = TryGetHexDigit(src, end);
+  char hex_digit = TryGetHexDigit(src, end);
   if (hex_digit < 0) {
     _upb_DefBuilder_Errf(
-        ctx, "\\x must be followed by at least one hex digit (field='%s')",
+        ctx, "\\x cannot be followed by non-hex digit in field '%s' default",
         upb_FieldDef_FullName(f));
     return 0;
   }
@@ -7922,7 +7875,7 @@
 }
 
 bool _upb_DefPool_InsertExt(upb_DefPool* s, const upb_MiniTableExtension* ext,
-                            const upb_FieldDef* f) {
+                            upb_FieldDef* f) {
   return upb_inttable_insert(&s->exts, (uintptr_t)ext, upb_value_constptr(f),
                              s->arena);
 }
@@ -8154,6 +8107,12 @@
     const upb_MiniTableFile* layout, upb_Status* status) {
   const upb_StringView name = UPB_DESC(FileDescriptorProto_name)(file_proto);
 
+  if (name.size == 0) {
+    upb_Status_SetErrorFormat(status,
+                              "missing name in google_protobuf_FileDescriptorProto");
+    return NULL;
+  }
+
   // Determine whether we already know about this file.
   {
     upb_value v;
@@ -8545,6 +8504,7 @@
   e->file = _upb_DefBuilder_File(ctx);
 
   name = UPB_DESC(EnumDescriptorProto_name)(enum_proto);
+  _upb_DefBuilder_CheckIdentNotFull(ctx, name);
 
   e->full_name = _upb_DefBuilder_MakeFullName(ctx, prefix, name);
   _upb_DefBuilder_Add(ctx, e->full_name,
@@ -9354,14 +9314,7 @@
   }
 
   const upb_StringView name = UPB_DESC(FieldDescriptorProto_name)(field_proto);
-
-  f->full_name = _upb_DefBuilder_MakeFullName(ctx, prefix, name);
-  f->label_ = (int)UPB_DESC(FieldDescriptorProto_label)(field_proto);
-  f->number_ = UPB_DESC(FieldDescriptorProto_number)(field_proto);
-  f->is_proto3_optional =
-      UPB_DESC(FieldDescriptorProto_proto3_optional)(field_proto);
-  f->msgdef = m;
-  f->scope.oneof = NULL;
+  _upb_DefBuilder_CheckIdentNotFull(ctx, name);
 
   f->has_json_name = UPB_DESC(FieldDescriptorProto_has_json_name)(field_proto);
   if (f->has_json_name) {
@@ -9373,6 +9326,14 @@
   }
   if (!f->json_name) _upb_DefBuilder_OomErr(ctx);
 
+  f->full_name = _upb_DefBuilder_MakeFullName(ctx, prefix, name);
+  f->label_ = (int)UPB_DESC(FieldDescriptorProto_label)(field_proto);
+  f->number_ = UPB_DESC(FieldDescriptorProto_number)(field_proto);
+  f->is_proto3_optional =
+      UPB_DESC(FieldDescriptorProto_proto3_optional)(field_proto);
+  f->msgdef = m;
+  f->scope.oneof = NULL;
+
   const bool has_type = UPB_DESC(FieldDescriptorProto_has_type)(field_proto);
   const bool has_type_name =
       UPB_DESC(FieldDescriptorProto_has_type_name)(field_proto);
@@ -9502,24 +9463,19 @@
   }
 
   _upb_MessageDef_InsertField(ctx, m, f);
-}
 
-upb_FieldDef* _upb_Extensions_New(
-    upb_DefBuilder* ctx, int n,
-    const UPB_DESC(FieldDescriptorProto) * const* protos, const char* prefix,
-    upb_MessageDef* m) {
-  _upb_DefType_CheckPadding(sizeof(upb_FieldDef));
-  upb_FieldDef* defs =
-      (upb_FieldDef*)_upb_DefBuilder_Alloc(ctx, sizeof(upb_FieldDef) * n);
+  if (!ctx->layout) return;
 
-  for (int i = 0; i < n; i++) {
-    upb_FieldDef* f = &defs[i];
-
-    _upb_FieldDef_CreateExt(ctx, prefix, protos[i], m, f);
-    f->index_ = i;
+  const upb_MiniTable* mt = upb_MessageDef_MiniTable(m);
+  const upb_MiniTableField* fields = mt->fields;
+  for (int i = 0; i < mt->field_count; i++) {
+    if (fields[i].number == f->number_) {
+      f->layout_index = i;
+      return;
+    }
   }
 
-  return defs;
+  UPB_ASSERT(false);  // It should be impossible to reach this point.
 }
 
 upb_FieldDef* _upb_FieldDefs_New(
@@ -9530,23 +9486,28 @@
   upb_FieldDef* defs =
       (upb_FieldDef*)_upb_DefBuilder_Alloc(ctx, sizeof(upb_FieldDef) * n);
 
-  uint32_t previous = 0;
-  for (int i = 0; i < n; i++) {
-    upb_FieldDef* f = &defs[i];
+  // If we are creating extensions then is_sorted will be NULL.
+  // If we are not creating extensions then is_sorted will be non-NULL.
+  if (is_sorted) {
+    uint32_t previous = 0;
+    for (int i = 0; i < n; i++) {
+      upb_FieldDef* f = &defs[i];
 
-    _upb_FieldDef_CreateNotExt(ctx, prefix, protos[i], m, f);
-    f->index_ = i;
-    if (!ctx->layout) {
-      // Speculate that the def fields are sorted.  We will always sort the
-      // MiniTable fields, so if defs are sorted then indices will match.
-      //
-      // If this is incorrect, we will overwrite later.
-      f->layout_index = i;
+      _upb_FieldDef_CreateNotExt(ctx, prefix, protos[i], m, f);
+      f->index_ = i;
+      if (!ctx->layout) f->layout_index = i;
+
+      const uint32_t current = f->number_;
+      if (previous > current) *is_sorted = false;
+      previous = current;
     }
+  } else {
+    for (int i = 0; i < n; i++) {
+      upb_FieldDef* f = &defs[i];
 
-    const uint32_t current = f->number_;
-    if (previous > current) *is_sorted = false;
-    previous = current;
+      _upb_FieldDef_CreateExt(ctx, prefix, protos[i], m, f);
+      f->index_ = i;
+    }
   }
 
   return defs;
@@ -9602,9 +9563,6 @@
   return (v1 < v2) ? -1 : (v1 > v2);
 }
 
-// _upb_FieldDefs_Sorted() is mostly a pure function of its inputs, but has one
-// critical side effect that we depend on: it sets layout_index appropriately
-// for non-sorted lists of fields.
 const upb_FieldDef** _upb_FieldDefs_Sorted(const upb_FieldDef* f, int n,
                                            upb_Arena* a) {
   // TODO(salo): Replace this arena alloc with a persistent scratch buffer.
@@ -9662,10 +9620,7 @@
         "field number %u in extension %s has no extension range in message %s",
         (unsigned)f->number_, f->full_name, upb_MessageDef_FullName(m));
   }
-}
 
-void _upb_FieldDef_BuildMiniTableExtension(upb_DefBuilder* ctx,
-                                           const upb_FieldDef* f) {
   const upb_MiniTableExtension* ext = _upb_FieldDef_ExtensionMiniTable(f);
 
   if (ctx->layout) {
@@ -9684,8 +9639,8 @@
       sub.subenum = _upb_EnumDef_MiniTable(f->sub.enumdef);
     }
     bool ok2 = upb_MiniTableExtension_Build(desc.data, desc.size, mut_ext,
-                                            upb_MessageDef_MiniTable(f->msgdef),
-                                            sub, ctx->status);
+                                            upb_MessageDef_MiniTable(m), sub,
+                                            ctx->status);
     if (!ok2) _upb_DefBuilder_Errf(ctx, "Could not build extension mini table");
   }
 
@@ -9742,7 +9697,6 @@
   const UPB_DESC(FileOptions) * opts;
   const char* name;
   const char* package;
-  const char* edition;
 
   const upb_FileDef** deps;
   const int32_t* public_deps;
@@ -9779,10 +9733,6 @@
   return f->package ? f->package : "";
 }
 
-const char* upb_FileDef_Edition(const upb_FileDef* f) {
-  return f->edition ? f->edition : "";
-}
-
 const char* _upb_FileDef_RawPackage(const upb_FileDef* f) { return f->package; }
 
 upb_Syntax upb_FileDef_Syntax(const upb_FileDef* f) { return f->syntax; }
@@ -9930,14 +9880,13 @@
     }
   }
 
-  upb_StringView name = UPB_DESC(FileDescriptorProto_name)(file_proto);
-  file->name = strviewdup(ctx, name);
-  if (strlen(file->name) != name.size) {
-    _upb_DefBuilder_Errf(ctx, "File name contained embedded NULL");
+  if (!UPB_DESC(FileDescriptorProto_has_name)(file_proto)) {
+    _upb_DefBuilder_Errf(ctx, "File has no name");
   }
 
-  upb_StringView package = UPB_DESC(FileDescriptorProto_package)(file_proto);
+  file->name = strviewdup(ctx, UPB_DESC(FileDescriptorProto_name)(file_proto));
 
+  upb_StringView package = UPB_DESC(FileDescriptorProto_package)(file_proto);
   if (package.size) {
     _upb_DefBuilder_CheckIdentFull(ctx, package);
     file->package = strviewdup(ctx, package);
@@ -9945,18 +9894,6 @@
     file->package = NULL;
   }
 
-  upb_StringView edition = UPB_DESC(FileDescriptorProto_edition)(file_proto);
-
-  if (edition.size == 0) {
-    file->edition = NULL;
-  } else {
-    // TODO(b/267770604): How should we validate this?
-    file->edition = strviewdup(ctx, edition);
-    if (strlen(file->edition) != edition.size) {
-      _upb_DefBuilder_Errf(ctx, "Edition name contained embedded NULL");
-    }
-  }
-
   if (UPB_DESC(FileDescriptorProto_has_syntax)(file_proto)) {
     upb_StringView syntax = UPB_DESC(FileDescriptorProto_syntax)(file_proto);
 
@@ -10025,7 +9962,8 @@
   // Create extensions.
   exts = UPB_DESC(FileDescriptorProto_extension)(file_proto, &n);
   file->top_lvl_ext_count = n;
-  file->top_lvl_exts = _upb_Extensions_New(ctx, n, exts, file->package, NULL);
+  file->top_lvl_exts =
+      _upb_FieldDefs_New(ctx, n, exts, file->package, NULL, NULL);
 
   // Create messages.
   msgs = UPB_DESC(FileDescriptorProto_message_type)(file_proto, &n);
@@ -10049,19 +9987,11 @@
     _upb_FieldDef_Resolve(ctx, file->package, f);
   }
 
-  for (int i = 0; i < file->top_lvl_msg_count; i++) {
-    upb_MessageDef* m = (upb_MessageDef*)upb_FileDef_TopLevelMessage(file, i);
-    _upb_MessageDef_CreateMiniTable(ctx, (upb_MessageDef*)m);
-  }
-
-  for (int i = 0; i < file->top_lvl_ext_count; i++) {
-    upb_FieldDef* f = (upb_FieldDef*)upb_FileDef_TopLevelExtension(file, i);
-    _upb_FieldDef_BuildMiniTableExtension(ctx, f);
-  }
-
-  for (int i = 0; i < file->top_lvl_msg_count; i++) {
-    upb_MessageDef* m = (upb_MessageDef*)upb_FileDef_TopLevelMessage(file, i);
-    _upb_MessageDef_LinkMiniTable(ctx, m);
+  if (!ctx->layout) {
+    for (int i = 0; i < file->top_lvl_msg_count; i++) {
+      upb_MessageDef* m = (upb_MessageDef*)upb_FileDef_TopLevelMessage(file, i);
+      _upb_MessageDef_LinkMiniTable(ctx, m);
+    }
   }
 
   if (file->ext_count) {
@@ -10574,8 +10504,6 @@
 static upb_MiniTable* _upb_MessageDef_MakeMiniTable(upb_DefBuilder* ctx,
                                                     const upb_MessageDef* m) {
   upb_StringView desc;
-  // Note: this will assign layout_index for fields, so upb_FieldDef_MiniTable()
-  // is safe to call only after this call.
   bool ok = upb_MessageDef_MiniDescriptorEncode(m, ctx->tmp_arena, &desc);
   if (!ok) _upb_DefBuilder_OomErr(ctx);
 
@@ -10595,6 +10523,23 @@
     _upb_FieldDef_Resolve(ctx, m->full_name, f);
   }
 
+  if (!ctx->layout) {
+    m->layout = _upb_MessageDef_MakeMiniTable(ctx, m);
+    if (!m->layout) _upb_DefBuilder_OomErr(ctx);
+  }
+
+#ifndef NDEBUG
+  for (int i = 0; i < m->field_count; i++) {
+    const upb_FieldDef* f = upb_MessageDef_Field(m, i);
+    const int layout_index = _upb_FieldDef_LayoutIndex(f);
+    UPB_ASSERT(layout_index < m->layout->field_count);
+    const upb_MiniTableField* mt_f = &m->layout->fields[layout_index];
+    UPB_ASSERT(upb_FieldDef_Type(f) == upb_MiniTableField_Type(mt_f));
+    UPB_ASSERT(upb_FieldDef_HasPresence(f) ==
+               upb_MiniTableField_HasPresence(mt_f));
+  }
+#endif
+
   m->in_message_set = false;
   for (int i = 0; i < upb_MessageDef_NestedExtensionCount(m); i++) {
     upb_FieldDef* ext = (upb_FieldDef*)upb_MessageDef_NestedExtension(m, i);
@@ -10657,39 +10602,8 @@
   if (!ok) _upb_DefBuilder_OomErr(ctx);
 }
 
-void _upb_MessageDef_CreateMiniTable(upb_DefBuilder* ctx, upb_MessageDef* m) {
-  if (ctx->layout == NULL) {
-    m->layout = _upb_MessageDef_MakeMiniTable(ctx, m);
-  } else {
-    UPB_ASSERT(ctx->msg_count < ctx->layout->msg_count);
-    m->layout = ctx->layout->msgs[ctx->msg_count++];
-    UPB_ASSERT(m->field_count == m->layout->field_count);
-
-    // We don't need the result of this call, but it will assign layout_index
-    // for all the fields in O(n lg n) time.
-    _upb_FieldDefs_Sorted(m->fields, m->field_count, ctx->tmp_arena);
-  }
-
-  for (int i = 0; i < m->nested_msg_count; i++) {
-    upb_MessageDef* nested =
-        (upb_MessageDef*)upb_MessageDef_NestedMessage(m, i);
-    _upb_MessageDef_CreateMiniTable(ctx, nested);
-  }
-}
-
 void _upb_MessageDef_LinkMiniTable(upb_DefBuilder* ctx,
                                    const upb_MessageDef* m) {
-  for (int i = 0; i < upb_MessageDef_NestedExtensionCount(m); i++) {
-    const upb_FieldDef* ext = upb_MessageDef_NestedExtension(m, i);
-    _upb_FieldDef_BuildMiniTableExtension(ctx, ext);
-  }
-
-  for (int i = 0; i < m->nested_msg_count; i++) {
-    _upb_MessageDef_LinkMiniTable(ctx, upb_MessageDef_NestedMessage(m, i));
-  }
-
-  if (ctx->layout) return;
-
   for (int i = 0; i < m->field_count; i++) {
     const upb_FieldDef* f = upb_MessageDef_Field(m, i);
     const upb_MessageDef* sub_m = upb_FieldDef_MessageSubDef(f);
@@ -10717,17 +10631,9 @@
     }
   }
 
-#ifndef NDEBUG
-  for (int i = 0; i < m->field_count; i++) {
-    const upb_FieldDef* f = upb_MessageDef_Field(m, i);
-    const int layout_index = _upb_FieldDef_LayoutIndex(f);
-    UPB_ASSERT(layout_index < m->layout->field_count);
-    const upb_MiniTableField* mt_f = &m->layout->fields[layout_index];
-    UPB_ASSERT(upb_FieldDef_Type(f) == upb_MiniTableField_Type(mt_f));
-    UPB_ASSERT(upb_FieldDef_HasPresence(f) ==
-               upb_MiniTableField_HasPresence(mt_f));
+  for (int i = 0; i < m->nested_msg_count; i++) {
+    _upb_MessageDef_LinkMiniTable(ctx, upb_MessageDef_NestedMessage(m, i));
   }
-#endif
 }
 
 static uint64_t _upb_MessageDef_Modifiers(const upb_MessageDef* m) {
@@ -10860,6 +10766,7 @@
   m->is_sorted = true;
 
   name = UPB_DESC(DescriptorProto_name)(msg_proto);
+  _upb_DefBuilder_CheckIdentNotFull(ctx, name);
 
   m->full_name = _upb_DefBuilder_MakeFullName(ctx, prefix, name);
   _upb_DefBuilder_Add(ctx, m->full_name, _upb_DefType_Pack(m, UPB_DEFTYPE_MSG));
@@ -10878,6 +10785,17 @@
   ok = upb_strtable_init(&m->ntof, n_oneof + n_field, ctx->arena);
   if (!ok) _upb_DefBuilder_OomErr(ctx);
 
+  if (ctx->layout) {
+    /* create_fielddef() below depends on this being set. */
+    UPB_ASSERT(ctx->msg_count < ctx->layout->msg_count);
+    m->layout = ctx->layout->msgs[ctx->msg_count++];
+    UPB_ASSERT(n_field == m->layout->field_count);
+  } else {
+    /* Allocate now (to allow cross-linking), populate later. */
+    m->layout = _upb_DefBuilder_Alloc(
+        ctx, sizeof(*m->layout) + sizeof(_upb_FastTable_Entry));
+  }
+
   UPB_DEF_SET_OPTIONS(m->opts, DescriptorProto, MessageOptions, msg_proto);
 
   m->oneof_count = n_oneof;
@@ -10918,7 +10836,7 @@
   const UPB_DESC(FieldDescriptorProto)* const* exts =
       UPB_DESC(DescriptorProto_extension)(msg_proto, &n_ext);
   m->nested_ext_count = n_ext;
-  m->nested_exts = _upb_Extensions_New(ctx, n_ext, exts, m->full_name, m);
+  m->nested_exts = _upb_FieldDefs_New(ctx, n_ext, exts, m->full_name, m, NULL);
 
   const UPB_DESC(DescriptorProto)* const* msgs =
       UPB_DESC(DescriptorProto_nested_type)(msg_proto, &n_msg);
@@ -11325,6 +11243,7 @@
   s->file = _upb_DefBuilder_File(ctx);
 
   name = UPB_DESC(ServiceDescriptorProto_name)(svc_proto);
+  _upb_DefBuilder_CheckIdentNotFull(ctx, name);
   const char* package = _upb_FileDef_RawPackage(s->file);
   s->full_name = _upb_DefBuilder_MakeFullName(ctx, package, name);
   _upb_DefBuilder_Add(ctx, s->full_name,
@@ -14092,15 +14011,6 @@
   encode_tag(e, kUpb_MsgSet_Item, kUpb_WireType_StartGroup);
 }
 
-static void encode_ext(upb_encstate* e, const upb_Message_Extension* ext,
-                       bool is_message_set) {
-  if (UPB_UNLIKELY(is_message_set)) {
-    encode_msgset_item(e, ext);
-  } else {
-    encode_field(e, &ext->data, &ext->ext->sub, &ext->ext->field);
-  }
-}
-
 static void encode_message(upb_encstate* e, const upb_Message* msg,
                            const upb_MiniTable* m, size_t* size) {
   size_t pre_len = e->limit - e->ptr;
@@ -14130,17 +14040,12 @@
     size_t ext_count;
     const upb_Message_Extension* ext = _upb_Message_Getexts(msg, &ext_count);
     if (ext_count) {
-      if (e->options & kUpb_EncodeOption_Deterministic) {
-        _upb_sortedmap sorted;
-        _upb_mapsorter_pushexts(&e->sorter, ext, ext_count, &sorted);
-        while (_upb_sortedmap_nextext(&e->sorter, &sorted, &ext)) {
-          encode_ext(e, ext, m->ext == kUpb_ExtMode_IsMessageSet);
-        }
-        _upb_mapsorter_popmap(&e->sorter, &sorted);
-      } else {
-        const upb_Message_Extension* end = ext + ext_count;
-        for (; ext != end; ext++) {
-          encode_ext(e, ext, m->ext == kUpb_ExtMode_IsMessageSet);
+      const upb_Message_Extension* end = ext + ext_count;
+      for (; ext != end; ext++) {
+        if (UPB_UNLIKELY(m->ext == kUpb_ExtMode_IsMessageSet)) {
+          encode_msgset_item(e, ext);
+        } else {
+          encode_field(e, &ext->data, &ext->ext->sub, &ext->ext->field);
         }
       }
     }
diff --git a/php/ext/google/protobuf/php-upb.h b/php/ext/google/protobuf/php-upb.h
index 3de3e6d..6b554cc 100644
--- a/php/ext/google/protobuf/php-upb.h
+++ b/php/ext/google/protobuf/php-upb.h
@@ -1422,6 +1422,179 @@
 #include <stdlib.h>
 
 
+#ifndef UPB_MINI_TABLE_MESSAGE_INTERNAL_H_
+#define UPB_MINI_TABLE_MESSAGE_INTERNAL_H_
+
+
+// Must be last.
+
+struct upb_Decoder;
+typedef const char* _upb_FieldParser(struct upb_Decoder* d, const char* ptr,
+                                     upb_Message* msg, intptr_t table,
+                                     uint64_t hasbits, uint64_t data);
+typedef struct {
+  uint64_t field_data;
+  _upb_FieldParser* field_parser;
+} _upb_FastTable_Entry;
+
+typedef enum {
+  kUpb_ExtMode_NonExtendable = 0,  // Non-extendable message.
+  kUpb_ExtMode_Extendable = 1,     // Normal extendable message.
+  kUpb_ExtMode_IsMessageSet = 2,   // MessageSet message.
+  kUpb_ExtMode_IsMessageSet_ITEM =
+      3,  // MessageSet item (temporary only, see decode.c)
+
+  // During table building we steal a bit to indicate that the message is a map
+  // entry.  *Only* used during table building!
+  kUpb_ExtMode_IsMapEntry = 4,
+} upb_ExtMode;
+
+// upb_MiniTable represents the memory layout of a given upb_MessageDef.
+// The members are public so generated code can initialize them,
+// but users MUST NOT directly read or write any of its members.
+struct upb_MiniTable {
+  const upb_MiniTableSub* subs;
+  const upb_MiniTableField* fields;
+
+  // Must be aligned to sizeof(void*). Doesn't include internal members like
+  // unknown fields, extension dict, pointer to msglayout, etc.
+  uint16_t size;
+
+  uint16_t field_count;
+  uint8_t ext;  // upb_ExtMode, declared as uint8_t so sizeof(ext) == 1
+  uint8_t dense_below;
+  uint8_t table_mask;
+  uint8_t required_count;  // Required fields have the lowest hasbits.
+
+  // To statically initialize the tables of variable length, we need a flexible
+  // array member, and we need to compile in gnu99 mode (constant initialization
+  // of flexible array members is a GNU extension, not in C99 unfortunately.
+  _upb_FastTable_Entry fasttable[];
+};
+
+// Map entries aren't actually stored for map fields, they are only used during
+// parsing. For parsing, it helps a lot if all map entry messages have the same
+// layout. The layout code in mini_table/decode.c will ensure that all map
+// entries have this layout.
+//
+// Note that users can and do create map entries directly, which will also use
+// this layout.
+//
+// NOTE: sync with mini_table/decode.c.
+typedef struct {
+  // We only need 2 hasbits max, but due to alignment we'll use 8 bytes here,
+  // and the uint64_t helps make this clear.
+  uint64_t hasbits;
+  union {
+    upb_StringView str;  // For str/bytes.
+    upb_value val;       // For all other types.
+  } k;
+  union {
+    upb_StringView str;  // For str/bytes.
+    upb_value val;       // For all other types.
+  } v;
+} upb_MapEntryData;
+
+typedef struct {
+  void* internal_data;
+  upb_MapEntryData data;
+} upb_MapEntry;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Computes a bitmask in which the |l->required_count| lowest bits are set,
+// except that we skip the lowest bit (because upb never uses hasbit 0).
+//
+// Sample output:
+//    requiredmask(1) => 0b10 (0x2)
+//    requiredmask(5) => 0b111110 (0x3e)
+UPB_INLINE uint64_t upb_MiniTable_requiredmask(const upb_MiniTable* l) {
+  int n = l->required_count;
+  assert(0 < n && n <= 63);
+  return ((1ULL << n) - 1) << 1;
+}
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+#endif /* UPB_MINI_TABLE_MESSAGE_INTERNAL_H_ */
+
+// Must be last.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// _upb_mapsorter sorts maps and provides ordered iteration over the entries.
+// Since maps can be recursive (map values can be messages which contain other
+// maps), _upb_mapsorter can contain a stack of maps.
+
+typedef struct {
+  upb_tabent const** entries;
+  int size;
+  int cap;
+} _upb_mapsorter;
+
+typedef struct {
+  int start;
+  int pos;
+  int end;
+} _upb_sortedmap;
+
+UPB_INLINE void _upb_mapsorter_init(_upb_mapsorter* s) {
+  s->entries = NULL;
+  s->size = 0;
+  s->cap = 0;
+}
+
+UPB_INLINE void _upb_mapsorter_destroy(_upb_mapsorter* s) {
+  if (s->entries) free(s->entries);
+}
+
+UPB_INLINE bool _upb_sortedmap_next(_upb_mapsorter* s, const upb_Map* map,
+                                    _upb_sortedmap* sorted, upb_MapEntry* ent) {
+  if (sorted->pos == sorted->end) return false;
+  const upb_tabent* tabent = s->entries[sorted->pos++];
+  upb_StringView key = upb_tabstrview(tabent->key);
+  _upb_map_fromkey(key, &ent->data.k, map->key_size);
+  upb_value val = {tabent->val.val};
+  _upb_map_fromvalue(val, &ent->data.v, map->val_size);
+  return true;
+}
+
+UPB_INLINE void _upb_mapsorter_popmap(_upb_mapsorter* s,
+                                      _upb_sortedmap* sorted) {
+  s->size = sorted->start;
+}
+
+bool _upb_mapsorter_pushmap(_upb_mapsorter* s, upb_FieldType key_type,
+                            const upb_Map* map, _upb_sortedmap* sorted);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+#endif /* UPB_COLLECTIONS_MAP_SORTER_INTERNAL_H_ */
+
+/*
+** Our memory representation for parsing tables and messages themselves.
+** Functions in this file are used by generated code and possibly reflection.
+**
+** The definitions in this file are internal to upb.
+**/
+
+#ifndef UPB_MESSAGE_INTERNAL_H_
+#define UPB_MESSAGE_INTERNAL_H_
+
+#include <stdlib.h>
+#include <string.h>
+
+
 #ifndef UPB_MESSAGE_EXTENSION_INTERNAL_H_
 #define UPB_MESSAGE_EXTENSION_INTERNAL_H_
 
@@ -1694,191 +1867,6 @@
 
 #endif /* UPB_MESSAGE_EXTENSION_INTERNAL_H_ */
 
-#ifndef UPB_MINI_TABLE_MESSAGE_INTERNAL_H_
-#define UPB_MINI_TABLE_MESSAGE_INTERNAL_H_
-
-
-// Must be last.
-
-struct upb_Decoder;
-typedef const char* _upb_FieldParser(struct upb_Decoder* d, const char* ptr,
-                                     upb_Message* msg, intptr_t table,
-                                     uint64_t hasbits, uint64_t data);
-typedef struct {
-  uint64_t field_data;
-  _upb_FieldParser* field_parser;
-} _upb_FastTable_Entry;
-
-typedef enum {
-  kUpb_ExtMode_NonExtendable = 0,  // Non-extendable message.
-  kUpb_ExtMode_Extendable = 1,     // Normal extendable message.
-  kUpb_ExtMode_IsMessageSet = 2,   // MessageSet message.
-  kUpb_ExtMode_IsMessageSet_ITEM =
-      3,  // MessageSet item (temporary only, see decode.c)
-
-  // During table building we steal a bit to indicate that the message is a map
-  // entry.  *Only* used during table building!
-  kUpb_ExtMode_IsMapEntry = 4,
-} upb_ExtMode;
-
-// upb_MiniTable represents the memory layout of a given upb_MessageDef.
-// The members are public so generated code can initialize them,
-// but users MUST NOT directly read or write any of its members.
-struct upb_MiniTable {
-  const upb_MiniTableSub* subs;
-  const upb_MiniTableField* fields;
-
-  // Must be aligned to sizeof(void*). Doesn't include internal members like
-  // unknown fields, extension dict, pointer to msglayout, etc.
-  uint16_t size;
-
-  uint16_t field_count;
-  uint8_t ext;  // upb_ExtMode, declared as uint8_t so sizeof(ext) == 1
-  uint8_t dense_below;
-  uint8_t table_mask;
-  uint8_t required_count;  // Required fields have the lowest hasbits.
-
-  // To statically initialize the tables of variable length, we need a flexible
-  // array member, and we need to compile in gnu99 mode (constant initialization
-  // of flexible array members is a GNU extension, not in C99 unfortunately.
-  _upb_FastTable_Entry fasttable[];
-};
-
-// Map entries aren't actually stored for map fields, they are only used during
-// parsing. For parsing, it helps a lot if all map entry messages have the same
-// layout. The layout code in mini_table/decode.c will ensure that all map
-// entries have this layout.
-//
-// Note that users can and do create map entries directly, which will also use
-// this layout.
-//
-// NOTE: sync with mini_table/decode.c.
-typedef struct {
-  // We only need 2 hasbits max, but due to alignment we'll use 8 bytes here,
-  // and the uint64_t helps make this clear.
-  uint64_t hasbits;
-  union {
-    upb_StringView str;  // For str/bytes.
-    upb_value val;       // For all other types.
-  } k;
-  union {
-    upb_StringView str;  // For str/bytes.
-    upb_value val;       // For all other types.
-  } v;
-} upb_MapEntryData;
-
-typedef struct {
-  void* internal_data;
-  upb_MapEntryData data;
-} upb_MapEntry;
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Computes a bitmask in which the |l->required_count| lowest bits are set,
-// except that we skip the lowest bit (because upb never uses hasbit 0).
-//
-// Sample output:
-//    requiredmask(1) => 0b10 (0x2)
-//    requiredmask(5) => 0b111110 (0x3e)
-UPB_INLINE uint64_t upb_MiniTable_requiredmask(const upb_MiniTable* l) {
-  int n = l->required_count;
-  assert(0 < n && n <= 63);
-  return ((1ULL << n) - 1) << 1;
-}
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-
-#endif /* UPB_MINI_TABLE_MESSAGE_INTERNAL_H_ */
-
-// Must be last.
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// _upb_mapsorter sorts maps and provides ordered iteration over the entries.
-// Since maps can be recursive (map values can be messages which contain other
-// maps), _upb_mapsorter can contain a stack of maps.
-
-typedef struct {
-  void const** entries;
-  int size;
-  int cap;
-} _upb_mapsorter;
-
-typedef struct {
-  int start;
-  int pos;
-  int end;
-} _upb_sortedmap;
-
-UPB_INLINE void _upb_mapsorter_init(_upb_mapsorter* s) {
-  s->entries = NULL;
-  s->size = 0;
-  s->cap = 0;
-}
-
-UPB_INLINE void _upb_mapsorter_destroy(_upb_mapsorter* s) {
-  if (s->entries) free(s->entries);
-}
-
-UPB_INLINE bool _upb_sortedmap_next(_upb_mapsorter* s, const upb_Map* map,
-                                    _upb_sortedmap* sorted, upb_MapEntry* ent) {
-  if (sorted->pos == sorted->end) return false;
-  const upb_tabent* tabent = (const upb_tabent*)s->entries[sorted->pos++];
-  upb_StringView key = upb_tabstrview(tabent->key);
-  _upb_map_fromkey(key, &ent->data.k, map->key_size);
-  upb_value val = {tabent->val.val};
-  _upb_map_fromvalue(val, &ent->data.v, map->val_size);
-  return true;
-}
-
-UPB_INLINE bool _upb_sortedmap_nextext(_upb_mapsorter* s,
-                                       _upb_sortedmap* sorted,
-                                       const upb_Message_Extension** ext) {
-  if (sorted->pos == sorted->end) return false;
-  *ext = (const upb_Message_Extension*)s->entries[sorted->pos++];
-  return true;
-}
-
-UPB_INLINE void _upb_mapsorter_popmap(_upb_mapsorter* s,
-                                      _upb_sortedmap* sorted) {
-  s->size = sorted->start;
-}
-
-bool _upb_mapsorter_pushmap(_upb_mapsorter* s, upb_FieldType key_type,
-                            const upb_Map* map, _upb_sortedmap* sorted);
-
-bool _upb_mapsorter_pushexts(_upb_mapsorter* s,
-                             const upb_Message_Extension* exts, size_t count,
-                             _upb_sortedmap* sorted);
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-
-#endif /* UPB_COLLECTIONS_MAP_SORTER_INTERNAL_H_ */
-
-/*
-** Our memory representation for parsing tables and messages themselves.
-** Functions in this file are used by generated code and possibly reflection.
-**
-** The definitions in this file are internal to upb.
-**/
-
-#ifndef UPB_MESSAGE_INTERNAL_H_
-#define UPB_MESSAGE_INTERNAL_H_
-
-#include <stdlib.h>
-#include <string.h>
-
-
 #ifndef UPB_MINI_TABLE_EXTENSION_REGISTRY_H_
 #define UPB_MINI_TABLE_EXTENSION_REGISTRY_H_
 
@@ -8338,7 +8326,6 @@
 const char* upb_FileDef_Name(const upb_FileDef* f);
 const UPB_DESC(FileOptions) * upb_FileDef_Options(const upb_FileDef* f);
 const char* upb_FileDef_Package(const upb_FileDef* f);
-const char* upb_FileDef_Edition(const upb_FileDef* f);
 const upb_DefPool* upb_FileDef_Pool(const upb_FileDef* f);
 
 const upb_FileDef* upb_FileDef_PublicDependency(const upb_FileDef* f, int i);
@@ -8724,7 +8711,7 @@
 upb_ExtensionRegistry* _upb_DefPool_ExtReg(const upb_DefPool* s);
 
 bool _upb_DefPool_InsertExt(upb_DefPool* s, const upb_MiniTableExtension* ext,
-                            const upb_FieldDef* f);
+                            upb_FieldDef* f);
 bool _upb_DefPool_InsertSym(upb_DefPool* s, upb_StringView sym, upb_value v,
                             upb_Status* status);
 bool _upb_DefPool_LookupSym(const upb_DefPool* s, const char* sym, size_t size,
@@ -10210,6 +10197,23 @@
 void _upb_DefBuilder_CheckIdentSlow(upb_DefBuilder* ctx, upb_StringView name,
                                     bool full);
 
+// Verify a relative identifier string. The loop is branchless for speed.
+UPB_INLINE void _upb_DefBuilder_CheckIdentNotFull(upb_DefBuilder* ctx,
+                                                  upb_StringView name) {
+  bool good = name.size > 0;
+
+  for (size_t i = 0; i < name.size; i++) {
+    const char c = name.data[i];
+    const char d = c | 0x20;  // force lowercase
+    const bool is_alpha = (('a' <= d) & (d <= 'z')) | (c == '_');
+    const bool is_numer = ('0' <= c) & (c <= '9') & (i != 0);
+
+    good &= is_alpha | is_numer;
+  }
+
+  if (!good) _upb_DefBuilder_CheckIdentSlow(ctx, name, false);
+}
+
 // Verify a full identifier string. This is slightly more complicated than
 // verifying a relative identifier string because we must track '.' chars.
 UPB_INLINE void _upb_DefBuilder_CheckIdentFull(upb_DefBuilder* ctx,
@@ -10313,14 +10317,6 @@
 uint64_t _upb_FieldDef_Modifiers(const upb_FieldDef* f);
 void _upb_FieldDef_Resolve(upb_DefBuilder* ctx, const char* prefix,
                            upb_FieldDef* f);
-void _upb_FieldDef_BuildMiniTableExtension(upb_DefBuilder* ctx,
-                                           const upb_FieldDef* f);
-
-// Allocate and initialize an array of |n| extensions (field defs).
-upb_FieldDef* _upb_Extensions_New(
-    upb_DefBuilder* ctx, int n,
-    const UPB_DESC(FieldDescriptorProto) * const* protos, const char* prefix,
-    upb_MessageDef* m);
 
 // Allocate and initialize an array of |n| field defs.
 upb_FieldDef* _upb_FieldDefs_New(
@@ -10385,7 +10381,6 @@
 void _upb_MessageDef_InsertField(upb_DefBuilder* ctx, upb_MessageDef* m,
                                  const upb_FieldDef* f);
 bool _upb_MessageDef_IsValidExtensionNumber(const upb_MessageDef* m, int n);
-void _upb_MessageDef_CreateMiniTable(upb_DefBuilder* ctx, upb_MessageDef* m);
 void _upb_MessageDef_LinkMiniTable(upb_DefBuilder* ctx,
                                    const upb_MessageDef* m);
 void _upb_MessageDef_Resolve(upb_DefBuilder* ctx, upb_MessageDef* m);
diff --git a/php/generate_descriptor_protos.sh b/php/generate_descriptor_protos.sh
index a9af40c..56d95d8 100755
--- a/php/generate_descriptor_protos.sh
+++ b/php/generate_descriptor_protos.sh
@@ -6,11 +6,11 @@
 set -e
 
 if [[ -z "${PROTOC}" ]]; then
-  PROTOC=$(realpath protoc)
+  PROTOC=$(pwd)/protoc
 fi
 if [ ! -f $PROTOC ]; then
   ${BAZEL:-bazel} $BAZEL_STARTUP_FLAGS build -c opt //:protoc $BAZEL_FLAGS
-  PROTOC=$(realpath bazel-bin/protoc)
+  PROTOC=$(pwd)/bazel-bin/protoc
 fi
 
 if test ! -e src/google/protobuf/stubs/common.h; then
diff --git a/php/src/Google/Protobuf/Internal/RepeatedField.php b/php/src/Google/Protobuf/Internal/RepeatedField.php
index ea7971f..f6ecd1c 100644
--- a/php/src/Google/Protobuf/Internal/RepeatedField.php
+++ b/php/src/Google/Protobuf/Internal/RepeatedField.php
@@ -219,13 +219,13 @@
     public function offsetUnset($offset)
     {
         $count = count($this->container);
-        if (!is_numeric($offset) || $count === 0 || $offset !== $count - 1) {
+        if (!is_numeric($offset) || $count === 0 || $offset < 0 || $offset >= $count) {
             trigger_error(
                 "Cannot remove element at the given index",
                 E_USER_ERROR);
             return;
         }
-        array_pop($this->container);
+        array_splice($this->container, $offset, 1);
     }
 
     /**
diff --git a/php/tests/ArrayTest.php b/php/tests/ArrayTest.php
index 9e8fcb8..2cade79 100644
--- a/php/tests/ArrayTest.php
+++ b/php/tests/ArrayTest.php
@@ -655,4 +655,31 @@
         $arr2 = clone $arr;
         $this->assertSame($arr[0], $arr2[0]);
     }
+
+    #########################################################
+    # Test offsetUnset
+    #########################################################
+
+    public function testOffsetUnset()
+    {
+        $arr = new RepeatedField(GPBType::INT32);
+        $arr[] = 0;
+        $arr[] = 1;
+        $arr[] = 2;
+
+        $this->assertSame(3, count($arr));
+        $this->assertSame(0, $arr[0]);
+        $this->assertSame(1, $arr[1]);
+        $this->assertSame(2, $arr[2]);
+
+        $arr->offsetUnset(1);
+        $this->assertSame(0, $arr[0]);
+        $this->assertSame(2, $arr[1]);
+
+        $arr->offsetUnset(0);
+        $this->assertSame(2, $arr[0]);
+
+        $arr->offsetUnset(0);
+        $this->assertCount(0, $arr);
+    }
 }
diff --git a/php/tests/compile_extension.sh b/php/tests/compile_extension.sh
index 5f7a6cc..91ed1d4 100755
--- a/php/tests/compile_extension.sh
+++ b/php/tests/compile_extension.sh
@@ -33,6 +33,6 @@
   echo "$FINGERPRINT" > BUILD_STAMP
 fi
 
-make
-TEST_PHP_ARGS="-q" make test
+make -j8
+TEST_PHP_ARGS="-q" make -j8 test
 popd > /dev/null
diff --git a/pkg/BUILD.bazel b/pkg/BUILD.bazel
index 4edda57..4f7e446 100644
--- a/pkg/BUILD.bazel
+++ b/pkg/BUILD.bazel
@@ -404,6 +404,7 @@
         "//src/google/protobuf/compiler/php",
         "//src/google/protobuf/compiler/python",
         "//src/google/protobuf/compiler/ruby",
+        "//src/google/protobuf/compiler/rust",
     ],
     dist_deps = [
         ":protobuf",
diff --git a/protobuf_deps.bzl b/protobuf_deps.bzl
index 5686623..fee1e9c 100644
--- a/protobuf_deps.bzl
+++ b/protobuf_deps.bzl
@@ -149,7 +149,7 @@
         _github_archive(
             name = "upb",
             repo = "https://github.com/protocolbuffers/upb",
-            commit = "9d69c47896648691e854b7078478ed4320efbcab",
-            sha256 = "4b67f05495abfd886227fdae841cdf8c54d0e99b9907ed15b318a451082e9647",
+            commit = "662497f1d3dcced2bba1620cea9aae8b484bd3cd",
+            sha256 = "57c87ca4145d2cbc162a6c613b114b9325b577f4f6525bd78747a34b3d03627c",
             patches = ["@com_google_protobuf//build_defs:upb.patch"],
         )
diff --git a/python/README.md b/python/README.md
index 6549fb6..dc4b1d0 100644
--- a/python/README.md
+++ b/python/README.md
@@ -1,130 +1,105 @@
-Protocol Buffers - Google's data interchange format
-===================================================
+# Protocol Buffers Python
 
-Copyright 2008 Google Inc.
+This directory contains the Protobuf library for Python.
 
-This directory contains the Python Protocol Buffers runtime library.
+In most cases you should install the library using `pip` or another package
+manager:
 
-Normally, this directory comes as part of the protobuf package, available
-from:
+```
+$ pip install protobuf
+```
 
-  https://developers.google.com/protocol-buffers/
+The packages released on https://pypi.org/project/protobuf/#files include both a
+source distribution and binary wheels.
 
-The complete package includes the C++ source code, which includes the
-Protocol Compiler (protoc).  If you downloaded this package from PyPI
-or some other Python-specific source, you may have received only the
-Python part of the code.  In this case, you will need to obtain the
-Protocol Compiler from some other source before you can use this
-package.
+For user documentation about how to use Protobuf Python, see
+https://protobuf.dev/getting-started/pythontutorial/
 
-Development Warning
-===================
+# Building packages from this repo
 
-The pure python performance is slow. For better performance please
-use python c++ implementation.
+If for some reason you wish to build the packages directly from this repo, you
+can use the following Bazel commands:
 
-Installation
-============
+```
+$ bazel build @upb//python/dist:source_wheel
+$ bazel build @upb//python/dist:binary_wheel
+```
 
-1) Make sure you have Python 3.7 or newer.  If in doubt, run:
+The binary wheel will build against whatever version of Python is installed on
+your system. The source package is always the same and does not depend on a
+local version of Python.
 
-       $ python -V
+# Implementation backends
 
-2) Make sure you have Bazel 0.5.4 or later (or CMake 3.5 or later).
+There are three separate implementations of Python Protobuf. All of them offer
+the same API and are thus functionally the same, though they have very different
+performance characteristics.
 
-3) If you do not have setuptools installed, note that it will be
-   downloaded and installed automatically as soon as you run `setup.py`.
-   If you would rather install it manually, you may do so by following
-   the instructions on [this page](https://packaging.python.org/en/latest/tutorials/installing-packages/).
+The runtime library contains a switching layer that can choose between these
+backends at runtime. Normally it will choose between them automatically, using
+priority-ordered list, and skipping any backends that are not available. However
+you can request a specific backend by setting the
+`PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION` environment variable to one of the
+following values:
 
-4) Build the C++ code, or install a binary distribution of `protoc`.  If
-   you install a binary distribution, make sure that it is the same
-   version as this package.  If in doubt, run:
+1.  **upb**: Built on the
+    [upb C library](https://github.com/protocolbuffers/upb), this is a new
+    extension module
+    [released in 4.21.0](https://protobuf.dev/news/2022-05-06/). It offers
+    better performance than any of the previous backends, and it is now the
+    default. It is distributed in our PyPI packages, and requires no special
+    installation. The code for this module lives in
+    [@upb/python](https://github.com/protocolbuffers/upb/tree/main/python).
+1.  **cpp**: This extension module wraps the C++ protobuf library. It is
+    deprecated and is no longer released in our PyPI packages, however it is
+    still used in some legacy cases where apps want to perform zero-copy message
+    sharing between Python and C++. It must be installed separately before it
+    can be used. The code for this module lives in
+    [google/protobuf/pyext](https://github.com/protocolbuffers/protobuf/tree/main/python/google/protobuf/pyext).
+1.  **python**: The pure-Python backend, this does not require any extension
+    module to be present on the system. The code for the pure-Python backend
+    lives in [google/protobuf/internal](google/protobuf/internal)
 
-       $ protoc --version
+The order given above is the general priority order, with `upb` being preferred
+the most and the `python` backend preferred the least. However this ordering can
+be overridden by the presence of a
+`google.protobuf.internal._api_implementation` module. See the logic in
+[api_implementation.py](https://github.com/protocolbuffers/protobuf/blob/main/python/google/protobuf/internal/api_implementation.py)
+for details.
 
-5) Build and run the tests:
+You can check which backend you are using with the following snippet:
 
-       $ python setup.py build
-       $ python setup.py test
+```
+$ python
+Python 3.10.9 (main, Dec  7 2022, 13:47:07) [GCC 12.2.0] on linux
+Type "help", "copyright", "credits" or "license" for more information.
+>>> from google.protobuf.internal import api_implementation
+>>> print(api_implementation.Type())
+upb
+```
 
-   To build, test, and use the C++ implementation, you must first compile
-   `libprotobuf.so` using either [Bazel](../README.md) or [CMake](../src/README.md):
+This is not an officially-supported or stable API, but it is useful for ad hoc
+diagnostics.
 
-   On OS X:
+More information about sharing messages between Python and C++ is available
+here: https://protobuf.dev/reference/python/python-generated/#sharing-messages
 
-   If you are running a Homebrew-provided Python, you must make sure another
-   version of protobuf is not already installed, as Homebrew's Python will
-   search `/usr/local/lib` for `libprotobuf.so` before it searches the compiled
-   binaries.
+# Code generator
 
-   You can either unlink Homebrew's protobuf or install the `libprotobuf` you
-   built earlier:
+The code for the Protobuf Python code generator lives in
+[//src/google/protobuf/compiler/python](https://github.com/protocolbuffers/protobuf/tree/main/src/google/protobuf/compiler/python).
+The code generator can output two different files for each proto `foo.proto`:
 
-       $ brew unlink protobuf
+*   **foo_pb2.py**: The module you import to actually use the protos.
+*   **foo_pb2.pyi**: A stub file that describes the interface of the protos.
 
-   or
+The `foo_pb2.pyi` file is useful for IDEs or for users who want to read the
+output file. The `foo_pb2.py` file is optimized for fast loading and is not
+readable at all.
 
-       $ (cd .. && cmake . && make install)
+Note that the pyi file is only generated if you pass the `pyi_out` option to
+`protoc`:
 
-    On other *nix:
-
-    You must make `libprotobuf.so` dynamically available. You can either
-    install libprotobuf you built earlier, or set `LD_LIBRARY_PATH`:
-
-       $ (cd .. && cmake . && make -j20 install)
-
-    or
-
-       $ export LD_LIBRARY_PATH=../bazel-bin
-
-   To build the C++ implementation run:
-
-       $ python setup.py build --cpp_implementation
-
-   Then run the tests like so:
-
-       $ python setup.py test --cpp_implementation
-
-   If some tests fail, this library may not work correctly on your
-   system.  Continue at your own risk.
-
-   Please note that there is a known problem with some versions of
-   Python on Cygwin which causes the tests to fail after printing the
-   error:  `sem_init: Resource temporarily unavailable`.  This appears
-   to be a [bug either in Cygwin or in
-   Python](http://www.cygwin.com/ml/cygwin/2005-07/msg01378.html).
-
-   We do not know if or when it might be fixed.  We also do not know
-   how likely it is that this bug will affect users in practice.
-
-6) Install:
-
-       $ python setup.py install
-
-   or:
-
-       $ (cd .. && make install)
-       $ python setup.py install --cpp_implementation
-
-   This step may require superuser privileges.
-   NOTE: To use C++ implementation, you need to export an environment
-   variable before running your program.  See the "C++ Implementation"
-   section below for more details.
-
-Usage
-=====
-
-The complete documentation for Protocol Buffers is available via the
-web at:
-
-  https://developers.google.com/protocol-buffers/
-
-C++ Implementation
-==================
-
-The C++ implementation for Python messages is built as a Python extension to
-improve the overall protobuf Python performance.
-
-To use the C++ implementation, you need to install the C++ protobuf runtime
-library, please see instructions in the parent directory.
+```
+$ protoc --python_out=pyi_out:output_dir
+```
diff --git a/python/google/protobuf/internal/generator_test.py b/python/google/protobuf/internal/generator_test.py
index 291b97d..a4370ac 100644
--- a/python/google/protobuf/internal/generator_test.py
+++ b/python/google/protobuf/internal/generator_test.py
@@ -49,6 +49,7 @@
 from google.protobuf import unittest_mset_pb2
 from google.protobuf import unittest_mset_wire_format_pb2
 from google.protobuf import unittest_pb2
+from google.protobuf import unittest_retention_pb2
 from google.protobuf import unittest_custom_options_pb2
 from google.protobuf import unittest_no_generic_services_pb2
 
@@ -152,6 +153,82 @@
     # TODO(gps): We really should test for the presence of the enum_opt1
     # extension and for its value to be set to -789.
 
+  # Options that are explicitly marked RETENTION_SOURCE should not be present
+  # in the descriptors in the binary.
+  def testOptionRetention(self):
+    # Direct options
+    options = unittest_retention_pb2.DESCRIPTOR.GetOptions()
+    self.assertTrue(options.HasExtension(unittest_retention_pb2.plain_option))
+    self.assertTrue(
+        options.HasExtension(unittest_retention_pb2.runtime_retention_option)
+    )
+    self.assertFalse(
+        options.HasExtension(unittest_retention_pb2.source_retention_option)
+    )
+
+    def check_options_message_is_stripped_correctly(options):
+      self.assertEqual(options.plain_field, 1)
+      self.assertEqual(options.runtime_retention_field, 2)
+      self.assertFalse(options.HasField('source_retention_field'))
+      self.assertEqual(options.source_retention_field, 0)
+
+    # Verify that our test OptionsMessage is stripped correctly on all
+    # different entity types.
+    check_options_message_is_stripped_correctly(
+        options.Extensions[unittest_retention_pb2.file_option]
+    )
+    check_options_message_is_stripped_correctly(
+        unittest_retention_pb2.TopLevelMessage.DESCRIPTOR.GetOptions().Extensions[
+            unittest_retention_pb2.message_option
+        ]
+    )
+    check_options_message_is_stripped_correctly(
+        unittest_retention_pb2.TopLevelMessage.NestedMessage.DESCRIPTOR.GetOptions().Extensions[
+            unittest_retention_pb2.message_option
+        ]
+    )
+    check_options_message_is_stripped_correctly(
+        unittest_retention_pb2._TOPLEVELENUM.GetOptions().Extensions[
+            unittest_retention_pb2.enum_option
+        ]
+    )
+    check_options_message_is_stripped_correctly(
+        unittest_retention_pb2._TOPLEVELMESSAGE_NESTEDENUM.GetOptions().Extensions[
+            unittest_retention_pb2.enum_option
+        ]
+    )
+    check_options_message_is_stripped_correctly(
+        unittest_retention_pb2._TOPLEVELENUM.values[0]
+        .GetOptions()
+        .Extensions[unittest_retention_pb2.enum_entry_option]
+    )
+    check_options_message_is_stripped_correctly(
+        unittest_retention_pb2.DESCRIPTOR.extensions_by_name['i']
+        .GetOptions()
+        .Extensions[unittest_retention_pb2.field_option]
+    )
+    check_options_message_is_stripped_correctly(
+        unittest_retention_pb2.TopLevelMessage.DESCRIPTOR.fields[0]
+        .GetOptions()
+        .Extensions[unittest_retention_pb2.field_option]
+    )
+    check_options_message_is_stripped_correctly(
+        unittest_retention_pb2.TopLevelMessage.DESCRIPTOR.oneofs[0]
+        .GetOptions()
+        .Extensions[unittest_retention_pb2.oneof_option]
+    )
+    check_options_message_is_stripped_correctly(
+        unittest_retention_pb2.DESCRIPTOR.services_by_name['Service']
+        .GetOptions()
+        .Extensions[unittest_retention_pb2.service_option]
+    )
+    check_options_message_is_stripped_correctly(
+        unittest_retention_pb2.DESCRIPTOR.services_by_name['Service']
+        .methods[0]
+        .GetOptions()
+        .Extensions[unittest_retention_pb2.method_option]
+    )
+
   def testNestedTypes(self):
     self.assertEqual(
         set(unittest_pb2.TestAllTypes.DESCRIPTOR.nested_types),
diff --git a/python/google/protobuf/internal/proto_builder_test.py b/python/google/protobuf/internal/proto_builder_test.py
index 48077b0..d1102b8 100644
--- a/python/google/protobuf/internal/proto_builder_test.py
+++ b/python/google/protobuf/internal/proto_builder_test.py
@@ -33,8 +33,8 @@
 import collections
 import unittest
 
-from google.protobuf import descriptor_pb2  # pylint: disable=g-import-not-at-top
 from google.protobuf import descriptor
+from google.protobuf import descriptor_pb2
 from google.protobuf import descriptor_pool
 from google.protobuf import proto_builder
 from google.protobuf import text_format
@@ -43,6 +43,8 @@
 class ProtoBuilderTest(unittest.TestCase):
 
   def setUp(self):
+    super().setUp()
+
     self.ordered_fields = collections.OrderedDict([
         ('foo', descriptor_pb2.FieldDescriptorProto.TYPE_INT64),
         ('bar', descriptor_pb2.FieldDescriptorProto.TYPE_STRING),
diff --git a/python/google/protobuf/message_factory.py b/python/google/protobuf/message_factory.py
index fac1165..74dd4a6 100644
--- a/python/google/protobuf/message_factory.py
+++ b/python/google/protobuf/message_factory.py
@@ -163,10 +163,9 @@
     Returns:
       A class describing the passed in descriptor.
     """
-    # TODO(b/258832141): add this warning
-    # warnings.warn('MessageFactory class is deprecated. Please use '
-    #               'GetMessageClass() instead of MessageFactory.GetPrototype. '
-    #               'MessageFactory class will be removed after 2024.')
+    warnings.warn('MessageFactory class is deprecated. Please use '
+                  'GetMessageClass() instead of MessageFactory.GetPrototype. '
+                  'MessageFactory class will be removed after 2024.')
     return GetMessageClass(descriptor)
 
   def CreatePrototype(self, descriptor):
@@ -181,10 +180,9 @@
     Returns:
       A class describing the passed in descriptor.
     """
-    # TODO(b/258832141): add this warning
-    # warnings.warn('Directly call CreatePrototype is wrong. Please use '
-    #               'GetMessageClass() method instead. Directly use '
-    #               'CreatePrototype will raise error after July 2023.')
+    warnings.warn('Directly call CreatePrototype is wrong. Please use '
+                  'GetMessageClass() method instead. Directly use '
+                  'CreatePrototype will raise error after July 2023.')
     return _InternalCreateMessageClass(descriptor)
 
   def GetMessages(self, files):
@@ -201,11 +199,10 @@
       any dependent messages as well as any messages defined in the same file as
       a specified message.
     """
-    # TODO(b/258832141): add this warning
-    # warnings.warn('MessageFactory class is deprecated. Please use '
-    #               'GetMessageClassesForFiles() instead of '
-    #               'MessageFactory.GetMessages(). MessageFactory class '
-    #               'will be removed after 2024.')
+    warnings.warn('MessageFactory class is deprecated. Please use '
+                  'GetMessageClassesForFiles() instead of '
+                  'MessageFactory.GetMessages(). MessageFactory class '
+                  'will be removed after 2024.')
     return GetMessageClassesForFiles(files, self.pool)
 
 
diff --git a/python/google/protobuf/pyext/map_container.cc b/python/google/protobuf/pyext/map_container.cc
index 7d690c1..d2784fd 100644
--- a/python/google/protobuf/pyext/map_container.cc
+++ b/python/google/protobuf/pyext/map_container.cc
@@ -437,7 +437,10 @@
       self->version++;
     }
 
-    if (!PythonToMapValueRef(self, v, reflection->SupportsUnknownEnumValues(),
+    if (!PythonToMapValueRef(self, v,
+                             !self->parent_field_descriptor->message_type()
+                                  ->map_value()
+                                  ->legacy_enum_field_treated_as_closed(),
                              &value)) {
       return -1;
     }
diff --git a/python/google/protobuf/pyext/message.cc b/python/google/protobuf/pyext/message.cc
index 3ff31e2..d5693bb 100644
--- a/python/google/protobuf/pyext/message.cc
+++ b/python/google/protobuf/pyext/message.cc
@@ -2300,7 +2300,7 @@
     }
     case FieldDescriptor::CPPTYPE_ENUM: {
       PROTOBUF_CHECK_GET_INT32(arg, value, -1);
-      if (reflection->SupportsUnknownEnumValues()) {
+      if (!field_descriptor->legacy_enum_field_treated_as_closed()) {
         reflection->SetEnumValue(message, field_descriptor, value);
       } else {
         const EnumDescriptor* enum_descriptor = field_descriptor->enum_type();
diff --git a/python/google/protobuf/pyext/repeated_scalar_container.cc b/python/google/protobuf/pyext/repeated_scalar_container.cc
index b19f004..ba24f0f 100644
--- a/python/google/protobuf/pyext/repeated_scalar_container.cc
+++ b/python/google/protobuf/pyext/repeated_scalar_container.cc
@@ -151,7 +151,7 @@
     }
     case FieldDescriptor::CPPTYPE_ENUM: {
       PROTOBUF_CHECK_GET_INT32(arg, value, -1);
-      if (reflection->SupportsUnknownEnumValues()) {
+      if (!field_descriptor->legacy_enum_field_treated_as_closed()) {
         reflection->SetRepeatedEnumValue(message, field_descriptor, index,
                                          value);
       } else {
@@ -376,7 +376,7 @@
     }
     case FieldDescriptor::CPPTYPE_ENUM: {
       PROTOBUF_CHECK_GET_INT32(item, value, nullptr);
-      if (reflection->SupportsUnknownEnumValues()) {
+      if (!field_descriptor->legacy_enum_field_treated_as_closed()) {
         reflection->AddEnumValue(message, field_descriptor, value);
       } else {
         const EnumDescriptor* enum_descriptor = field_descriptor->enum_type();
diff --git a/regenerate_stale_files.sh b/regenerate_stale_files.sh
index 1c38104..58c640b 100755
--- a/regenerate_stale_files.sh
+++ b/regenerate_stale_files.sh
@@ -15,6 +15,7 @@
 # Run and fix all staleness tests.
 ${BazelBin} test src:cmake_lists_staleness_test "$@" || ./bazel-bin/src/cmake_lists_staleness_test --fix
 ${BazelBin} test src/google/protobuf:well_known_types_staleness_test "$@" || ./bazel-bin/src/google/protobuf/well_known_types_staleness_test --fix
+${BazelBin} test objectivec:well_known_types_staleness_test "$@" || ./bazel-bin/objectivec/well_known_types_staleness_test --fix
 
 # Generate C# code.
 # This doesn't currently have Bazel staleness tests, but there's an existing
diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c
index fb1f5dc..75555fc 100644
--- a/ruby/ext/google/protobuf_c/defs.c
+++ b/ruby/ext/google/protobuf_c/defs.c
@@ -223,6 +223,8 @@
 
 typedef struct {
   const upb_MessageDef* msgdef;
+  // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+  // macro to update VALUE references, as to trigger write barriers.
   VALUE klass;
   VALUE descriptor_pool;
 } Descriptor;
@@ -238,7 +240,7 @@
 static const rb_data_type_t Descriptor_type = {
     "Google::Protobuf::Descriptor",
     {Descriptor_mark, RUBY_DEFAULT_FREE, NULL},
-    .flags = RUBY_TYPED_FREE_IMMEDIATELY,
+    .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 static Descriptor* ruby_to_Descriptor(VALUE val) {
@@ -280,7 +282,7 @@
              "Descriptor objects may not be created from Ruby.");
   }
 
-  self->descriptor_pool = descriptor_pool;
+  RB_OBJ_WRITE(_self, &self->descriptor_pool, descriptor_pool);
   self->msgdef = (const upb_MessageDef*)NUM2ULL(ptr);
 
   return Qnil;
@@ -390,7 +392,7 @@
 static VALUE Descriptor_msgclass(VALUE _self) {
   Descriptor* self = ruby_to_Descriptor(_self);
   if (self->klass == Qnil) {
-    self->klass = build_class_from_descriptor(_self);
+    RB_OBJ_WRITE(_self, &self->klass, build_class_from_descriptor(_self));
   }
   return self->klass;
 }
@@ -417,6 +419,8 @@
 
 typedef struct {
   const upb_FileDef* filedef;
+  // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+  // macro to update VALUE references, as to trigger write barriers.
   VALUE descriptor_pool;  // Owns the upb_FileDef.
 } FileDescriptor;
 
@@ -430,7 +434,7 @@
 static const rb_data_type_t FileDescriptor_type = {
     "Google::Protobuf::FileDescriptor",
     {FileDescriptor_mark, RUBY_DEFAULT_FREE, NULL},
-    .flags = RUBY_TYPED_FREE_IMMEDIATELY,
+    .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 static FileDescriptor* ruby_to_FileDescriptor(VALUE val) {
@@ -463,7 +467,7 @@
              "Descriptor objects may not be created from Ruby.");
   }
 
-  self->descriptor_pool = descriptor_pool;
+  RB_OBJ_WRITE(_self, &self->descriptor_pool, descriptor_pool);
   self->filedef = (const upb_FileDef*)NUM2ULL(ptr);
 
   return Qnil;
@@ -519,6 +523,8 @@
 
 typedef struct {
   const upb_FieldDef* fielddef;
+  // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+  // macro to update VALUE references, as to trigger write barriers.
   VALUE descriptor_pool;  // Owns the upb_FieldDef.
 } FieldDescriptor;
 
@@ -532,7 +538,7 @@
 static const rb_data_type_t FieldDescriptor_type = {
     "Google::Protobuf::FieldDescriptor",
     {FieldDescriptor_mark, RUBY_DEFAULT_FREE, NULL},
-    .flags = RUBY_TYPED_FREE_IMMEDIATELY,
+    .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 static FieldDescriptor* ruby_to_FieldDescriptor(VALUE val) {
@@ -570,7 +576,7 @@
              "Descriptor objects may not be created from Ruby.");
   }
 
-  self->descriptor_pool = descriptor_pool;
+  RB_OBJ_WRITE(_self, &self->descriptor_pool, descriptor_pool);
   self->fielddef = (const upb_FieldDef*)NUM2ULL(ptr);
 
   return Qnil;
@@ -884,6 +890,8 @@
 
 typedef struct {
   const upb_OneofDef* oneofdef;
+  // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+  // macro to update VALUE references, as to trigger write barriers.
   VALUE descriptor_pool;  // Owns the upb_OneofDef.
 } OneofDescriptor;
 
@@ -897,7 +905,7 @@
 static const rb_data_type_t OneofDescriptor_type = {
     "Google::Protobuf::OneofDescriptor",
     {OneofDescriptor_mark, RUBY_DEFAULT_FREE, NULL},
-    .flags = RUBY_TYPED_FREE_IMMEDIATELY,
+    .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 static OneofDescriptor* ruby_to_OneofDescriptor(VALUE val) {
@@ -936,7 +944,7 @@
              "Descriptor objects may not be created from Ruby.");
   }
 
-  self->descriptor_pool = descriptor_pool;
+  RB_OBJ_WRITE(_self, &self->descriptor_pool, descriptor_pool);
   self->oneofdef = (const upb_OneofDef*)NUM2ULL(ptr);
 
   return Qnil;
@@ -988,6 +996,8 @@
 
 typedef struct {
   const upb_EnumDef* enumdef;
+  // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+  // macro to update VALUE references, as to trigger write barriers.
   VALUE module;           // begins as nil
   VALUE descriptor_pool;  // Owns the upb_EnumDef.
 } EnumDescriptor;
@@ -1003,7 +1013,7 @@
 static const rb_data_type_t EnumDescriptor_type = {
     "Google::Protobuf::EnumDescriptor",
     {EnumDescriptor_mark, RUBY_DEFAULT_FREE, NULL},
-    .flags = RUBY_TYPED_FREE_IMMEDIATELY,
+    .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 static EnumDescriptor* ruby_to_EnumDescriptor(VALUE val) {
@@ -1042,7 +1052,7 @@
              "Descriptor objects may not be created from Ruby.");
   }
 
-  self->descriptor_pool = descriptor_pool;
+  RB_OBJ_WRITE(_self, &self->descriptor_pool, descriptor_pool);
   self->enumdef = (const upb_EnumDef*)NUM2ULL(ptr);
 
   return Qnil;
@@ -1138,7 +1148,7 @@
 static VALUE EnumDescriptor_enummodule(VALUE _self) {
   EnumDescriptor* self = ruby_to_EnumDescriptor(_self);
   if (self->module == Qnil) {
-    self->module = build_module_from_enumdesc(_self);
+    RB_OBJ_WRITE(_self, &self->module, build_module_from_enumdesc(_self));
   }
   return self->module;
 }
diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c
index 99e2946..ab19d27 100644
--- a/ruby/ext/google/protobuf_c/message.c
+++ b/ruby/ext/google/protobuf_c/message.c
@@ -53,6 +53,8 @@
 // -----------------------------------------------------------------------------
 
 typedef struct {
+  // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+  // macro to update VALUE references, as to trigger write barriers.
   VALUE arena;
   const upb_Message* msg;  // Can get as mutable when non-frozen.
   const upb_MessageDef*
@@ -65,9 +67,9 @@
 }
 
 static rb_data_type_t Message_type = {
-    "Message",
+    "Google::Protobuf::Message",
     {Message_mark, RUBY_DEFAULT_FREE, NULL},
-    .flags = RUBY_TYPED_FREE_IMMEDIATELY,
+    .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 static Message* ruby_to_Message(VALUE msg_rb) {
@@ -105,7 +107,7 @@
 void Message_InitPtr(VALUE self_, upb_Message* msg, VALUE arena) {
   Message* self = ruby_to_Message(self_);
   self->msg = msg;
-  self->arena = arena;
+  RB_OBJ_WRITE(self_, &self->arena, arena);
   ObjectCache_Add(msg, self_);
 }
 
@@ -1162,6 +1164,12 @@
                               Qfalse))) {
       options |= upb_JsonEncode_EmitDefaults;
     }
+
+    if (RTEST(rb_hash_lookup2(hash_args,
+                              ID2SYM(rb_intern("format_enums_as_integers")),
+                              Qfalse))) {
+      options |= upb_JsonEncode_FormatEnumsAsIntegers;
+    }
   }
 
   upb_Status_Clear(&status);
diff --git a/ruby/ext/google/protobuf_c/protobuf.c b/ruby/ext/google/protobuf_c/protobuf.c
index 3c765c5..bee8ac2 100644
--- a/ruby/ext/google/protobuf_c/protobuf.c
+++ b/ruby/ext/google/protobuf_c/protobuf.c
@@ -171,6 +171,8 @@
 
 typedef struct {
   upb_Arena *arena;
+  // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+  // macro to update VALUE references, as to trigger write barriers.
   VALUE pinned_objs;
 } Arena;
 
@@ -190,7 +192,7 @@
 const rb_data_type_t Arena_type = {
     "Google::Protobuf::Internal::Arena",
     {Arena_mark, Arena_free, NULL},
-    .flags = RUBY_TYPED_FREE_IMMEDIATELY,
+    .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 static void* ruby_upb_allocfunc(upb_alloc* alloc, void* ptr, size_t oldsize, size_t size) {
@@ -233,7 +235,7 @@
   Arena *arena;
   TypedData_Get_Struct(_arena, Arena, &Arena_type, arena);
   if (arena->pinned_objs == Qnil) {
-    arena->pinned_objs = rb_ary_new();
+    RB_OBJ_WRITE(_arena, &arena->pinned_objs, rb_ary_new());
   }
   rb_ary_push(arena->pinned_objs, obj);
 }
diff --git a/ruby/ext/google/protobuf_c/ruby-upb.c b/ruby/ext/google/protobuf_c/ruby-upb.c
index bda70b2..6fbf78b 100644
--- a/ruby/ext/google/protobuf_c/ruby-upb.c
+++ b/ruby/ext/google/protobuf_c/ruby-upb.c
@@ -641,12 +641,14 @@
     [kUpb_FieldType_Bytes] = _upb_mapsorter_cmpstr,
 };
 
-static bool _upb_mapsorter_resize(_upb_mapsorter* s, _upb_sortedmap* sorted,
-                                  int size) {
+bool _upb_mapsorter_pushmap(_upb_mapsorter* s, upb_FieldType key_type,
+                            const upb_Map* map, _upb_sortedmap* sorted) {
+  int map_size = _upb_Map_Size(map);
   sorted->start = s->size;
   sorted->pos = sorted->start;
-  sorted->end = sorted->start + size;
+  sorted->end = sorted->start + map_size;
 
+  // Grow s->entries if necessary.
   if (sorted->end > s->cap) {
     s->cap = upb_Log2CeilingSize(sorted->end);
     s->entries = realloc(s->entries, s->cap * sizeof(*s->entries));
@@ -654,17 +656,9 @@
   }
 
   s->size = sorted->end;
-  return true;
-}
-
-bool _upb_mapsorter_pushmap(_upb_mapsorter* s, upb_FieldType key_type,
-                            const upb_Map* map, _upb_sortedmap* sorted) {
-  int map_size = _upb_Map_Size(map);
-
-  if (!_upb_mapsorter_resize(s, sorted, map_size)) return false;
 
   // Copy non-empty entries from the table to s->entries.
-  const void** dst = &s->entries[sorted->start];
+  upb_tabent const** dst = &s->entries[sorted->start];
   const upb_tabent* src = map->table.t.entries;
   const upb_tabent* end = src + upb_table_size(&map->table.t);
   for (; src < end; src++) {
@@ -680,29 +674,6 @@
         compar[key_type]);
   return true;
 }
-
-static int _upb_mapsorter_cmpext(const void* _a, const void* _b) {
-  const upb_Message_Extension* const* a = _a;
-  const upb_Message_Extension* const* b = _b;
-  uint32_t a_num = (*a)->ext->field.number;
-  uint32_t b_num = (*b)->ext->field.number;
-  assert(a_num != b_num);
-  return a_num < b_num ? -1 : 1;
-}
-
-bool _upb_mapsorter_pushexts(_upb_mapsorter* s,
-                             const upb_Message_Extension* exts, size_t count,
-                             _upb_sortedmap* sorted) {
-  if (!_upb_mapsorter_resize(s, sorted, count)) return false;
-
-  for (size_t i = 0; i < count; i++) {
-    s->entries[sorted->start + i] = &exts[i];
-  }
-
-  qsort(&s->entries[sorted->start], count, sizeof(*s->entries),
-        _upb_mapsorter_cmpext);
-  return true;
-}
 /* This file was generated by upbc (the upb compiler) from the input
  * file:
  *
@@ -1891,7 +1862,7 @@
   n = len + 1;
   p = upb_Arena_Malloc(a, n);
   if (p) {
-    if (len != 0) memcpy(p, s, len);
+    memcpy(p, s, len);
     p[len] = 0;
   }
   return p;
@@ -7227,27 +7198,9 @@
   _upb_DefBuilder_FailJmp(ctx);
 }
 
-// Verify a relative identifier string. The loop is branchless for speed.
-static void _upb_DefBuilder_CheckIdentNotFull(upb_DefBuilder* ctx,
-                                              upb_StringView name) {
-  bool good = name.size > 0;
-
-  for (size_t i = 0; i < name.size; i++) {
-    const char c = name.data[i];
-    const char d = c | 0x20;  // force lowercase
-    const bool is_alpha = (('a' <= d) & (d <= 'z')) | (c == '_');
-    const bool is_numer = ('0' <= c) & (c <= '9') & (i != 0);
-
-    good &= is_alpha | is_numer;
-  }
-
-  if (!good) _upb_DefBuilder_CheckIdentSlow(ctx, name, false);
-}
-
 const char* _upb_DefBuilder_MakeFullName(upb_DefBuilder* ctx,
                                          const char* prefix,
                                          upb_StringView name) {
-  _upb_DefBuilder_CheckIdentNotFull(ctx, name);
   if (prefix) {
     // ret = prefix + '.' + name;
     size_t n = strlen(prefix);
@@ -7363,7 +7316,7 @@
   return true;
 }
 
-static int TryGetHexDigit(const char** src, const char* end) {
+static char TryGetHexDigit(const char** src, const char* end) {
   char ch;
   if (!TryGetChar(src, end, &ch)) return -1;
   if ('0' <= ch && ch <= '9') {
@@ -7380,10 +7333,10 @@
 static char upb_DefBuilder_ParseHexEscape(upb_DefBuilder* ctx,
                                           const upb_FieldDef* f,
                                           const char** src, const char* end) {
-  int hex_digit = TryGetHexDigit(src, end);
+  char hex_digit = TryGetHexDigit(src, end);
   if (hex_digit < 0) {
     _upb_DefBuilder_Errf(
-        ctx, "\\x must be followed by at least one hex digit (field='%s')",
+        ctx, "\\x cannot be followed by non-hex digit in field '%s' default",
         upb_FieldDef_FullName(f));
     return 0;
   }
@@ -7559,7 +7512,7 @@
 }
 
 bool _upb_DefPool_InsertExt(upb_DefPool* s, const upb_MiniTableExtension* ext,
-                            const upb_FieldDef* f) {
+                            upb_FieldDef* f) {
   return upb_inttable_insert(&s->exts, (uintptr_t)ext, upb_value_constptr(f),
                              s->arena);
 }
@@ -7791,6 +7744,12 @@
     const upb_MiniTableFile* layout, upb_Status* status) {
   const upb_StringView name = UPB_DESC(FileDescriptorProto_name)(file_proto);
 
+  if (name.size == 0) {
+    upb_Status_SetErrorFormat(status,
+                              "missing name in google_protobuf_FileDescriptorProto");
+    return NULL;
+  }
+
   // Determine whether we already know about this file.
   {
     upb_value v;
@@ -8182,6 +8141,7 @@
   e->file = _upb_DefBuilder_File(ctx);
 
   name = UPB_DESC(EnumDescriptorProto_name)(enum_proto);
+  _upb_DefBuilder_CheckIdentNotFull(ctx, name);
 
   e->full_name = _upb_DefBuilder_MakeFullName(ctx, prefix, name);
   _upb_DefBuilder_Add(ctx, e->full_name,
@@ -8991,14 +8951,7 @@
   }
 
   const upb_StringView name = UPB_DESC(FieldDescriptorProto_name)(field_proto);
-
-  f->full_name = _upb_DefBuilder_MakeFullName(ctx, prefix, name);
-  f->label_ = (int)UPB_DESC(FieldDescriptorProto_label)(field_proto);
-  f->number_ = UPB_DESC(FieldDescriptorProto_number)(field_proto);
-  f->is_proto3_optional =
-      UPB_DESC(FieldDescriptorProto_proto3_optional)(field_proto);
-  f->msgdef = m;
-  f->scope.oneof = NULL;
+  _upb_DefBuilder_CheckIdentNotFull(ctx, name);
 
   f->has_json_name = UPB_DESC(FieldDescriptorProto_has_json_name)(field_proto);
   if (f->has_json_name) {
@@ -9010,6 +8963,14 @@
   }
   if (!f->json_name) _upb_DefBuilder_OomErr(ctx);
 
+  f->full_name = _upb_DefBuilder_MakeFullName(ctx, prefix, name);
+  f->label_ = (int)UPB_DESC(FieldDescriptorProto_label)(field_proto);
+  f->number_ = UPB_DESC(FieldDescriptorProto_number)(field_proto);
+  f->is_proto3_optional =
+      UPB_DESC(FieldDescriptorProto_proto3_optional)(field_proto);
+  f->msgdef = m;
+  f->scope.oneof = NULL;
+
   const bool has_type = UPB_DESC(FieldDescriptorProto_has_type)(field_proto);
   const bool has_type_name =
       UPB_DESC(FieldDescriptorProto_has_type_name)(field_proto);
@@ -9139,24 +9100,19 @@
   }
 
   _upb_MessageDef_InsertField(ctx, m, f);
-}
 
-upb_FieldDef* _upb_Extensions_New(
-    upb_DefBuilder* ctx, int n,
-    const UPB_DESC(FieldDescriptorProto) * const* protos, const char* prefix,
-    upb_MessageDef* m) {
-  _upb_DefType_CheckPadding(sizeof(upb_FieldDef));
-  upb_FieldDef* defs =
-      (upb_FieldDef*)_upb_DefBuilder_Alloc(ctx, sizeof(upb_FieldDef) * n);
+  if (!ctx->layout) return;
 
-  for (int i = 0; i < n; i++) {
-    upb_FieldDef* f = &defs[i];
-
-    _upb_FieldDef_CreateExt(ctx, prefix, protos[i], m, f);
-    f->index_ = i;
+  const upb_MiniTable* mt = upb_MessageDef_MiniTable(m);
+  const upb_MiniTableField* fields = mt->fields;
+  for (int i = 0; i < mt->field_count; i++) {
+    if (fields[i].number == f->number_) {
+      f->layout_index = i;
+      return;
+    }
   }
 
-  return defs;
+  UPB_ASSERT(false);  // It should be impossible to reach this point.
 }
 
 upb_FieldDef* _upb_FieldDefs_New(
@@ -9167,23 +9123,28 @@
   upb_FieldDef* defs =
       (upb_FieldDef*)_upb_DefBuilder_Alloc(ctx, sizeof(upb_FieldDef) * n);
 
-  uint32_t previous = 0;
-  for (int i = 0; i < n; i++) {
-    upb_FieldDef* f = &defs[i];
+  // If we are creating extensions then is_sorted will be NULL.
+  // If we are not creating extensions then is_sorted will be non-NULL.
+  if (is_sorted) {
+    uint32_t previous = 0;
+    for (int i = 0; i < n; i++) {
+      upb_FieldDef* f = &defs[i];
 
-    _upb_FieldDef_CreateNotExt(ctx, prefix, protos[i], m, f);
-    f->index_ = i;
-    if (!ctx->layout) {
-      // Speculate that the def fields are sorted.  We will always sort the
-      // MiniTable fields, so if defs are sorted then indices will match.
-      //
-      // If this is incorrect, we will overwrite later.
-      f->layout_index = i;
+      _upb_FieldDef_CreateNotExt(ctx, prefix, protos[i], m, f);
+      f->index_ = i;
+      if (!ctx->layout) f->layout_index = i;
+
+      const uint32_t current = f->number_;
+      if (previous > current) *is_sorted = false;
+      previous = current;
     }
+  } else {
+    for (int i = 0; i < n; i++) {
+      upb_FieldDef* f = &defs[i];
 
-    const uint32_t current = f->number_;
-    if (previous > current) *is_sorted = false;
-    previous = current;
+      _upb_FieldDef_CreateExt(ctx, prefix, protos[i], m, f);
+      f->index_ = i;
+    }
   }
 
   return defs;
@@ -9239,9 +9200,6 @@
   return (v1 < v2) ? -1 : (v1 > v2);
 }
 
-// _upb_FieldDefs_Sorted() is mostly a pure function of its inputs, but has one
-// critical side effect that we depend on: it sets layout_index appropriately
-// for non-sorted lists of fields.
 const upb_FieldDef** _upb_FieldDefs_Sorted(const upb_FieldDef* f, int n,
                                            upb_Arena* a) {
   // TODO(salo): Replace this arena alloc with a persistent scratch buffer.
@@ -9299,10 +9257,7 @@
         "field number %u in extension %s has no extension range in message %s",
         (unsigned)f->number_, f->full_name, upb_MessageDef_FullName(m));
   }
-}
 
-void _upb_FieldDef_BuildMiniTableExtension(upb_DefBuilder* ctx,
-                                           const upb_FieldDef* f) {
   const upb_MiniTableExtension* ext = _upb_FieldDef_ExtensionMiniTable(f);
 
   if (ctx->layout) {
@@ -9321,8 +9276,8 @@
       sub.subenum = _upb_EnumDef_MiniTable(f->sub.enumdef);
     }
     bool ok2 = upb_MiniTableExtension_Build(desc.data, desc.size, mut_ext,
-                                            upb_MessageDef_MiniTable(f->msgdef),
-                                            sub, ctx->status);
+                                            upb_MessageDef_MiniTable(m), sub,
+                                            ctx->status);
     if (!ok2) _upb_DefBuilder_Errf(ctx, "Could not build extension mini table");
   }
 
@@ -9379,7 +9334,6 @@
   const UPB_DESC(FileOptions) * opts;
   const char* name;
   const char* package;
-  const char* edition;
 
   const upb_FileDef** deps;
   const int32_t* public_deps;
@@ -9416,10 +9370,6 @@
   return f->package ? f->package : "";
 }
 
-const char* upb_FileDef_Edition(const upb_FileDef* f) {
-  return f->edition ? f->edition : "";
-}
-
 const char* _upb_FileDef_RawPackage(const upb_FileDef* f) { return f->package; }
 
 upb_Syntax upb_FileDef_Syntax(const upb_FileDef* f) { return f->syntax; }
@@ -9567,14 +9517,13 @@
     }
   }
 
-  upb_StringView name = UPB_DESC(FileDescriptorProto_name)(file_proto);
-  file->name = strviewdup(ctx, name);
-  if (strlen(file->name) != name.size) {
-    _upb_DefBuilder_Errf(ctx, "File name contained embedded NULL");
+  if (!UPB_DESC(FileDescriptorProto_has_name)(file_proto)) {
+    _upb_DefBuilder_Errf(ctx, "File has no name");
   }
 
-  upb_StringView package = UPB_DESC(FileDescriptorProto_package)(file_proto);
+  file->name = strviewdup(ctx, UPB_DESC(FileDescriptorProto_name)(file_proto));
 
+  upb_StringView package = UPB_DESC(FileDescriptorProto_package)(file_proto);
   if (package.size) {
     _upb_DefBuilder_CheckIdentFull(ctx, package);
     file->package = strviewdup(ctx, package);
@@ -9582,18 +9531,6 @@
     file->package = NULL;
   }
 
-  upb_StringView edition = UPB_DESC(FileDescriptorProto_edition)(file_proto);
-
-  if (edition.size == 0) {
-    file->edition = NULL;
-  } else {
-    // TODO(b/267770604): How should we validate this?
-    file->edition = strviewdup(ctx, edition);
-    if (strlen(file->edition) != edition.size) {
-      _upb_DefBuilder_Errf(ctx, "Edition name contained embedded NULL");
-    }
-  }
-
   if (UPB_DESC(FileDescriptorProto_has_syntax)(file_proto)) {
     upb_StringView syntax = UPB_DESC(FileDescriptorProto_syntax)(file_proto);
 
@@ -9662,7 +9599,8 @@
   // Create extensions.
   exts = UPB_DESC(FileDescriptorProto_extension)(file_proto, &n);
   file->top_lvl_ext_count = n;
-  file->top_lvl_exts = _upb_Extensions_New(ctx, n, exts, file->package, NULL);
+  file->top_lvl_exts =
+      _upb_FieldDefs_New(ctx, n, exts, file->package, NULL, NULL);
 
   // Create messages.
   msgs = UPB_DESC(FileDescriptorProto_message_type)(file_proto, &n);
@@ -9686,19 +9624,11 @@
     _upb_FieldDef_Resolve(ctx, file->package, f);
   }
 
-  for (int i = 0; i < file->top_lvl_msg_count; i++) {
-    upb_MessageDef* m = (upb_MessageDef*)upb_FileDef_TopLevelMessage(file, i);
-    _upb_MessageDef_CreateMiniTable(ctx, (upb_MessageDef*)m);
-  }
-
-  for (int i = 0; i < file->top_lvl_ext_count; i++) {
-    upb_FieldDef* f = (upb_FieldDef*)upb_FileDef_TopLevelExtension(file, i);
-    _upb_FieldDef_BuildMiniTableExtension(ctx, f);
-  }
-
-  for (int i = 0; i < file->top_lvl_msg_count; i++) {
-    upb_MessageDef* m = (upb_MessageDef*)upb_FileDef_TopLevelMessage(file, i);
-    _upb_MessageDef_LinkMiniTable(ctx, m);
+  if (!ctx->layout) {
+    for (int i = 0; i < file->top_lvl_msg_count; i++) {
+      upb_MessageDef* m = (upb_MessageDef*)upb_FileDef_TopLevelMessage(file, i);
+      _upb_MessageDef_LinkMiniTable(ctx, m);
+    }
   }
 
   if (file->ext_count) {
@@ -10211,8 +10141,6 @@
 static upb_MiniTable* _upb_MessageDef_MakeMiniTable(upb_DefBuilder* ctx,
                                                     const upb_MessageDef* m) {
   upb_StringView desc;
-  // Note: this will assign layout_index for fields, so upb_FieldDef_MiniTable()
-  // is safe to call only after this call.
   bool ok = upb_MessageDef_MiniDescriptorEncode(m, ctx->tmp_arena, &desc);
   if (!ok) _upb_DefBuilder_OomErr(ctx);
 
@@ -10232,6 +10160,23 @@
     _upb_FieldDef_Resolve(ctx, m->full_name, f);
   }
 
+  if (!ctx->layout) {
+    m->layout = _upb_MessageDef_MakeMiniTable(ctx, m);
+    if (!m->layout) _upb_DefBuilder_OomErr(ctx);
+  }
+
+#ifndef NDEBUG
+  for (int i = 0; i < m->field_count; i++) {
+    const upb_FieldDef* f = upb_MessageDef_Field(m, i);
+    const int layout_index = _upb_FieldDef_LayoutIndex(f);
+    UPB_ASSERT(layout_index < m->layout->field_count);
+    const upb_MiniTableField* mt_f = &m->layout->fields[layout_index];
+    UPB_ASSERT(upb_FieldDef_Type(f) == upb_MiniTableField_Type(mt_f));
+    UPB_ASSERT(upb_FieldDef_HasPresence(f) ==
+               upb_MiniTableField_HasPresence(mt_f));
+  }
+#endif
+
   m->in_message_set = false;
   for (int i = 0; i < upb_MessageDef_NestedExtensionCount(m); i++) {
     upb_FieldDef* ext = (upb_FieldDef*)upb_MessageDef_NestedExtension(m, i);
@@ -10294,39 +10239,8 @@
   if (!ok) _upb_DefBuilder_OomErr(ctx);
 }
 
-void _upb_MessageDef_CreateMiniTable(upb_DefBuilder* ctx, upb_MessageDef* m) {
-  if (ctx->layout == NULL) {
-    m->layout = _upb_MessageDef_MakeMiniTable(ctx, m);
-  } else {
-    UPB_ASSERT(ctx->msg_count < ctx->layout->msg_count);
-    m->layout = ctx->layout->msgs[ctx->msg_count++];
-    UPB_ASSERT(m->field_count == m->layout->field_count);
-
-    // We don't need the result of this call, but it will assign layout_index
-    // for all the fields in O(n lg n) time.
-    _upb_FieldDefs_Sorted(m->fields, m->field_count, ctx->tmp_arena);
-  }
-
-  for (int i = 0; i < m->nested_msg_count; i++) {
-    upb_MessageDef* nested =
-        (upb_MessageDef*)upb_MessageDef_NestedMessage(m, i);
-    _upb_MessageDef_CreateMiniTable(ctx, nested);
-  }
-}
-
 void _upb_MessageDef_LinkMiniTable(upb_DefBuilder* ctx,
                                    const upb_MessageDef* m) {
-  for (int i = 0; i < upb_MessageDef_NestedExtensionCount(m); i++) {
-    const upb_FieldDef* ext = upb_MessageDef_NestedExtension(m, i);
-    _upb_FieldDef_BuildMiniTableExtension(ctx, ext);
-  }
-
-  for (int i = 0; i < m->nested_msg_count; i++) {
-    _upb_MessageDef_LinkMiniTable(ctx, upb_MessageDef_NestedMessage(m, i));
-  }
-
-  if (ctx->layout) return;
-
   for (int i = 0; i < m->field_count; i++) {
     const upb_FieldDef* f = upb_MessageDef_Field(m, i);
     const upb_MessageDef* sub_m = upb_FieldDef_MessageSubDef(f);
@@ -10354,17 +10268,9 @@
     }
   }
 
-#ifndef NDEBUG
-  for (int i = 0; i < m->field_count; i++) {
-    const upb_FieldDef* f = upb_MessageDef_Field(m, i);
-    const int layout_index = _upb_FieldDef_LayoutIndex(f);
-    UPB_ASSERT(layout_index < m->layout->field_count);
-    const upb_MiniTableField* mt_f = &m->layout->fields[layout_index];
-    UPB_ASSERT(upb_FieldDef_Type(f) == upb_MiniTableField_Type(mt_f));
-    UPB_ASSERT(upb_FieldDef_HasPresence(f) ==
-               upb_MiniTableField_HasPresence(mt_f));
+  for (int i = 0; i < m->nested_msg_count; i++) {
+    _upb_MessageDef_LinkMiniTable(ctx, upb_MessageDef_NestedMessage(m, i));
   }
-#endif
 }
 
 static uint64_t _upb_MessageDef_Modifiers(const upb_MessageDef* m) {
@@ -10497,6 +10403,7 @@
   m->is_sorted = true;
 
   name = UPB_DESC(DescriptorProto_name)(msg_proto);
+  _upb_DefBuilder_CheckIdentNotFull(ctx, name);
 
   m->full_name = _upb_DefBuilder_MakeFullName(ctx, prefix, name);
   _upb_DefBuilder_Add(ctx, m->full_name, _upb_DefType_Pack(m, UPB_DEFTYPE_MSG));
@@ -10515,6 +10422,17 @@
   ok = upb_strtable_init(&m->ntof, n_oneof + n_field, ctx->arena);
   if (!ok) _upb_DefBuilder_OomErr(ctx);
 
+  if (ctx->layout) {
+    /* create_fielddef() below depends on this being set. */
+    UPB_ASSERT(ctx->msg_count < ctx->layout->msg_count);
+    m->layout = ctx->layout->msgs[ctx->msg_count++];
+    UPB_ASSERT(n_field == m->layout->field_count);
+  } else {
+    /* Allocate now (to allow cross-linking), populate later. */
+    m->layout = _upb_DefBuilder_Alloc(
+        ctx, sizeof(*m->layout) + sizeof(_upb_FastTable_Entry));
+  }
+
   UPB_DEF_SET_OPTIONS(m->opts, DescriptorProto, MessageOptions, msg_proto);
 
   m->oneof_count = n_oneof;
@@ -10555,7 +10473,7 @@
   const UPB_DESC(FieldDescriptorProto)* const* exts =
       UPB_DESC(DescriptorProto_extension)(msg_proto, &n_ext);
   m->nested_ext_count = n_ext;
-  m->nested_exts = _upb_Extensions_New(ctx, n_ext, exts, m->full_name, m);
+  m->nested_exts = _upb_FieldDefs_New(ctx, n_ext, exts, m->full_name, m, NULL);
 
   const UPB_DESC(DescriptorProto)* const* msgs =
       UPB_DESC(DescriptorProto_nested_type)(msg_proto, &n_msg);
@@ -10962,6 +10880,7 @@
   s->file = _upb_DefBuilder_File(ctx);
 
   name = UPB_DESC(ServiceDescriptorProto_name)(svc_proto);
+  _upb_DefBuilder_CheckIdentNotFull(ctx, name);
   const char* package = _upb_FileDef_RawPackage(s->file);
   s->full_name = _upb_DefBuilder_MakeFullName(ctx, package, name);
   _upb_DefBuilder_Add(ctx, s->full_name,
@@ -13729,15 +13648,6 @@
   encode_tag(e, kUpb_MsgSet_Item, kUpb_WireType_StartGroup);
 }
 
-static void encode_ext(upb_encstate* e, const upb_Message_Extension* ext,
-                       bool is_message_set) {
-  if (UPB_UNLIKELY(is_message_set)) {
-    encode_msgset_item(e, ext);
-  } else {
-    encode_field(e, &ext->data, &ext->ext->sub, &ext->ext->field);
-  }
-}
-
 static void encode_message(upb_encstate* e, const upb_Message* msg,
                            const upb_MiniTable* m, size_t* size) {
   size_t pre_len = e->limit - e->ptr;
@@ -13767,17 +13677,12 @@
     size_t ext_count;
     const upb_Message_Extension* ext = _upb_Message_Getexts(msg, &ext_count);
     if (ext_count) {
-      if (e->options & kUpb_EncodeOption_Deterministic) {
-        _upb_sortedmap sorted;
-        _upb_mapsorter_pushexts(&e->sorter, ext, ext_count, &sorted);
-        while (_upb_sortedmap_nextext(&e->sorter, &sorted, &ext)) {
-          encode_ext(e, ext, m->ext == kUpb_ExtMode_IsMessageSet);
-        }
-        _upb_mapsorter_popmap(&e->sorter, &sorted);
-      } else {
-        const upb_Message_Extension* end = ext + ext_count;
-        for (; ext != end; ext++) {
-          encode_ext(e, ext, m->ext == kUpb_ExtMode_IsMessageSet);
+      const upb_Message_Extension* end = ext + ext_count;
+      for (; ext != end; ext++) {
+        if (UPB_UNLIKELY(m->ext == kUpb_ExtMode_IsMessageSet)) {
+          encode_msgset_item(e, ext);
+        } else {
+          encode_field(e, &ext->data, &ext->ext->sub, &ext->ext->field);
         }
       }
     }
diff --git a/ruby/ext/google/protobuf_c/ruby-upb.h b/ruby/ext/google/protobuf_c/ruby-upb.h
index 0af88d6..cd703c2 100755
--- a/ruby/ext/google/protobuf_c/ruby-upb.h
+++ b/ruby/ext/google/protobuf_c/ruby-upb.h
@@ -1424,6 +1424,179 @@
 #include <stdlib.h>
 
 
+#ifndef UPB_MINI_TABLE_MESSAGE_INTERNAL_H_
+#define UPB_MINI_TABLE_MESSAGE_INTERNAL_H_
+
+
+// Must be last.
+
+struct upb_Decoder;
+typedef const char* _upb_FieldParser(struct upb_Decoder* d, const char* ptr,
+                                     upb_Message* msg, intptr_t table,
+                                     uint64_t hasbits, uint64_t data);
+typedef struct {
+  uint64_t field_data;
+  _upb_FieldParser* field_parser;
+} _upb_FastTable_Entry;
+
+typedef enum {
+  kUpb_ExtMode_NonExtendable = 0,  // Non-extendable message.
+  kUpb_ExtMode_Extendable = 1,     // Normal extendable message.
+  kUpb_ExtMode_IsMessageSet = 2,   // MessageSet message.
+  kUpb_ExtMode_IsMessageSet_ITEM =
+      3,  // MessageSet item (temporary only, see decode.c)
+
+  // During table building we steal a bit to indicate that the message is a map
+  // entry.  *Only* used during table building!
+  kUpb_ExtMode_IsMapEntry = 4,
+} upb_ExtMode;
+
+// upb_MiniTable represents the memory layout of a given upb_MessageDef.
+// The members are public so generated code can initialize them,
+// but users MUST NOT directly read or write any of its members.
+struct upb_MiniTable {
+  const upb_MiniTableSub* subs;
+  const upb_MiniTableField* fields;
+
+  // Must be aligned to sizeof(void*). Doesn't include internal members like
+  // unknown fields, extension dict, pointer to msglayout, etc.
+  uint16_t size;
+
+  uint16_t field_count;
+  uint8_t ext;  // upb_ExtMode, declared as uint8_t so sizeof(ext) == 1
+  uint8_t dense_below;
+  uint8_t table_mask;
+  uint8_t required_count;  // Required fields have the lowest hasbits.
+
+  // To statically initialize the tables of variable length, we need a flexible
+  // array member, and we need to compile in gnu99 mode (constant initialization
+  // of flexible array members is a GNU extension, not in C99 unfortunately.
+  _upb_FastTable_Entry fasttable[];
+};
+
+// Map entries aren't actually stored for map fields, they are only used during
+// parsing. For parsing, it helps a lot if all map entry messages have the same
+// layout. The layout code in mini_table/decode.c will ensure that all map
+// entries have this layout.
+//
+// Note that users can and do create map entries directly, which will also use
+// this layout.
+//
+// NOTE: sync with mini_table/decode.c.
+typedef struct {
+  // We only need 2 hasbits max, but due to alignment we'll use 8 bytes here,
+  // and the uint64_t helps make this clear.
+  uint64_t hasbits;
+  union {
+    upb_StringView str;  // For str/bytes.
+    upb_value val;       // For all other types.
+  } k;
+  union {
+    upb_StringView str;  // For str/bytes.
+    upb_value val;       // For all other types.
+  } v;
+} upb_MapEntryData;
+
+typedef struct {
+  void* internal_data;
+  upb_MapEntryData data;
+} upb_MapEntry;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Computes a bitmask in which the |l->required_count| lowest bits are set,
+// except that we skip the lowest bit (because upb never uses hasbit 0).
+//
+// Sample output:
+//    requiredmask(1) => 0b10 (0x2)
+//    requiredmask(5) => 0b111110 (0x3e)
+UPB_INLINE uint64_t upb_MiniTable_requiredmask(const upb_MiniTable* l) {
+  int n = l->required_count;
+  assert(0 < n && n <= 63);
+  return ((1ULL << n) - 1) << 1;
+}
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+#endif /* UPB_MINI_TABLE_MESSAGE_INTERNAL_H_ */
+
+// Must be last.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// _upb_mapsorter sorts maps and provides ordered iteration over the entries.
+// Since maps can be recursive (map values can be messages which contain other
+// maps), _upb_mapsorter can contain a stack of maps.
+
+typedef struct {
+  upb_tabent const** entries;
+  int size;
+  int cap;
+} _upb_mapsorter;
+
+typedef struct {
+  int start;
+  int pos;
+  int end;
+} _upb_sortedmap;
+
+UPB_INLINE void _upb_mapsorter_init(_upb_mapsorter* s) {
+  s->entries = NULL;
+  s->size = 0;
+  s->cap = 0;
+}
+
+UPB_INLINE void _upb_mapsorter_destroy(_upb_mapsorter* s) {
+  if (s->entries) free(s->entries);
+}
+
+UPB_INLINE bool _upb_sortedmap_next(_upb_mapsorter* s, const upb_Map* map,
+                                    _upb_sortedmap* sorted, upb_MapEntry* ent) {
+  if (sorted->pos == sorted->end) return false;
+  const upb_tabent* tabent = s->entries[sorted->pos++];
+  upb_StringView key = upb_tabstrview(tabent->key);
+  _upb_map_fromkey(key, &ent->data.k, map->key_size);
+  upb_value val = {tabent->val.val};
+  _upb_map_fromvalue(val, &ent->data.v, map->val_size);
+  return true;
+}
+
+UPB_INLINE void _upb_mapsorter_popmap(_upb_mapsorter* s,
+                                      _upb_sortedmap* sorted) {
+  s->size = sorted->start;
+}
+
+bool _upb_mapsorter_pushmap(_upb_mapsorter* s, upb_FieldType key_type,
+                            const upb_Map* map, _upb_sortedmap* sorted);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+#endif /* UPB_COLLECTIONS_MAP_SORTER_INTERNAL_H_ */
+
+/*
+** Our memory representation for parsing tables and messages themselves.
+** Functions in this file are used by generated code and possibly reflection.
+**
+** The definitions in this file are internal to upb.
+**/
+
+#ifndef UPB_MESSAGE_INTERNAL_H_
+#define UPB_MESSAGE_INTERNAL_H_
+
+#include <stdlib.h>
+#include <string.h>
+
+
 #ifndef UPB_MESSAGE_EXTENSION_INTERNAL_H_
 #define UPB_MESSAGE_EXTENSION_INTERNAL_H_
 
@@ -1696,191 +1869,6 @@
 
 #endif /* UPB_MESSAGE_EXTENSION_INTERNAL_H_ */
 
-#ifndef UPB_MINI_TABLE_MESSAGE_INTERNAL_H_
-#define UPB_MINI_TABLE_MESSAGE_INTERNAL_H_
-
-
-// Must be last.
-
-struct upb_Decoder;
-typedef const char* _upb_FieldParser(struct upb_Decoder* d, const char* ptr,
-                                     upb_Message* msg, intptr_t table,
-                                     uint64_t hasbits, uint64_t data);
-typedef struct {
-  uint64_t field_data;
-  _upb_FieldParser* field_parser;
-} _upb_FastTable_Entry;
-
-typedef enum {
-  kUpb_ExtMode_NonExtendable = 0,  // Non-extendable message.
-  kUpb_ExtMode_Extendable = 1,     // Normal extendable message.
-  kUpb_ExtMode_IsMessageSet = 2,   // MessageSet message.
-  kUpb_ExtMode_IsMessageSet_ITEM =
-      3,  // MessageSet item (temporary only, see decode.c)
-
-  // During table building we steal a bit to indicate that the message is a map
-  // entry.  *Only* used during table building!
-  kUpb_ExtMode_IsMapEntry = 4,
-} upb_ExtMode;
-
-// upb_MiniTable represents the memory layout of a given upb_MessageDef.
-// The members are public so generated code can initialize them,
-// but users MUST NOT directly read or write any of its members.
-struct upb_MiniTable {
-  const upb_MiniTableSub* subs;
-  const upb_MiniTableField* fields;
-
-  // Must be aligned to sizeof(void*). Doesn't include internal members like
-  // unknown fields, extension dict, pointer to msglayout, etc.
-  uint16_t size;
-
-  uint16_t field_count;
-  uint8_t ext;  // upb_ExtMode, declared as uint8_t so sizeof(ext) == 1
-  uint8_t dense_below;
-  uint8_t table_mask;
-  uint8_t required_count;  // Required fields have the lowest hasbits.
-
-  // To statically initialize the tables of variable length, we need a flexible
-  // array member, and we need to compile in gnu99 mode (constant initialization
-  // of flexible array members is a GNU extension, not in C99 unfortunately.
-  _upb_FastTable_Entry fasttable[];
-};
-
-// Map entries aren't actually stored for map fields, they are only used during
-// parsing. For parsing, it helps a lot if all map entry messages have the same
-// layout. The layout code in mini_table/decode.c will ensure that all map
-// entries have this layout.
-//
-// Note that users can and do create map entries directly, which will also use
-// this layout.
-//
-// NOTE: sync with mini_table/decode.c.
-typedef struct {
-  // We only need 2 hasbits max, but due to alignment we'll use 8 bytes here,
-  // and the uint64_t helps make this clear.
-  uint64_t hasbits;
-  union {
-    upb_StringView str;  // For str/bytes.
-    upb_value val;       // For all other types.
-  } k;
-  union {
-    upb_StringView str;  // For str/bytes.
-    upb_value val;       // For all other types.
-  } v;
-} upb_MapEntryData;
-
-typedef struct {
-  void* internal_data;
-  upb_MapEntryData data;
-} upb_MapEntry;
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Computes a bitmask in which the |l->required_count| lowest bits are set,
-// except that we skip the lowest bit (because upb never uses hasbit 0).
-//
-// Sample output:
-//    requiredmask(1) => 0b10 (0x2)
-//    requiredmask(5) => 0b111110 (0x3e)
-UPB_INLINE uint64_t upb_MiniTable_requiredmask(const upb_MiniTable* l) {
-  int n = l->required_count;
-  assert(0 < n && n <= 63);
-  return ((1ULL << n) - 1) << 1;
-}
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-
-#endif /* UPB_MINI_TABLE_MESSAGE_INTERNAL_H_ */
-
-// Must be last.
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// _upb_mapsorter sorts maps and provides ordered iteration over the entries.
-// Since maps can be recursive (map values can be messages which contain other
-// maps), _upb_mapsorter can contain a stack of maps.
-
-typedef struct {
-  void const** entries;
-  int size;
-  int cap;
-} _upb_mapsorter;
-
-typedef struct {
-  int start;
-  int pos;
-  int end;
-} _upb_sortedmap;
-
-UPB_INLINE void _upb_mapsorter_init(_upb_mapsorter* s) {
-  s->entries = NULL;
-  s->size = 0;
-  s->cap = 0;
-}
-
-UPB_INLINE void _upb_mapsorter_destroy(_upb_mapsorter* s) {
-  if (s->entries) free(s->entries);
-}
-
-UPB_INLINE bool _upb_sortedmap_next(_upb_mapsorter* s, const upb_Map* map,
-                                    _upb_sortedmap* sorted, upb_MapEntry* ent) {
-  if (sorted->pos == sorted->end) return false;
-  const upb_tabent* tabent = (const upb_tabent*)s->entries[sorted->pos++];
-  upb_StringView key = upb_tabstrview(tabent->key);
-  _upb_map_fromkey(key, &ent->data.k, map->key_size);
-  upb_value val = {tabent->val.val};
-  _upb_map_fromvalue(val, &ent->data.v, map->val_size);
-  return true;
-}
-
-UPB_INLINE bool _upb_sortedmap_nextext(_upb_mapsorter* s,
-                                       _upb_sortedmap* sorted,
-                                       const upb_Message_Extension** ext) {
-  if (sorted->pos == sorted->end) return false;
-  *ext = (const upb_Message_Extension*)s->entries[sorted->pos++];
-  return true;
-}
-
-UPB_INLINE void _upb_mapsorter_popmap(_upb_mapsorter* s,
-                                      _upb_sortedmap* sorted) {
-  s->size = sorted->start;
-}
-
-bool _upb_mapsorter_pushmap(_upb_mapsorter* s, upb_FieldType key_type,
-                            const upb_Map* map, _upb_sortedmap* sorted);
-
-bool _upb_mapsorter_pushexts(_upb_mapsorter* s,
-                             const upb_Message_Extension* exts, size_t count,
-                             _upb_sortedmap* sorted);
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-
-#endif /* UPB_COLLECTIONS_MAP_SORTER_INTERNAL_H_ */
-
-/*
-** Our memory representation for parsing tables and messages themselves.
-** Functions in this file are used by generated code and possibly reflection.
-**
-** The definitions in this file are internal to upb.
-**/
-
-#ifndef UPB_MESSAGE_INTERNAL_H_
-#define UPB_MESSAGE_INTERNAL_H_
-
-#include <stdlib.h>
-#include <string.h>
-
-
 #ifndef UPB_MINI_TABLE_EXTENSION_REGISTRY_H_
 #define UPB_MINI_TABLE_EXTENSION_REGISTRY_H_
 
@@ -8813,7 +8801,6 @@
 const char* upb_FileDef_Name(const upb_FileDef* f);
 const UPB_DESC(FileOptions) * upb_FileDef_Options(const upb_FileDef* f);
 const char* upb_FileDef_Package(const upb_FileDef* f);
-const char* upb_FileDef_Edition(const upb_FileDef* f);
 const upb_DefPool* upb_FileDef_Pool(const upb_FileDef* f);
 
 const upb_FileDef* upb_FileDef_PublicDependency(const upb_FileDef* f, int i);
@@ -9926,7 +9913,7 @@
 upb_ExtensionRegistry* _upb_DefPool_ExtReg(const upb_DefPool* s);
 
 bool _upb_DefPool_InsertExt(upb_DefPool* s, const upb_MiniTableExtension* ext,
-                            const upb_FieldDef* f);
+                            upb_FieldDef* f);
 bool _upb_DefPool_InsertSym(upb_DefPool* s, upb_StringView sym, upb_value v,
                             upb_Status* status);
 bool _upb_DefPool_LookupSym(const upb_DefPool* s, const char* sym, size_t size,
@@ -10052,6 +10039,23 @@
 void _upb_DefBuilder_CheckIdentSlow(upb_DefBuilder* ctx, upb_StringView name,
                                     bool full);
 
+// Verify a relative identifier string. The loop is branchless for speed.
+UPB_INLINE void _upb_DefBuilder_CheckIdentNotFull(upb_DefBuilder* ctx,
+                                                  upb_StringView name) {
+  bool good = name.size > 0;
+
+  for (size_t i = 0; i < name.size; i++) {
+    const char c = name.data[i];
+    const char d = c | 0x20;  // force lowercase
+    const bool is_alpha = (('a' <= d) & (d <= 'z')) | (c == '_');
+    const bool is_numer = ('0' <= c) & (c <= '9') & (i != 0);
+
+    good &= is_alpha | is_numer;
+  }
+
+  if (!good) _upb_DefBuilder_CheckIdentSlow(ctx, name, false);
+}
+
 // Verify a full identifier string. This is slightly more complicated than
 // verifying a relative identifier string because we must track '.' chars.
 UPB_INLINE void _upb_DefBuilder_CheckIdentFull(upb_DefBuilder* ctx,
@@ -10155,14 +10159,6 @@
 uint64_t _upb_FieldDef_Modifiers(const upb_FieldDef* f);
 void _upb_FieldDef_Resolve(upb_DefBuilder* ctx, const char* prefix,
                            upb_FieldDef* f);
-void _upb_FieldDef_BuildMiniTableExtension(upb_DefBuilder* ctx,
-                                           const upb_FieldDef* f);
-
-// Allocate and initialize an array of |n| extensions (field defs).
-upb_FieldDef* _upb_Extensions_New(
-    upb_DefBuilder* ctx, int n,
-    const UPB_DESC(FieldDescriptorProto) * const* protos, const char* prefix,
-    upb_MessageDef* m);
 
 // Allocate and initialize an array of |n| field defs.
 upb_FieldDef* _upb_FieldDefs_New(
@@ -10227,7 +10223,6 @@
 void _upb_MessageDef_InsertField(upb_DefBuilder* ctx, upb_MessageDef* m,
                                  const upb_FieldDef* f);
 bool _upb_MessageDef_IsValidExtensionNumber(const upb_MessageDef* m, int n);
-void _upb_MessageDef_CreateMiniTable(upb_DefBuilder* ctx, upb_MessageDef* m);
 void _upb_MessageDef_LinkMiniTable(upb_DefBuilder* ctx,
                                    const upb_MessageDef* m);
 void _upb_MessageDef_Resolve(upb_DefBuilder* ctx, upb_MessageDef* m);
diff --git a/ruby/pom.xml b/ruby/pom.xml
index 503d3af..cd2d68e 100644
--- a/ruby/pom.xml
+++ b/ruby/pom.xml
@@ -76,7 +76,11 @@
         <dependency>
           <groupId>com.google.protobuf</groupId>
           <artifactId>protobuf-java-util</artifactId>
+<<<<<<< HEAD
+          <version>3.21.12</version>
+=======
           <version>3.22.1</version>
+>>>>>>> upstream/22.x
         </dependency>
         <dependency>
             <groupId>org.jruby</groupId>
diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java
index 92a31d6..dc76e2d 100644
--- a/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java
+++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java
@@ -669,6 +669,7 @@
    * @param options [Hash] options for the decoder
    *  preserve_proto_fieldnames: set true to use original fieldnames (default is to camelCase)
    *  emit_defaults: set true to emit 0/false values (default is to omit them)
+   *  format_enums_as_integers: set true to emit enum values as integer (default is string)
    */
   @JRubyMethod(name = "encode_json", required = 1, optional = 1, meta = true)
   public static IRubyObject encodeJson(
@@ -690,6 +691,8 @@
 
       IRubyObject emitDefaults = options.fastARef(runtime.newSymbol("emit_defaults"));
       IRubyObject preserveNames = options.fastARef(runtime.newSymbol("preserve_proto_fieldnames"));
+      IRubyObject printingEnumsAsInts =
+          options.fastARef(runtime.newSymbol("format_enums_as_integers"));
 
       if (emitDefaults != null && emitDefaults.isTrue()) {
         printer = printer.includingDefaultValueFields();
@@ -698,6 +701,10 @@
       if (preserveNames != null && preserveNames.isTrue()) {
         printer = printer.preservingProtoFieldNames();
       }
+
+      if (printingEnumsAsInts != null && printingEnumsAsInts.isTrue()) {
+        printer = printer.printingEnumsAsInts();
+      }
     }
     printer =
         printer.usingTypeRegistry(
diff --git a/ruby/tests/encode_decode_test.rb b/ruby/tests/encode_decode_test.rb
index df2cd36..710e754 100755
--- a/ruby/tests/encode_decode_test.rb
+++ b/ruby/tests/encode_decode_test.rb
@@ -84,6 +84,34 @@
     )
 
     assert_match 'optional_int32', json
+
+
+    # Test for enums printing as ints.
+    msg = A::B::C::TestMessage.new({ optional_enum: 1 })
+    json = A::B::C::TestMessage.encode_json(
+      msg, 
+      { :format_enums_as_integers => true }
+    )
+
+    assert_match '"optionalEnum":1', json
+
+    # Test for default enum being printed as int.
+    msg = A::B::C::TestMessage.new({ optional_enum: 0 })
+    json = A::B::C::TestMessage.encode_json(
+      msg, 
+      { :format_enums_as_integers => true, :emit_defaults => true }
+    )
+
+    assert_match '"optionalEnum":0', json
+
+    # Test for repeated enums printing as ints.
+    msg = A::B::C::TestMessage.new({ repeated_enum: [0,1,2,3] })
+    json = A::B::C::TestMessage.encode_json(
+      msg, 
+      { :format_enums_as_integers => true }
+    )
+
+    assert_match '"repeatedEnum":[0,1,2,3]', json
   end
 
   def test_encode_wrong_msg
diff --git a/rust/BUILD b/rust/BUILD
new file mode 100644
index 0000000..ac72f07
--- /dev/null
+++ b/rust/BUILD
@@ -0,0 +1,20 @@
+# Protobuf Rust runtime packages.
+
+load("@rules_rust//rust:defs.bzl", "rust_library")
+load("@rules_proto//proto:defs.bzl", "proto_lang_toolchain")
+
+package(default_visibility = ["//src/google/protobuf:__subpackages__"])
+
+rust_library(
+    name = "protobuf",
+    srcs = ["lib.rs"],
+)
+
+# TODO(b/270125787): Move to the right location once rust_proto_library is no longer experimental.
+proto_lang_toolchain(
+    name = "proto_lang_toolchain",
+    command_line = "--rust_out=experimental-codegen=enabled:$(OUT)",
+    progress_message = "Generating Rust proto_library %{label}",
+    runtime = ":protobuf",
+    visibility = ["//visibility:public"],
+)
diff --git a/rust/BUILD.bazel b/rust/BUILD.bazel
deleted file mode 100644
index 5efdab5..0000000
--- a/rust/BUILD.bazel
+++ /dev/null
@@ -1,10 +0,0 @@
-# Protobuf Rust runtime packages.
-
-load("@rules_rust//rust:defs.bzl", "rust_library")
-
-package(default_visibility = ["//src/google/protobuf:__subpackages__"])
-
-rust_library(
-    name = "protobuf",
-    srcs = ["lib.rs"],
-)
diff --git a/rust/defs.bzl b/rust/defs.bzl
new file mode 100644
index 0000000..8e7d47e
--- /dev/null
+++ b/rust/defs.bzl
@@ -0,0 +1,243 @@
+"""This file implements an experimental, do-not-use-kind of rust_proto_library.
+
+Disclaimer: This project is experimental, under heavy development, and should not
+be used yet."""
+
+# buildifier: disable=bzl-visibility
+load("@rules_rust//rust/private:providers.bzl", "CrateInfo", "DepInfo", "DepVariantInfo")
+
+# buildifier: disable=bzl-visibility
+load("@rules_rust//rust/private:rustc.bzl", "rustc_compile_action")
+load("@rules_rust//rust:defs.bzl", "rust_common")
+
+proto_common = proto_common_do_not_use
+
+RustProtoInfo = provider(
+    doc = "Rust protobuf provider info",
+    fields = {
+        "dep_variant_info": "DepVariantInfo for the compiled Rust gencode (also covers its " +
+                            "transitive dependencies)",
+    },
+)
+
+def _generate_rust_gencode(
+        ctx,
+        proto_info,
+        proto_lang_toolchain):
+    """Generates Rust gencode
+
+        This function uses proto_common APIs and a ProtoLangToolchain to register an action
+        that invokes protoc with the right flags.
+    Args:
+        ctx (RuleContext): current rule context
+        proto_info (ProtoInfo): ProtoInfo of the proto_library target for which we are generating
+                    gencode
+        proto_lang_toolchain (ProtoLangToolchainInfo): proto lang toolchain for Rust
+    Returns:
+        rs_outputs ([File]): generated Rust source files
+    """
+    actions = ctx.actions
+    rs_outputs = proto_common.declare_generated_files(
+        actions = actions,
+        proto_info = proto_info,
+        extension = ".pb.rs",
+    )
+
+    proto_common.compile(
+        actions = ctx.actions,
+        proto_info = proto_info,
+        generated_files = rs_outputs,
+        proto_lang_toolchain_info = proto_lang_toolchain,
+        plugin_output = ctx.bin_dir.path,
+    )
+    return rs_outputs
+
+def _get_crate_info(providers):
+    for provider in providers:
+        if hasattr(provider, "name"):
+            return provider
+    fail("Couldn't find a CrateInfo in the list of providers")
+
+def _get_dep_info(providers):
+    for provider in providers:
+        if hasattr(provider, "direct_crates"):
+            return provider
+    fail("Couldn't find a DepInfo in the list of providers")
+
+def _get_cc_info(providers):
+    for provider in providers:
+        if hasattr(provider, "linking_context"):
+            return provider
+    fail("Couldn't find a CcInfo in the list of providers")
+
+def _compile_rust(ctx, attr, src, extra_srcs, deps):
+    """Compiles a Rust source file.
+
+    Eventually this function could be upstreamed into rules_rust and be made present in rust_common.
+
+    Args:
+      ctx (RuleContext): The rule context.
+      attr (Attrs): The current rule's attributes (`ctx.attr` for rules, `ctx.rule.attr` for aspects)
+      src (File): The crate root source file to be compiled.
+      extra_srcs ([File]): Additional source files to include in the crate.
+      deps (List[DepVariantInfo]): A list of dependencies needed.
+
+    Returns:
+      A DepVariantInfo provider.
+    """
+    toolchain = ctx.toolchains["@rules_rust//rust:toolchain"]
+    output_hash = repr(hash(src.path))
+
+    # TODO(b/270124215): Use the import! macro once available
+    crate_name = ctx.label.name.replace("-", "_")
+
+    lib_name = "{prefix}{name}-{lib_hash}{extension}".format(
+        prefix = "lib",
+        name = crate_name,
+        lib_hash = output_hash,
+        extension = ".rlib",
+    )
+
+    rmeta_name = "{prefix}{name}-{lib_hash}{extension}".format(
+        prefix = "lib",
+        name = crate_name,
+        lib_hash = output_hash,
+        extension = ".rmeta",
+    )
+
+    lib = ctx.actions.declare_file(lib_name)
+    rmeta = ctx.actions.declare_file(rmeta_name)
+
+    providers = rustc_compile_action(
+        ctx = ctx,
+        attr = attr,
+        toolchain = toolchain,
+        crate_info = rust_common.create_crate_info(
+            name = crate_name,
+            type = "rlib",
+            root = src,
+            srcs = depset([src] + extra_srcs),
+            deps = depset(deps),
+            proc_macro_deps = depset([]),
+            aliases = {},
+            output = lib,
+            metadata = rmeta,
+            edition = "2021",
+            is_test = False,
+            rustc_env = {},
+            compile_data = depset([]),
+            compile_data_targets = depset([]),
+            owner = ctx.label,
+        ),
+        output_hash = output_hash,
+    )
+
+    return DepVariantInfo(
+        crate_info = _get_crate_info(providers),
+        dep_info = _get_dep_info(providers),
+        cc_info = _get_cc_info(providers),
+        build_info = None,
+    )
+
+def _rust_proto_aspect_impl(target, ctx):
+    if ProtoInfo not in target:
+        return None
+
+    proto_lang_toolchain = ctx.attr._proto_lang_toolchain[proto_common.ProtoLangToolchainInfo]
+
+    gencode = _generate_rust_gencode(ctx, target[ProtoInfo], proto_lang_toolchain)
+
+    runtime = proto_lang_toolchain.runtime
+    dep_variant_info_for_runtime = DepVariantInfo(
+        crate_info = runtime[CrateInfo] if CrateInfo in runtime else None,
+        dep_info = runtime[DepInfo] if DepInfo in runtime else None,
+        cc_info = runtime[CcInfo] if CcInfo in runtime else None,
+        build_info = None,
+    )
+
+    proto_dep = getattr(ctx.rule.attr, "deps", [])
+    dep_variant_info = _compile_rust(
+        ctx = ctx,
+        attr = ctx.rule.attr,
+        src = gencode[0],
+        extra_srcs = gencode[1:],
+        deps = [dep_variant_info_for_runtime] + ([proto_dep[0][RustProtoInfo].dep_variant_info] if proto_dep else []),
+    )
+    return [RustProtoInfo(
+        dep_variant_info = dep_variant_info,
+    )]
+
+rust_proto_library_aspect = aspect(
+    implementation = _rust_proto_aspect_impl,
+    attr_aspects = ["deps"],
+    attrs = {
+        "_cc_toolchain": attr.label(
+            doc = (
+                "In order to use find_cc_toolchain, your rule has to depend " +
+                "on C++ toolchain. See `@rules_cc//cc:find_cc_toolchain.bzl` " +
+                "docs for details."
+            ),
+            default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
+        ),
+        "_collect_cc_coverage": attr.label(
+            default = Label("@rules_rust//util:collect_coverage"),
+            executable = True,
+            cfg = "exec",
+        ),
+        "_error_format": attr.label(
+            default = Label("@rules_rust//:error_format"),
+        ),
+        "_extra_exec_rustc_flag": attr.label(
+            default = Label("@rules_rust//:extra_exec_rustc_flag"),
+        ),
+        "_extra_exec_rustc_flags": attr.label(
+            default = Label("@rules_rust//:extra_exec_rustc_flags"),
+        ),
+        "_extra_rustc_flag": attr.label(
+            default = Label("@rules_rust//:extra_rustc_flag"),
+        ),
+        "_extra_rustc_flags": attr.label(
+            default = Label("@rules_rust//:extra_rustc_flags"),
+        ),
+        "_process_wrapper": attr.label(
+            doc = "A process wrapper for running rustc on all platforms.",
+            default = Label("@rules_rust//util/process_wrapper"),
+            executable = True,
+            allow_single_file = True,
+            cfg = "exec",
+        ),
+        "_proto_lang_toolchain": attr.label(
+            default = Label("//rust:proto_lang_toolchain"),
+        ),
+    },
+    fragments = ["cpp"],
+    host_fragments = ["cpp"],
+    toolchains = [
+        str(Label("@rules_rust//rust:toolchain")),
+        "@bazel_tools//tools/cpp:toolchain_type",
+    ],
+    incompatible_use_toolchain_transition = True,
+)
+
+def _rust_proto_library_impl(ctx):
+    deps = ctx.attr.deps
+    if not deps:
+        fail("Exactly 1 dependency in `deps` attribute expected, none were provided.")
+    if len(deps) > 1:
+        fail("Exactly 1 dependency in `deps` attribute expected, too many were provided.")
+
+    dep = deps[0]
+    rust_proto_info = dep[RustProtoInfo]
+    dep_variant_info = rust_proto_info.dep_variant_info
+    return [dep_variant_info.crate_info, dep_variant_info.dep_info, dep_variant_info.cc_info]
+
+rust_proto_library = rule(
+    implementation = _rust_proto_library_impl,
+    attrs = {
+        "deps": attr.label_list(
+            mandatory = True,
+            providers = [ProtoInfo],
+            aspects = [rust_proto_library_aspect],
+        ),
+    },
+)
diff --git a/rust/lib.rs b/rust/lib.rs
index 8974efa..d860245 100644
--- a/rust/lib.rs
+++ b/rust/lib.rs
@@ -31,3 +31,6 @@
 //! Rust Protobuf Runtime
 
 // Not yet implemented.
+
+// TODO(b/270138878): Remove once we have real logic in the runtime.
+pub fn do_nothing() {}
diff --git a/rust/test/BUILD b/rust/test/BUILD
new file mode 100644
index 0000000..6e61e15
--- /dev/null
+++ b/rust/test/BUILD
@@ -0,0 +1,49 @@
+load("//rust:defs.bzl", "rust_proto_library")
+load("@rules_rust//rust:defs.bzl", "rust_test")
+
+rust_proto_library(
+    name = "hello_world_rs_proto",
+    testonly = True,
+    deps = ["//third_party/protobuf:unittest_proto"],
+)
+
+rust_test(
+    name = "hello_world_test",
+    srcs = ["hello_world_test.rs"],
+    # TODO(b/270274576): Enable testing on arm once we have a Rust Arm toolchain.
+    tags = ["not_build:arm"],
+    deps = [":hello_world_rs_proto"],
+)
+
+proto_library(
+    name = "parent_proto",
+    srcs = ["parent.proto"],
+)
+
+proto_library(
+    name = "child_proto",
+    srcs = ["child.proto"],
+    exports = [":parent_proto"],
+    deps = [":parent_proto"],
+)
+
+rust_proto_library(
+    name = "parent_rs_proto",
+    deps = [":parent_proto"],
+)
+
+rust_proto_library(
+    name = "child_rs_proto",
+    deps = [":child_proto"],
+)
+
+rust_test(
+    name = "child_parent_test",
+    srcs = ["child_parent_test.rs"],
+    # TODO(b/270274576): Enable testing on arm once we have a Rust Arm toolchain.
+    tags = ["not_build:arm"],
+    deps = [
+        ":child_rs_proto",
+        ":parent_rs_proto",
+    ],
+)
diff --git a/rust/test/child.proto b/rust/test/child.proto
new file mode 100644
index 0000000..cb43465
--- /dev/null
+++ b/rust/test/child.proto
@@ -0,0 +1,40 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+package third_party_protobuf_rust_test;
+
+// copybara:strip_begin(Replace import path for Bazel)
+import public "google/protobuf/rust/test/parent.proto";
+
+// copybara:strip_end_and_replace import public "rust/test/parent.proto";
+
+message Child {}
diff --git a/rust/test/child_parent_test.rs b/rust/test/child_parent_test.rs
new file mode 100644
index 0000000..0bfb332
--- /dev/null
+++ b/rust/test/child_parent_test.rs
@@ -0,0 +1,37 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+fn main() {
+    let _child = child_proto::Child {};
+    let _parent = parent_proto::Parent {};
+    // Parent from child_proto crate should be the same type as Parent from
+    // parent_proto crate.
+    let _parent_from_child: child_proto::Parent = parent_proto::Parent {};
+}
diff --git a/rust/test/hello_world_test.rs b/rust/test/hello_world_test.rs
new file mode 100644
index 0000000..24cad65
--- /dev/null
+++ b/rust/test/hello_world_test.rs
@@ -0,0 +1,35 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+fn main() {
+    // This is currently just a smoke test checking that we can generate gencode, compile it, and
+    // link the test binary.
+    let _test_all_types: unittest_proto::TestAllTypes;
+}
diff --git a/rust/test/parent.proto b/rust/test/parent.proto
new file mode 100644
index 0000000..fa3b204
--- /dev/null
+++ b/rust/test/parent.proto
@@ -0,0 +1,35 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+package third_party_protobuf_rust_test;
+
+message Parent {}
diff --git a/rust/test/rust_proto_library_unit_test/BUILD b/rust/test/rust_proto_library_unit_test/BUILD
new file mode 100644
index 0000000..de7da61
--- /dev/null
+++ b/rust/test/rust_proto_library_unit_test/BUILD
@@ -0,0 +1,3 @@
+load(":rust_proto_library_unit_test.bzl", "rust_proto_library_unit_test")
+
+rust_proto_library_unit_test(name = "rust_proto_library_unit_test")
diff --git a/rust/test/rust_proto_library_unit_test/child.proto b/rust/test/rust_proto_library_unit_test/child.proto
new file mode 100644
index 0000000..acb7dc4
--- /dev/null
+++ b/rust/test/rust_proto_library_unit_test/child.proto
@@ -0,0 +1,33 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+package third_party_protobuf_rust_test_rust_proto_library_unit_test;
diff --git a/rust/test/rust_proto_library_unit_test/defs.bzl b/rust/test/rust_proto_library_unit_test/defs.bzl
new file mode 100644
index 0000000..81741aa
--- /dev/null
+++ b/rust/test/rust_proto_library_unit_test/defs.bzl
@@ -0,0 +1,19 @@
+"""Support for rust_proto_library_aspect unit-tests."""
+
+load("//rust:defs.bzl", "RustProtoInfo", "rust_proto_library_aspect")
+
+ActionsInfo = provider(
+    doc = ("A provider that exposes what actions were registered by rust_proto_library_aspect " +
+           "on proto_libraries."),
+    fields = {"actions": "List[Action]: actions registered on proto_libraries."},
+)
+
+def _attach_aspect_impl(ctx):
+    return [ctx.attr.dep[RustProtoInfo], ActionsInfo(actions = ctx.attr.dep.actions)]
+
+attach_aspect = rule(
+    implementation = _attach_aspect_impl,
+    attrs = {
+        "dep": attr.label(aspects = [rust_proto_library_aspect]),
+    },
+)
diff --git a/rust/test/rust_proto_library_unit_test/parent.proto b/rust/test/rust_proto_library_unit_test/parent.proto
new file mode 100644
index 0000000..acb7dc4
--- /dev/null
+++ b/rust/test/rust_proto_library_unit_test/parent.proto
@@ -0,0 +1,33 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+package third_party_protobuf_rust_test_rust_proto_library_unit_test;
diff --git a/rust/test/rust_proto_library_unit_test/rust_proto_library_unit_test.bzl b/rust/test/rust_proto_library_unit_test/rust_proto_library_unit_test.bzl
new file mode 100644
index 0000000..6749cf9
--- /dev/null
+++ b/rust/test/rust_proto_library_unit_test/rust_proto_library_unit_test.bzl
@@ -0,0 +1,95 @@
+"""This module contains unit tests for rust_proto_library and its aspect."""
+
+load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
+load(":defs.bzl", "ActionsInfo", "attach_aspect")
+
+def _find_action_with_mnemonic(actions, mnemonic):
+    action = [a for a in actions if a.mnemonic == mnemonic]
+    if not action:
+        fail("Couldn't find action with mnemonic {} among {}".format(mnemonic, actions))
+    return action[0]
+
+def _find_rust_lib_input(inputs, target_name):
+    inputs = inputs.to_list()
+    input = [i for i in inputs if i.basename.startswith("lib" + target_name) and
+                                  (i.basename.endswith(".rlib") or i.basename.endswith(".rmeta"))]
+    if not input:
+        fail("Couldn't find lib{}-<hash>.rlib or lib{}-<hash>.rmeta among {}".format(
+            target_name,
+            target_name,
+            [i.basename for i in inputs],
+        ))
+    return input[0]
+
+####################################################################################################
+
+def _rust_compilation_action_has_runtime_as_input_test_impl(ctx):
+    env = analysistest.begin(ctx)
+    target_under_test = analysistest.target_under_test(env)
+    actions = target_under_test[ActionsInfo].actions
+    rustc_action = _find_action_with_mnemonic(actions, "Rustc")
+    _find_rust_lib_input(rustc_action.inputs, "protobuf")
+    asserts.true(env, rustc_action.outputs.to_list()[0].path.endswith(".rlib"))
+
+    return analysistest.end(env)
+
+rust_compilation_action_has_runtime_as_input_test = analysistest.make(
+    _rust_compilation_action_has_runtime_as_input_test_impl,
+)
+
+def _test_rust_compilation_action_has_runtime_as_input():
+    native.proto_library(name = "some_proto", srcs = ["some_proto.proto"])
+    attach_aspect(name = "some_proto_with_aspect", dep = ":some_proto")
+
+    rust_compilation_action_has_runtime_as_input_test(
+        name = "rust_compilation_action_has_runtime_as_input_test",
+        target_under_test = ":some_proto_with_aspect",
+        # TODO(b/270274576): Enable testing on arm once we have a Rust Arm toolchain.
+        tags = ["not_build:arm"],
+    )
+
+####################################################################################################
+
+def _rust_compilation_action_has_deps_as_inputs_test_impl(ctx):
+    env = analysistest.begin(ctx)
+    target_under_test = analysistest.target_under_test(env)
+    actions = target_under_test[ActionsInfo].actions
+    rustc_action = _find_action_with_mnemonic(actions, "Rustc")
+    _find_rust_lib_input(rustc_action.inputs, "parent")
+
+    return analysistest.end(env)
+
+rust_compilation_action_has_deps_as_input_test = analysistest.make(
+    _rust_compilation_action_has_deps_as_inputs_test_impl,
+)
+
+def _test_rust_compilation_action_has_deps_as_input():
+    native.proto_library(name = "parent_proto", srcs = ["parent.proto"])
+    native.proto_library(name = "child_proto", srcs = ["child.proto"], deps = [":parent_proto"])
+
+    attach_aspect(name = "child_proto_with_aspect", dep = ":child_proto")
+
+    rust_compilation_action_has_deps_as_input_test(
+        name = "rust_compilation_action_has_deps_as_input_test",
+        target_under_test = ":child_proto_with_aspect",
+        # TODO(b/270274576): Enable testing on arm once we have a Rust Arm toolchain.
+        tags = ["not_build:arm"],
+    )
+
+####################################################################################################
+
+def rust_proto_library_unit_test(name):
+    """Sets up rust_proto_library_unit_test test suite.
+
+    Args:
+      name: name of the test suite"""
+    _test_rust_compilation_action_has_runtime_as_input()
+    _test_rust_compilation_action_has_deps_as_input()
+
+    native.test_suite(
+        name = name,
+        tests = [
+            ":rust_compilation_action_has_runtime_as_input_test",
+            ":rust_compilation_action_has_deps_as_input_test",
+        ],
+    )
diff --git a/rust/test/rust_proto_library_unit_test/some_proto.proto b/rust/test/rust_proto_library_unit_test/some_proto.proto
new file mode 100644
index 0000000..1cc4a52
--- /dev/null
+++ b/rust/test/rust_proto_library_unit_test/some_proto.proto
@@ -0,0 +1,39 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file exists because of the design of the Starlark unit testing
+// framework. We call these tests unittests, but Blaze still sees them as full
+// builds. And while Blaze doesn't build the test-setup targets unless it really
+// needs to, it checks that all input files are present. Therefore, we need
+// these "empty" files to be present.
+
+syntax = "proto2";
+
+package third_party_protobuf_rust_test_rust_proto_library_unit_test;
diff --git a/src/file_lists.cmake b/src/file_lists.cmake
index d754247..3b49e91 100644
--- a/src/file_lists.cmake
+++ b/src/file_lists.cmake
@@ -68,6 +68,7 @@
   ${protobuf_SOURCE_DIR}/src/google/protobuf/message.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/message_lite.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/parse_context.cc
+  ${protobuf_SOURCE_DIR}/src/google/protobuf/reflection_mode.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/reflection_ops.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_field.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_ptr_field.cc
@@ -165,6 +166,7 @@
   ${protobuf_SOURCE_DIR}/src/google/protobuf/port_undef.inc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/reflection.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/reflection_internal.h
+  ${protobuf_SOURCE_DIR}/src/google/protobuf/reflection_mode.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/reflection_ops.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_field.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_ptr_field.h
@@ -270,11 +272,13 @@
 
 # @//pkg:protoc
 set(libprotoc_srcs
+  ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/allowlists/allowlists.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/code_generator.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/command_line_interface.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/enum.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/extension.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/field.cc
+  ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/field_generators/cord_field.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/field_generators/enum_field.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/field_generators/map_field.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/field_generators/message_field.cc
@@ -361,12 +365,15 @@
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/python/pyi_generator.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/retention.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/ruby/ruby_generator.cc
+  ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/rust/generator.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/subprocess.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/zip_writer.cc
 )
 
 # @//pkg:protoc
 set(libprotoc_hdrs
+  ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/allowlists/allowlist.h
+  ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/allowlists/allowlists.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/code_generator.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/command_line_interface.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/enum.h
@@ -461,6 +468,7 @@
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/python/pyi_generator.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/retention.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/ruby/ruby_generator.h
+  ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/rust/generator.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/scc.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/subprocess.h
   ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/zip_writer.h
@@ -598,6 +606,7 @@
   ${protobuf_SOURCE_DIR}/src/google/protobuf/proto3_arena_lite_unittest.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/proto3_arena_unittest.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/proto3_lite_unittest.cc
+  ${protobuf_SOURCE_DIR}/src/google/protobuf/reflection_mode_test.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/reflection_ops_unittest.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_field_reflection_unittest.cc
   ${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_field_unittest.cc
@@ -613,6 +622,7 @@
 set(protobuf_test_protos_files
   ${protobuf_SOURCE_DIR}/src/google/protobuf/any_test.proto
   ${protobuf_SOURCE_DIR}/src/google/protobuf/map_proto2_unittest.proto
+  ${protobuf_SOURCE_DIR}/src/google/protobuf/map_proto3_unittest.proto
   ${protobuf_SOURCE_DIR}/src/google/protobuf/map_unittest.proto
   ${protobuf_SOURCE_DIR}/src/google/protobuf/unittest.proto
   ${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_arena.proto
diff --git a/src/google/protobuf/BUILD.bazel b/src/google/protobuf/BUILD.bazel
index d986479..34ee53b 100644
--- a/src/google/protobuf/BUILD.bazel
+++ b/src/google/protobuf/BUILD.bazel
@@ -392,6 +392,7 @@
         "generated_message_tctable_gen.cc",
         "map_field.cc",
         "message.cc",
+        "reflection_mode.cc",
         "reflection_ops.cc",
         "service.cc",
         "text_format.cc",
@@ -415,6 +416,7 @@
         "metadata.h",
         "reflection.h",
         "reflection_internal.h",
+        "reflection_mode.h",
         "reflection_ops.h",
         "service.h",
         "text_format.h",
@@ -548,6 +550,7 @@
     srcs = [
         "any_test.proto",
         "map_proto2_unittest.proto",
+        "map_proto3_unittest.proto",
         "map_unittest.proto",
         "unittest.proto",
         "unittest_arena.proto",
@@ -604,6 +607,7 @@
     name = "generic_test_protos",
     srcs = [
         "map_proto2_unittest.proto",
+        "map_proto3_unittest.proto",
         "map_unittest.proto",
         "unittest.proto",
         "unittest_arena.proto",
@@ -1257,6 +1261,7 @@
         "//src/google/protobuf/io",
         "//src/google/protobuf/stubs",
         "//src/google/protobuf/testing",
+        "@com_google_absl//absl/functional:bind_front",
         "@com_google_absl//absl/synchronization",
         "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
@@ -1313,6 +1318,16 @@
     ],
 )
 
+cc_test(
+    name = "reflection_mode_test",
+    srcs = ["reflection_mode_test.cc"],
+    deps = [
+        ":protobuf_nowkt",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
+
 ################################################################################
 # Helper targets for Kotlin tests
 ################################################################################
diff --git a/src/google/protobuf/any.pb.h b/src/google/protobuf/any.pb.h
index 8a74236..0917057 100644
--- a/src/google/protobuf/any.pb.h
+++ b/src/google/protobuf/any.pb.h
@@ -9,7 +9,7 @@
 #include <type_traits>
 
 #include "google/protobuf/port_def.inc"
-#if PROTOBUF_VERSION < 4022000
+#if PROTOBUF_VERSION < 3021000
 #error "This file was generated by a newer version of protoc which is"
 #error "incompatible with your Protocol Buffer headers. Please update"
 #error "your headers."
@@ -96,6 +96,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
diff --git a/src/google/protobuf/api.pb.cc b/src/google/protobuf/api.pb.cc
index ed63bbb..649d48d 100644
--- a/src/google/protobuf/api.pb.cc
+++ b/src/google/protobuf/api.pb.cc
@@ -436,7 +436,7 @@
       // .google.protobuf.Syntax syntax = 7;
       case 7:
         if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 56)) {
-          ::uint32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          ::int32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
           CHK_(ptr);
           _internal_set_syntax(static_cast<::PROTOBUF_NAMESPACE_ID::Syntax>(val));
         } else {
@@ -865,7 +865,7 @@
       // .google.protobuf.Syntax syntax = 7;
       case 7:
         if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 56)) {
-          ::uint32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          ::int32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
           CHK_(ptr);
           _internal_set_syntax(static_cast<::PROTOBUF_NAMESPACE_ID::Syntax>(val));
         } else {
diff --git a/src/google/protobuf/api.pb.h b/src/google/protobuf/api.pb.h
index ac59bce..d334922 100644
--- a/src/google/protobuf/api.pb.h
+++ b/src/google/protobuf/api.pb.h
@@ -9,7 +9,7 @@
 #include <type_traits>
 
 #include "google/protobuf/port_def.inc"
-#if PROTOBUF_VERSION < 4022000
+#if PROTOBUF_VERSION < 3021000
 #error "This file was generated by a newer version of protoc which is"
 #error "incompatible with your Protocol Buffer headers. Please update"
 #error "your headers."
@@ -108,6 +108,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
@@ -379,6 +386,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
@@ -636,6 +650,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
diff --git a/src/google/protobuf/arena.cc b/src/google/protobuf/arena.cc
index 071360f..5d25d65 100644
--- a/src/google/protobuf/arena.cc
+++ b/src/google/protobuf/arena.cc
@@ -197,18 +197,21 @@
 
 PROTOBUF_NOINLINE
 void* SerialArena::AllocateFromStringBlockFallback() {
+  ABSL_DCHECK_EQ(string_block_unused_.load(std::memory_order_relaxed), 0U);
   if (string_block_) {
-    ABSL_DCHECK_EQ(string_block_unused_.load(std::memory_order_relaxed), 0U);
-    space_used_.store(space_used_.load(std::memory_order_relaxed) +
-                          string_block_->effective_size(),
-                      std::memory_order_relaxed);
+    AddSpaceUsed(string_block_->effective_size());
   }
 
-  string_block_ = StringBlock::New(string_block_);
-  space_allocated_.store(space_allocated_.load(std::memory_order_relaxed) +
-                             string_block_->allocated_size(),
-                         std::memory_order_relaxed);
-
+  void* ptr;
+  size_t size = StringBlock::NextSize(string_block_);
+  if (MaybeAllocateAligned(size, &ptr)) {
+    // Correct space_used_ to avoid double counting
+    AddSpaceUsed(-size);
+    string_block_ = StringBlock::Emplace(ptr, size, string_block_);
+  } else {
+    string_block_ = StringBlock::New(string_block_);
+    AddSpaceAllocated(string_block_->allocated_size());
+  }
   size_t unused = string_block_->effective_size() - sizeof(std::string);
   string_block_unused_.store(unused, std::memory_order_relaxed);
   return string_block_->AtOffset(unused);
@@ -240,8 +243,7 @@
     // Record how much used in this block.
     used = static_cast<size_t>(ptr() - old_head->Pointer(kBlockHeaderSize));
     wasted = old_head->size - used;
-    space_used_.store(space_used_.load(std::memory_order_relaxed) + used,
-                      std::memory_order_relaxed);
+    AddSpaceUsed(used);
   }
 
   // TODO(sbenza): Evaluate if pushing unused space into the cached blocks is a
@@ -253,9 +255,7 @@
   // We don't want to emit an expensive RMW instruction that requires
   // exclusive access to a cacheline. Hence we write it in terms of a
   // regular add.
-  space_allocated_.store(
-      space_allocated_.load(std::memory_order_relaxed) + mem.n,
-      std::memory_order_relaxed);
+  AddSpaceAllocated(mem.n);
   ThreadSafeArenaStats::RecordAllocateStats(parent_.arena_stats_.MutableStats(),
                                             /*used=*/used,
                                             /*allocated=*/mem.n, wasted);
@@ -278,19 +278,21 @@
   // usage of the *current* block.
   // TODO(mkruskal) Consider eliminating this race in exchange for a possible
   // performance hit on ARM (see cl/455186837).
-  uint64_t current_space_used =
-      string_block_ ? string_block_->effective_size() -
-                          string_block_unused_.load(std::memory_order_relaxed)
-                    : 0;
+
+  uint64_t space_used = 0;
+  if (string_block_) {
+    size_t unused = string_block_unused_.load(std::memory_order_relaxed);
+    space_used += string_block_->effective_size() - unused;
+  }
   const ArenaBlock* h = head_.load(std::memory_order_acquire);
-  if (h->IsSentry()) return current_space_used;
+  if (h->IsSentry()) return space_used;
 
   const uint64_t current_block_size = h->size;
-  current_space_used += std::min(
+  space_used += std::min(
       static_cast<uint64_t>(
           ptr() - const_cast<ArenaBlock*>(h)->Pointer(kBlockHeaderSize)),
       current_block_size);
-  return current_space_used + space_used_.load(std::memory_order_relaxed);
+  return space_used + space_used_.load(std::memory_order_relaxed);
 }
 
 size_t SerialArena::FreeStringBlocks(StringBlock* string_block,
diff --git a/src/google/protobuf/arena.h b/src/google/protobuf/arena.h
index e9e7a15..7f088a1 100644
--- a/src/google/protobuf/arena.h
+++ b/src/google/protobuf/arena.h
@@ -389,7 +389,11 @@
   // message, or nullptr otherwise. If possible, the call resolves at compile
   // time. Note that we can often devirtualize calls to `value->GetArena()` so
   // usually calling this method is unnecessary.
+  // TODO(b/271599886): remove this function.
   template <typename T>
+  ABSL_DEPRECATED(
+      "This will be removed in a future release. Call value->GetArena() "
+      "instead.")
   PROTOBUF_ALWAYS_INLINE static Arena* GetArena(T* value) {
     return GetArenaInternal(value);
   }
@@ -552,7 +556,7 @@
     static_assert(
         InternalHelper<T>::is_arena_constructable::value,
         "CreateMessage can only construct types that are ArenaConstructable");
-    if (arena == nullptr) {
+    if (PROTOBUF_PREDICT_FALSE(arena == nullptr)) {
       // Generated arena constructor T(Arena*) is protected. Call via
       // InternalHelper.
       return InternalHelper<T>::New();
diff --git a/src/google/protobuf/arena_unittest.cc b/src/google/protobuf/arena_unittest.cc
index 4832cff..42984df 100644
--- a/src/google/protobuf/arena_unittest.cc
+++ b/src/google/protobuf/arena_unittest.cc
@@ -1472,44 +1472,21 @@
   Arena arena;
   ArenaMessage* message = Arena::CreateMessage<ArenaMessage>(&arena);
   const ArenaMessage* const_pointer_to_message = message;
-  EXPECT_EQ(&arena, Arena::GetArena(message));
-  EXPECT_EQ(&arena, Arena::GetArena(const_pointer_to_message));
+  EXPECT_EQ(&arena, message->GetArena());
+  EXPECT_EQ(&arena, const_pointer_to_message->GetArena());
 
   // Test that the Message* / MessageLite* specialization SFINAE works.
   const Message* const_pointer_to_message_type = message;
-  EXPECT_EQ(&arena, Arena::GetArena(const_pointer_to_message_type));
+  EXPECT_EQ(&arena, const_pointer_to_message_type->GetArena());
   const MessageLite* const_pointer_to_message_lite_type = message;
-  EXPECT_EQ(&arena, Arena::GetArena(const_pointer_to_message_lite_type));
+  EXPECT_EQ(&arena, const_pointer_to_message_lite_type->GetArena());
 }
 
 TEST(ArenaTest, GetArenaShouldReturnNullForNonArenaAllocatedMessages) {
   ArenaMessage message;
   const ArenaMessage* const_pointer_to_message = &message;
-  EXPECT_EQ(nullptr, Arena::GetArena(&message));
-  EXPECT_EQ(nullptr, Arena::GetArena(const_pointer_to_message));
-}
-
-TEST(ArenaTest, GetArenaShouldReturnNullForNonArenaCompatibleTypes) {
-  // Test that GetArena returns nullptr for types that have a GetArena method
-  // that doesn't return Arena*.
-  struct {
-    int GetArena() const { return 0; }
-  } has_get_arena_method_wrong_return_type;
-  EXPECT_EQ(nullptr, Arena::GetArena(&has_get_arena_method_wrong_return_type));
-
-  // Test that GetArena returns nullptr for types that have a GetArena alias.
-  struct {
-    using GetArena = Arena*;
-    GetArena unused;
-  } has_get_arena_alias;
-  EXPECT_EQ(nullptr, Arena::GetArena(&has_get_arena_alias));
-
-  // Test that GetArena returns nullptr for types that have a GetArena data
-  // member.
-  struct {
-    Arena GetArena;
-  } has_get_arena_data_member;
-  EXPECT_EQ(nullptr, Arena::GetArena(&has_get_arena_data_member));
+  EXPECT_EQ(nullptr, message.GetArena());
+  EXPECT_EQ(nullptr, const_pointer_to_message->GetArena());
 }
 
 TEST(ArenaTest, AddCleanup) {
diff --git a/src/google/protobuf/compiler/BUILD.bazel b/src/google/protobuf/compiler/BUILD.bazel
index 1977211..c880205 100644
--- a/src/google/protobuf/compiler/BUILD.bazel
+++ b/src/google/protobuf/compiler/BUILD.bazel
@@ -89,7 +89,9 @@
     deps = [
         ":code_generator",
         ":importer",
+        ":retention",
         "//src/google/protobuf:protobuf_nowkt",
+        "//src/google/protobuf/compiler/allowlists",
         "@com_google_absl//absl/container:btree",
         "@com_google_absl//absl/log:absl_check",
         "@com_google_absl//absl/log:absl_log",
@@ -116,6 +118,7 @@
         "//src/google/protobuf/compiler/php",
         "//src/google/protobuf/compiler/python",
         "//src/google/protobuf/compiler/ruby",
+        "//src/google/protobuf/compiler/rust",
         "@com_google_absl//absl/log:initialize",
     ],
 )
@@ -258,6 +261,7 @@
     data = [
         ":test_plugin",
         "//:test_proto_srcs",
+        "//src/google/protobuf:descriptor_proto_srcs",
         "//src/google/protobuf:testdata",
     ],
     deps = [
@@ -377,10 +381,13 @@
 
 filegroup(
     name = "test_srcs",
-    srcs = glob([
-        "*_test.cc",
-        "*unittest.cc",
-    ], allow_empty = True) + [
+    srcs = glob(
+        [
+            "*_test.cc",
+            "*unittest.cc",
+        ],
+        allow_empty = True,
+    ) + [
         "//src/google/protobuf/compiler/cpp:test_srcs",
         "//src/google/protobuf/compiler/csharp:test_srcs",
         "//src/google/protobuf/compiler/java:test_srcs",
diff --git a/src/google/protobuf/compiler/allowlists/BUILD.bazel b/src/google/protobuf/compiler/allowlists/BUILD.bazel
new file mode 100644
index 0000000..c57091e
--- /dev/null
+++ b/src/google/protobuf/compiler/allowlists/BUILD.bazel
@@ -0,0 +1,42 @@
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
+load("//build_defs:cpp_opts.bzl", "COPTS")
+
+package(default_visibility = ["//visibility:private"])
+
+cc_library(
+    name = "allowlist",
+    hdrs = ["allowlist.h"],
+    copts = COPTS,
+    include_prefix = "google/protobuf/compiler/allowlists",
+    deps = [
+        "//src/google/protobuf/stubs",
+        "@com_google_absl//absl/algorithm:container",
+        "@com_google_absl//absl/base:core_headers",
+        "@com_google_absl//absl/strings",
+        "@com_google_absl//absl/types:span",
+    ],
+)
+
+cc_library(
+    name = "allowlists",
+    srcs = ["allowlists.cc"],
+    hdrs = ["allowlists.h"],
+    copts = COPTS,
+    include_prefix = "google/protobuf/compiler/allowlists",
+    visibility = ["//src/google/protobuf:__subpackages__"],
+    deps = [
+        ":allowlist",
+        "@com_google_absl//absl/strings",
+    ],
+)
+
+cc_test(
+    name = "allowlist_test",
+    srcs = ["allowlist_test.cc"],
+    copts = COPTS,
+    deps = [
+        ":allowlist",
+        "@com_google_googletest//:gtest",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
diff --git a/src/google/protobuf/compiler/allowlists/allowlist.h b/src/google/protobuf/compiler/allowlists/allowlist.h
new file mode 100644
index 0000000..191c138
--- /dev/null
+++ b/src/google/protobuf/compiler/allowlists/allowlist.h
@@ -0,0 +1,148 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_PROTOBUF_COMPILER_ALLOWLISTS_ALLOWLIST_H__
+#define GOOGLE_PROTOBUF_COMPILER_ALLOWLISTS_ALLOWLIST_H__
+
+#include <cstddef>
+#include <cstring>
+
+#include "absl/algorithm/container.h"
+#include "google/protobuf/stubs/common.h"
+#include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace internal {
+enum AllowlistFlags : unsigned int {
+  kNone = 0,
+  kMatchPrefix = 1 << 1,
+  kAllowAllInOss = 1 << 2,
+};
+
+// An allowlist of things (messages, files, targets) that are allowed to violate
+// some constraint.
+//
+// This is fundamentally a simple API over a set of static strings. It should
+// only ever be used as a `static const` variable.
+//
+// These allowlists are usually only used internally within Google, and contain
+// the names of internal files and Protobufs. In open source, these lists become
+// no-ops (either they always or never allow everything).
+template <size_t n>
+class Allowlist final {
+ public:
+  template <size_t m = n, typename = std::enable_if_t<m != 0>>
+  constexpr Allowlist(const absl::string_view (&list)[n], AllowlistFlags flags)
+      : flags_(flags) {
+    for (size_t i = 0; i < n; ++i) {
+      list_[i] = list[i];
+      if (i != 0) {
+        ABSL_ASSERT(list_[i - 1] < list_[i] && "Allowlist must be sorted!");
+      }
+    }
+  }
+
+  template <size_t m = n, typename = std::enable_if_t<m == 0>>
+  explicit constexpr Allowlist(AllowlistFlags flags)
+      : list_(nullptr, 0), flags_(flags) {}
+
+  // Checks if the element is allowed by this allowlist.
+  bool Allows(absl::string_view name) const {
+    if (flags_ & AllowlistFlags::kAllowAllInOss) return true;
+
+    // Convert to a span to get access to standard algorithms without resorting
+    // to horrible things like std::end().
+    absl::Span<const absl::string_view> list = list_;
+
+    auto bound = absl::c_lower_bound(list, name);
+    if (bound == list.end()) {
+      // If this string has the last element as a prefix, it will appear as if
+      // the element is not present in the list; we can take care of this case
+      // by manually checking the last element.
+      //
+      // This will also spuriously fire if a string sorts before everything in
+      // the list, but in that case the check will still return false as
+      // expected.
+      if (flags_ & AllowlistFlags::kMatchPrefix && !list.empty()) {
+        return absl::StartsWith(name, list.back());
+      }
+
+      return false;
+    }
+
+    if (name == *bound) return true;
+
+    if (flags_ & AllowlistFlags::kMatchPrefix && bound != list.begin()) {
+      return absl::StartsWith(name, bound[-1]);
+    }
+
+    return false;
+  }
+
+ private:
+  constexpr absl::Span<const absl::string_view> list() const { return list_; }
+
+  // NOTE: std::array::operator[] is *not* constexpr before C++17.
+  //
+  // In order for a zero-element list to work, we replace the array with a
+  // null string view when the size is zero.
+  std::conditional_t<n != 0, absl::string_view[n],
+                     absl::Span<absl::string_view>>
+      list_;
+  AllowlistFlags flags_;
+};
+
+struct EmptyAllowlistSentinel {};
+
+// This overload picks up MakeAllowlist({}), since zero-length arrays are not
+// a thing in C++.
+constexpr Allowlist<0> MakeAllowlist(
+    EmptyAllowlistSentinel,  // This binds to `{}`.
+    AllowlistFlags flags = AllowlistFlags::kNone) {
+  return Allowlist<0>(flags);
+}
+
+template <size_t n>
+constexpr Allowlist<n> MakeAllowlist(
+    const absl::string_view (&list)[n],
+    AllowlistFlags flags = AllowlistFlags::kNone) {
+  return Allowlist<n>(list, flags);
+}
+
+}  // namespace internal
+}  // namespace compiler
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_COMPILER_ALLOWLISTS_ALLOWLIST_H__
diff --git a/src/google/protobuf/compiler/allowlists/allowlist_test.cc b/src/google/protobuf/compiler/allowlists/allowlist_test.cc
new file mode 100644
index 0000000..03dda91
--- /dev/null
+++ b/src/google/protobuf/compiler/allowlists/allowlist_test.cc
@@ -0,0 +1,120 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "google/protobuf/compiler/allowlists/allowlist.h"
+
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace internal {
+namespace {
+
+TEST(AllowlistTest, Smoke) {
+  static const auto kList = MakeAllowlist({
+      "bar",
+      "baz",
+      "foo",
+  });
+
+  EXPECT_TRUE(kList.Allows("bar"));
+  EXPECT_TRUE(kList.Allows("baz"));
+  EXPECT_TRUE(kList.Allows("foo"));
+  EXPECT_FALSE(kList.Allows("barf"));
+  EXPECT_FALSE(kList.Allows("baq"));
+  EXPECT_FALSE(kList.Allows("bak"));
+  EXPECT_FALSE(kList.Allows("foob"));
+}
+
+TEST(AllowlistTest, Empty) {
+  static const auto kList = MakeAllowlist({});
+
+  EXPECT_FALSE(kList.Allows("bar"));
+  EXPECT_FALSE(kList.Allows("baz"));
+  EXPECT_FALSE(kList.Allows("foo"));
+  EXPECT_FALSE(kList.Allows("barf"));
+  EXPECT_FALSE(kList.Allows("baq"));
+  EXPECT_FALSE(kList.Allows("bak"));
+  EXPECT_FALSE(kList.Allows("foob"));
+}
+
+TEST(AllowlistTest, Prefix) {
+  static const auto kList = MakeAllowlist(
+      {
+          "bar",
+          "baz",
+          "foo",
+      },
+      AllowlistFlags::kMatchPrefix);
+
+  EXPECT_TRUE(kList.Allows("bar"));
+  EXPECT_TRUE(kList.Allows("baz"));
+  EXPECT_TRUE(kList.Allows("foo"));
+  EXPECT_TRUE(kList.Allows("barf"));
+  EXPECT_TRUE(kList.Allows("foon"));
+  EXPECT_TRUE(kList.Allows("bazaar"));
+  EXPECT_FALSE(kList.Allows("baq"));
+  EXPECT_FALSE(kList.Allows("bbr"));
+  EXPECT_FALSE(kList.Allows("fbar"));
+  EXPECT_FALSE(kList.Allows("ba"));
+  EXPECT_FALSE(kList.Allows("fon"));
+  EXPECT_FALSE(kList.Allows("fop"));
+}
+
+TEST(AllowlistTest, Oss) {
+  static const auto kList = MakeAllowlist(
+      {
+          "bar",
+          "baz",
+          "foo",
+      },
+      AllowlistFlags::kAllowAllInOss);
+
+  EXPECT_TRUE(kList.Allows("bar"));
+  EXPECT_TRUE(kList.Allows("baz"));
+  EXPECT_TRUE(kList.Allows("foo"));
+  EXPECT_TRUE(kList.Allows("barf"));
+  EXPECT_TRUE(kList.Allows("baq"));
+  EXPECT_TRUE(kList.Allows("bak"));
+  EXPECT_TRUE(kList.Allows("foob"));
+}
+
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+TEST(AllowlistTest, Unsorted) {
+  EXPECT_DEATH(MakeAllowlist({"foo", "bar"}), "Allowlist must be sorted!");
+}
+#endif
+
+}  // namespace
+}  // namespace internal
+}  // namespace compiler
+}  // namespace protobuf
+}  // namespace google
diff --git a/src/google/protobuf/compiler/allowlists/allowlists.cc b/src/google/protobuf/compiler/allowlists/allowlists.cc
new file mode 100644
index 0000000..0e4d60f
--- /dev/null
+++ b/src/google/protobuf/compiler/allowlists/allowlists.cc
@@ -0,0 +1,53 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "google/protobuf/compiler/allowlists/allowlists.h"
+
+#include "absl/strings/string_view.h"
+#include "google/protobuf/compiler/allowlists/allowlist.h"
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+
+// NOTE: Allowlists in this file are not accepting new entries unless otherwise
+// specified.
+
+static constexpr auto kWeakImports = internal::MakeAllowlist({
+// Intentionally left blank.
+});
+
+bool IsWeakImportFile(absl::string_view filename) {
+  return kWeakImports.Allows(filename);
+}
+
+}  // namespace compiler
+}  // namespace protobuf
+}  // namespace google
diff --git a/src/google/protobuf/compiler/allowlists/allowlists.h b/src/google/protobuf/compiler/allowlists/allowlists.h
new file mode 100644
index 0000000..b2dc174
--- /dev/null
+++ b/src/google/protobuf/compiler/allowlists/allowlists.h
@@ -0,0 +1,47 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_PROTOBUF_COMPILER_ALLOWLISTS_ALLOWLISTS_H__
+#define GOOGLE_PROTOBUF_COMPILER_ALLOWLISTS_ALLOWLISTS_H__
+
+#include "absl/strings/string_view.h"
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+
+// Returns whether a file can use the `import weak` syntax.
+bool IsWeakImportFile(absl::string_view file);
+
+}  // namespace compiler
+}  // namespace protobuf
+}  // namespace google
+
+#endif  // GOOGLE_PROTOBUF_ALLOWLISTS_COMPILER_ALLOWLISTS_H__
diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc
index 43a576d..86ae4b7 100644
--- a/src/google/protobuf/compiler/command_line_interface.cc
+++ b/src/google/protobuf/compiler/command_line_interface.cc
@@ -36,6 +36,7 @@
 
 #include "absl/container/btree_set.h"
 #include "absl/container/flat_hash_map.h"
+#include "google/protobuf/compiler/allowlists/allowlists.h"
 
 #include "google/protobuf/stubs/platform_macros.h"
 
@@ -86,6 +87,7 @@
 #include "absl/strings/substitute.h"
 #include "google/protobuf/compiler/code_generator.h"
 #include "google/protobuf/compiler/importer.h"
+#include "google/protobuf/compiler/retention.h"
 #include "google/protobuf/compiler/zip_writer.h"
 #include "google/protobuf/descriptor.h"
 #include "google/protobuf/dynamic_message.h"
@@ -2631,7 +2633,7 @@
 
   // Add this file.
   FileDescriptorProto* new_descriptor = output->Add();
-  file->CopyTo(new_descriptor);
+  *new_descriptor = StripSourceRetentionOptions(*file);
   if (include_json_name) {
     file->CopyJsonNameTo(new_descriptor);
   }
diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc
index c89f473..0b6d4aa 100644
--- a/src/google/protobuf/compiler/command_line_interface_unittest.cc
+++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc
@@ -1693,6 +1693,43 @@
   EXPECT_TRUE(descriptor_set.file(1).has_source_code_info());
 }
 
+TEST_F(CommandLineInterfaceTest, DescriptorSetOptionRetention) {
+  // clang-format off
+  CreateTempFile(
+      "foo.proto",
+      absl::Substitute(R"pb(
+          syntax = "proto2";
+          import "$0";
+          extend google.protobuf.FileOptions {
+            optional int32 runtime_retention_option = 50001
+                [retention = RETENTION_RUNTIME];
+            optional int32 source_retention_option = 50002
+                [retention = RETENTION_SOURCE];
+          }
+          option (runtime_retention_option) = 2;
+          option (source_retention_option) = 3;)pb",
+          DescriptorProto::descriptor()->file()->name()));
+  // clang-format on
+  std::string descriptor_proto_base_dir = "src";
+  Run(absl::Substitute(
+      "protocol_compiler --descriptor_set_out=$$tmpdir/descriptor_set "
+      "--proto_path=$$tmpdir --proto_path=$0 foo.proto",
+      descriptor_proto_base_dir));
+  ExpectNoErrors();
+
+  FileDescriptorSet descriptor_set;
+  ReadDescriptorSet("descriptor_set", &descriptor_set);
+  ASSERT_EQ(descriptor_set.file_size(), 1);
+  const UnknownFieldSet& unknown_fields =
+      descriptor_set.file(0).options().unknown_fields();
+
+  // We expect runtime_retention_option to be present while
+  // source_retention_option should have been stripped.
+  ASSERT_EQ(unknown_fields.field_count(), 1);
+  EXPECT_EQ(unknown_fields.field(0).number(), 50001);
+  EXPECT_EQ(unknown_fields.field(0).varint(), 2);
+}
+
 #ifdef _WIN32
 // TODO(teboring): Figure out how to write test on windows.
 #else
diff --git a/src/google/protobuf/compiler/cpp/BUILD.bazel b/src/google/protobuf/compiler/cpp/BUILD.bazel
index bd69440..e2908b3 100644
--- a/src/google/protobuf/compiler/cpp/BUILD.bazel
+++ b/src/google/protobuf/compiler/cpp/BUILD.bazel
@@ -47,6 +47,7 @@
         "enum.cc",
         "extension.cc",
         "field.cc",
+        "field_generators/cord_field.cc",
         "field_generators/enum_field.cc",
         "field_generators/map_field.cc",
         "field_generators/message_field.cc",
diff --git a/src/google/protobuf/compiler/cpp/enum.cc b/src/google/protobuf/compiler/cpp/enum.cc
index fdfcd4a..552e874 100644
--- a/src/google/protobuf/compiler/cpp/enum.cc
+++ b/src/google/protobuf/compiler/cpp/enum.cc
@@ -143,7 +143,7 @@
                            .AnnotatedAs(value),
                        {"kNumber", Int32ToString(value->number())},
                        {"DEPRECATED", value->options().deprecated()
-                                          ? "PROTOBUF_DEPRECATED_ENUM"
+                                          ? "PROTOBUF_DEPRECATED"
                                           : ""},
                    },
                    R"cc(
@@ -302,7 +302,7 @@
         {
             Sub("VALUE", EnumValueName(enum_->value(j))).AnnotatedAs(value),
             {"DEPRECATED",
-             value->options().deprecated() ? "PROTOBUF_DEPRECATED_ENUM" : ""},
+             value->options().deprecated() ? "PROTOBUF_DEPRECATED" : ""},
         },
         R"cc(
           $DEPRECATED $static constexpr $Enum_$ $VALUE$ = $Msg_Enum$_$VALUE$;
diff --git a/src/google/protobuf/compiler/cpp/field_generators/cord_field.cc b/src/google/protobuf/compiler/cpp/field_generators/cord_field.cc
new file mode 100644
index 0000000..c79c88c
--- /dev/null
+++ b/src/google/protobuf/compiler/cpp/field_generators/cord_field.cc
@@ -0,0 +1,419 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: kenton@google.com (Kenton Varda)
+//  Based on original Protocol Buffers design by
+//  Sanjay Ghemawat, Jeff Dean, and others.
+
+#include <memory>
+#include <string>
+#include <tuple>
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/log/absl_check.h"
+#include "absl/strings/escaping.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/substitute.h"
+#include "google/protobuf/compiler/cpp/field.h"
+#include "google/protobuf/compiler/cpp/field_generators/generators.h"
+#include "google/protobuf/compiler/cpp/helpers.h"
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace cpp {
+namespace {
+void SetCordVariables(
+    const FieldDescriptor* descriptor,
+    absl::flat_hash_map<absl::string_view, std::string>* variables,
+    const Options& options) {
+  (*variables)["default"] = absl::StrCat(
+      "\"", absl::CEscape(descriptor->default_value_string()), "\"");
+  (*variables)["default_length"] =
+      absl::StrCat(descriptor->default_value_string().length());
+  (*variables)["full_name"] = descriptor->full_name();
+  // For one of Cords
+  (*variables)["default_variable_name"] = MakeDefaultName(descriptor);
+  (*variables)["default_variable_field"] = MakeDefaultFieldName(descriptor);
+  (*variables)["default_variable"] =
+      descriptor->default_value_string().empty()
+          ? ProtobufNamespace(options) +
+                "::internal::GetEmptyCordAlreadyInited()"
+          : absl::StrCat(
+                QualifiedClassName(descriptor->containing_type(), options),
+                "::", MakeDefaultFieldName(descriptor));
+}
+
+class CordFieldGenerator : public FieldGeneratorBase {
+ public:
+  CordFieldGenerator(const FieldDescriptor* descriptor, const Options& options);
+  ~CordFieldGenerator() override = default;
+
+  void GeneratePrivateMembers(io::Printer* printer) const override;
+  void GenerateAccessorDeclarations(io::Printer* printer) const override;
+  void GenerateInlineAccessorDefinitions(io::Printer* printer) const override;
+  void GenerateClearingCode(io::Printer* printer) const override;
+  void GenerateMergingCode(io::Printer* printer) const override;
+  void GenerateSwappingCode(io::Printer* printer) const override;
+  void GenerateConstructorCode(io::Printer* printer) const override;
+  void GenerateDestructorCode(io::Printer* printer) const override;
+  void GenerateArenaDestructorCode(io::Printer* printer) const override;
+  void GenerateSerializeWithCachedSizesToArray(
+      io::Printer* printer) const override;
+  void GenerateByteSize(io::Printer* printer) const override;
+  void GenerateAggregateInitializer(io::Printer* printer) const override;
+  void GenerateConstexprAggregateInitializer(
+      io::Printer* printer) const override;
+  ArenaDtorNeeds NeedsArenaDestructor() const override {
+    return ArenaDtorNeeds::kRequired;
+  }
+};
+
+class CordOneofFieldGenerator : public CordFieldGenerator {
+ public:
+  CordOneofFieldGenerator(const FieldDescriptor* descriptor,
+                          const Options& options);
+  ~CordOneofFieldGenerator() override = default;
+
+  void GeneratePrivateMembers(io::Printer* printer) const override;
+  void GenerateStaticMembers(io::Printer* printer) const override;
+  void GenerateInlineAccessorDefinitions(io::Printer* printer) const override;
+  void GenerateNonInlineAccessorDefinitions(
+      io::Printer* printer) const override;
+  void GenerateClearingCode(io::Printer* printer) const override;
+  void GenerateSwappingCode(io::Printer* printer) const override;
+  void GenerateConstructorCode(io::Printer* printer) const override {}
+  void GenerateArenaDestructorCode(io::Printer* printer) const override;
+  // Overrides CordFieldGenerator behavior.
+  ArenaDtorNeeds NeedsArenaDestructor() const override {
+    return ArenaDtorNeeds::kNone;
+  }
+};
+
+
+CordFieldGenerator::CordFieldGenerator(const FieldDescriptor* descriptor,
+                                       const Options& options)
+    : FieldGeneratorBase(descriptor, options) {
+  SetCordVariables(descriptor, &variables_, options);
+}
+
+void CordFieldGenerator::GeneratePrivateMembers(io::Printer* printer) const {
+  Formatter format(printer, variables_);
+  format("::absl::Cord $name$_;\n");
+  if (!descriptor_->default_value_string().empty()) {
+    format(
+        "struct _default_$name$_func_ {\n"
+        "  constexpr absl::string_view operator()() const {\n"
+        "    return absl::string_view($default$, $default_length$);\n"
+        "  }\n"
+        "};\n");
+  }
+}
+
+void CordFieldGenerator::GenerateAccessorDeclarations(
+    io::Printer* printer) const {
+  Formatter format(printer, variables_);
+  format("$deprecated_attr$const ::absl::Cord& ${1$$name$$}$() const;\n",
+         descriptor_);
+  format(
+      "$deprecated_attr$void ${1$set_$name$$}$(const ::absl::Cord& value);\n"
+      "$deprecated_attr$void ${1$set_$name$$}$(::absl::string_view value);\n",
+      std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::SET));
+  format("private:\n");
+  format(
+      "void ${1$_internal_set_$name$$}$(const ::absl::Cord& value);\n"
+      "::absl::Cord* ${1$_internal_mutable_$name$$}$();\n"
+      "public:\n",
+      descriptor_);
+}
+
+void CordFieldGenerator::GenerateInlineAccessorDefinitions(
+    io::Printer* printer) const {
+  Formatter format(printer, variables_);
+  format(
+      "inline const ::absl::Cord& $classname$::_internal_$name$() const {\n"
+      "  return $field$;\n"
+      "}\n"
+      "inline const ::absl::Cord& $classname$::$name$() const {\n"
+      "$annotate_get$"
+      "  // @@protoc_insertion_point(field_get:$full_name$)\n"
+      "  return _internal_$name$();\n"
+      "}\n"
+      "inline void $classname$::_internal_set_$name$(const ::absl::Cord& "
+      "value) {\n"
+      "  $set_hasbit$\n"
+      "  $field$ = value;\n"
+      "}\n"
+      "inline void $classname$::set_$name$(const ::absl::Cord& value) {\n"
+      "$maybe_prepare_split_message$"
+      "  _internal_set_$name$(value);\n"
+      "$annotate_set$"
+      "  // @@protoc_insertion_point(field_set:$full_name$)\n"
+      "}\n"
+      "inline void $classname$::set_$name$(::absl::string_view value) {\n"
+      "$maybe_prepare_split_message$"
+      "  $set_hasbit$\n"
+      "  $field$ = value;\n"
+      "$annotate_set$"
+      "  // @@protoc_insertion_point(field_set_string_piece:$full_name$)\n"
+      "}\n");
+}
+
+void CordFieldGenerator::GenerateClearingCode(io::Printer* printer) const {
+  Formatter format(printer, variables_);
+  if (descriptor_->default_value_string().empty()) {
+    format("$field$.clear();\n");
+  } else {
+    format("$field$ = ::absl::string_view($default$, $default_length$);\n");
+  }
+}
+
+void CordFieldGenerator::GenerateMergingCode(io::Printer* printer) const {
+  Formatter format(printer, variables_);
+  format("_this->_internal_set_$name$(from._internal_$name$());\n");
+}
+
+void CordFieldGenerator::GenerateSwappingCode(io::Printer* printer) const {
+  Formatter format(printer, variables_);
+  format("$field$.swap(other->$field$);\n");
+}
+
+void CordFieldGenerator::GenerateConstructorCode(io::Printer* printer) const {
+  ABSL_CHECK(!ShouldSplit(descriptor_, options_));
+  Formatter format(printer, variables_);
+  if (!descriptor_->default_value_string().empty()) {
+    format("$field$ = ::absl::string_view($default$, $default_length$);\n");
+  }
+}
+
+void CordFieldGenerator::GenerateDestructorCode(io::Printer* printer) const {
+  Formatter format(printer, variables_);
+  if (ShouldSplit(descriptor_, options_)) {
+    // A cord field in the `Split` struct is automatically destroyed when the
+    // split pointer is deleted and should not be explicitly destroyed here.
+    return;
+  }
+  format("$field$.~Cord();\n");
+}
+
+void CordFieldGenerator::GenerateArenaDestructorCode(
+    io::Printer* printer) const {
+  Formatter format(printer, variables_);
+  // _this is the object being destructed (we are inside a static method here).
+  format("_this->$field$. ::absl::Cord::~Cord ();\n");
+}
+
+void CordFieldGenerator::GenerateSerializeWithCachedSizesToArray(
+    io::Printer* printer) const {
+  Formatter format(printer, variables_);
+  if (descriptor_->type() == FieldDescriptor::TYPE_STRING) {
+    GenerateUtf8CheckCodeForCord(
+        descriptor_, options_, false,
+        absl::Substitute("this->_internal_$0(), ", printer->LookupVar("name")),
+        format);
+  }
+  format(
+      "target = stream->Write$declared_type$($number$, "
+      "this->_internal_$name$(), "
+      "target);\n");
+}
+
+void CordFieldGenerator::GenerateByteSize(io::Printer* printer) const {
+  Formatter format(printer, variables_);
+  format(
+      "total_size += $tag_size$ +\n"
+      "  ::$proto_ns$::internal::WireFormatLite::$declared_type$Size(\n"
+      "    this->_internal_$name$());\n");
+}
+
+void CordFieldGenerator::GenerateConstexprAggregateInitializer(
+    io::Printer* printer) const {
+  Formatter format(printer, variables_);
+  if (descriptor_->default_value_string().empty()) {
+    format("/*decltype($field$)*/{}");
+  } else {
+    format(
+        "/*decltype($field$)*/{::absl::strings_internal::MakeStringConstant(\n"
+        "    $classname$::Impl_::$1$_default_$name$_func_{})}",
+        ShouldSplit(descriptor_, options_) ? "Split::" : "");
+  }
+}
+
+void CordFieldGenerator::GenerateAggregateInitializer(
+    io::Printer* printer) const {
+  Formatter format(printer, variables_);
+  if (ShouldSplit(descriptor_, options_)) {
+    format("decltype(Impl_::Split::$name$_){}");
+    return;
+  }
+  format("decltype($field$){}");
+}
+
+// ===================================================================
+
+CordOneofFieldGenerator::CordOneofFieldGenerator(
+    const FieldDescriptor* descriptor, const Options& options)
+    : CordFieldGenerator(descriptor, options) {
+}
+
+void CordOneofFieldGenerator::GeneratePrivateMembers(
+    io::Printer* printer) const {
+  Formatter format(printer, variables_);
+  format("::absl::Cord *$name$_;\n");
+}
+
+void CordOneofFieldGenerator::GenerateStaticMembers(
+    io::Printer* printer) const {
+  Formatter format(printer, variables_);
+  if (!descriptor_->default_value_string().empty()) {
+    format(
+        "struct _default_$name$_func_ {\n"
+        "  constexpr absl::string_view operator()() const {\n"
+        "    return absl::string_view($default$, $default_length$);\n"
+        "  }\n"
+        "};"
+        "static const ::absl::Cord $default_variable_name$;\n");
+  }
+}
+
+void CordOneofFieldGenerator::GenerateInlineAccessorDefinitions(
+    io::Printer* printer) const {
+  Formatter format(printer, variables_);
+  format(
+      "inline const ::absl::Cord& $classname$::_internal_$name$() const {\n"
+      "  if ($has_field$) {\n"
+      "    return *$field$;\n"
+      "  }\n"
+      "  return $default_variable$;\n"
+      "}\n"
+      "inline const ::absl::Cord& $classname$::$name$() const {\n"
+      "$annotate_get$"
+      "  // @@protoc_insertion_point(field_get:$full_name$)\n"
+      "  return _internal_$name$();\n"
+      "}\n"
+      "inline void $classname$::_internal_set_$name$(const ::absl::Cord& "
+      "value) {\n"
+      "  if ($not_has_field$) {\n"
+      "    clear_$oneof_name$();\n"
+      "    set_has_$name$();\n"
+      "    $field$ = new ::absl::Cord;\n"
+      "    if (GetArenaForAllocation() != nullptr) {\n"
+      "      GetArenaForAllocation()->Own($field$);\n"
+      "    }\n"
+      "  }\n"
+      "  *$field$ = value;\n"
+      "}\n"
+      "inline void $classname$::set_$name$(const ::absl::Cord& value) {\n"
+      "  _internal_set_$name$(value);\n"
+      "$annotate_set$"
+      "  // @@protoc_insertion_point(field_set:$full_name$)\n"
+      "}\n"
+      "inline void $classname$::set_$name$(::absl::string_view value) {\n"
+      "  if ($not_has_field$) {\n"
+      "    clear_$oneof_name$();\n"
+      "    set_has_$name$();\n"
+      "    $field$ = new ::absl::Cord;\n"
+      "    if (GetArenaForAllocation() != nullptr) {\n"
+      "      GetArenaForAllocation()->Own($field$);\n"
+      "    }\n"
+      "  }\n"
+      "  *$field$ = value;\n"
+      "$annotate_set$"
+      "  // @@protoc_insertion_point(field_set_string_piece:$full_name$)\n"
+      "}\n"
+      "inline ::absl::Cord* $classname$::_internal_mutable_$name$() {\n"
+      "  if ($not_has_field$) {\n"
+      "    clear_$oneof_name$();\n"
+      "    set_has_$name$();\n"
+      "    $field$ = new ::absl::Cord;\n"
+      "    if (GetArenaForAllocation() != nullptr) {\n"
+      "      GetArenaForAllocation()->Own($field$);\n"
+      "    }\n"
+      "  }\n"
+      "  return $field$;\n"
+      "}\n"
+      "inline ::absl::Cord* $classname$::mutable_$name$() {\n"
+      "  ::absl::Cord* _cord = _internal_mutable_$name$();\n"
+      "$annotate_mutable$"
+      "  // @@protoc_insertion_point(field_mutable:$full_name$)\n"
+      "  return _cord;\n"
+      "}\n");
+}
+
+void CordOneofFieldGenerator::GenerateNonInlineAccessorDefinitions(
+    io::Printer* printer) const {
+  Formatter format(printer, variables_);
+  if (!descriptor_->default_value_string().empty()) {
+    format(
+        "PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT "
+        "const ::absl::Cord $classname$::$default_variable_field$(\n"
+        "  ::absl::strings_internal::MakeStringConstant(\n"
+        "    _default_$name$_func_{}));\n");
+  }
+}
+
+void CordOneofFieldGenerator::GenerateClearingCode(io::Printer* printer) const {
+  Formatter format(printer, variables_);
+  format(
+      "if (GetArenaForAllocation() == nullptr) {\n"
+      "  delete $field$;\n"
+      "}\n");
+}
+
+void CordOneofFieldGenerator::GenerateSwappingCode(io::Printer* printer) const {
+  // Don't print any swapping code. Swapping the union will swap this field.
+}
+
+void CordOneofFieldGenerator::GenerateArenaDestructorCode(
+    io::Printer* printer) const {
+  // We inherit from CordFieldGenerator, so we need to re-override to the
+  // default behavior here.
+}
+
+// ===================================================================
+}  // namespace
+
+std::unique_ptr<FieldGeneratorBase> MakeSingularCordGenerator(
+    const FieldDescriptor* desc, const Options& options,
+    MessageSCCAnalyzer* scc) {
+  return absl::make_unique<CordFieldGenerator>(desc, options);
+}
+
+
+std::unique_ptr<FieldGeneratorBase> MakeOneofCordGenerator(
+    const FieldDescriptor* desc, const Options& options,
+    MessageSCCAnalyzer* scc) {
+  return absl::make_unique<CordOneofFieldGenerator>(desc, options);
+}
+
+}  // namespace cpp
+}  // namespace compiler
+}  // namespace protobuf
+}  // namespace google
diff --git a/src/google/protobuf/compiler/cpp/field_generators/enum_field.cc b/src/google/protobuf/compiler/cpp/field_generators/enum_field.cc
index 9744204..19bd6b5 100644
--- a/src/google/protobuf/compiler/cpp/field_generators/enum_field.cc
+++ b/src/google/protobuf/compiler/cpp/field_generators/enum_field.cc
@@ -51,6 +51,7 @@
 namespace compiler {
 namespace cpp {
 namespace {
+using Semantic = ::google::protobuf::io::AnnotationCollector::Semantic;
 using Sub = ::google::protobuf::io::Printer::Sub;
 
 std::vector<Sub> Vars(const FieldDescriptor* field, const Options& opts) {
@@ -172,7 +173,8 @@
 
 void SingularEnum::GenerateAccessorDeclarations(io::Printer* p) const {
   auto v = p->WithVars(
-      AnnotatedAccessors(field_, {"", "set_", "_internal_", "_internal_set_"}));
+      AnnotatedAccessors(field_, {"", "_internal_", "_internal_set_"}));
+  auto vs = p->WithVars(AnnotatedAccessors(field_, {"set_"}, Semantic::kSet));
   p->Emit(R"cc(
     $DEPRECATED$ $Enum$ $name$() const;
     $DEPRECATED$ void $set_name$($Enum$ value);
@@ -331,9 +333,12 @@
 };
 
 void RepeatedEnum::GenerateAccessorDeclarations(io::Printer* p) const {
-  auto v = p->WithVars(
-      AnnotatedAccessors(field_, {"", "set_", "add_", "mutable_", "_internal_",
-                                  "_internal_add_", "_internal_mutable_"}));
+  auto v = p->WithVars(AnnotatedAccessors(
+      field_, {"", "_internal_", "_internal_add_", "_internal_mutable_"}));
+  auto vs =
+      p->WithVars(AnnotatedAccessors(field_, {"set_", "add_"}, Semantic::kSet));
+  auto vm =
+      p->WithVars(AnnotatedAccessors(field_, {"mutable_"}, Semantic::kAlias));
 
   p->Emit(R"cc(
     public:
diff --git a/src/google/protobuf/compiler/cpp/field_generators/map_field.cc b/src/google/protobuf/compiler/cpp/field_generators/map_field.cc
index 79f1015..a91a394 100644
--- a/src/google/protobuf/compiler/cpp/field_generators/map_field.cc
+++ b/src/google/protobuf/compiler/cpp/field_generators/map_field.cc
@@ -30,6 +30,7 @@
 
 #include <memory>
 #include <string>
+#include <tuple>
 
 #include "absl/container/flat_hash_map.h"
 #include "absl/log/absl_check.h"
@@ -141,10 +142,12 @@
       "    ${1$_internal_mutable_$name$$}$();\n"
       "public:\n"
       "$deprecated_attr$const ::$proto_ns$::Map< $key_cpp$, $val_cpp$ >&\n"
-      "    ${1$$name$$}$() const;\n"
+      "    ${1$$name$$}$() const;\n",
+      descriptor_);
+  format(
       "$deprecated_attr$::$proto_ns$::Map< $key_cpp$, $val_cpp$ >*\n"
       "    ${1$mutable_$name$$}$();\n",
-      descriptor_);
+      std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::ALIAS));
 }
 
 void MapFieldGenerator::GenerateInlineAccessorDefinitions(
diff --git a/src/google/protobuf/compiler/cpp/field_generators/message_field.cc b/src/google/protobuf/compiler/cpp/field_generators/message_field.cc
index 6eaadef..b4bf2a9 100644
--- a/src/google/protobuf/compiler/cpp/field_generators/message_field.cc
+++ b/src/google/protobuf/compiler/cpp/field_generators/message_field.cc
@@ -809,9 +809,11 @@
       "$type$* ${1$_internal_add_$name$$}$();\n"
       "public:\n",
       descriptor_);
+  format("$deprecated_attr$const $type$& ${1$$name$$}$(int index) const;\n",
+         descriptor_);
+  format("$deprecated_attr$$type$* ${1$add_$name$$}$();\n",
+         std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::SET));
   format(
-      "$deprecated_attr$const $type$& ${1$$name$$}$(int index) const;\n"
-      "$deprecated_attr$$type$* ${1$add_$name$$}$();\n"
       "$deprecated_attr$const ::$proto_ns$::RepeatedPtrField< $type$ >&\n"
       "    ${1$$name$$}$() const;\n",
       descriptor_);
diff --git a/src/google/protobuf/compiler/cpp/field_generators/primitive_field.cc b/src/google/protobuf/compiler/cpp/field_generators/primitive_field.cc
index 5bbf298..b15801e 100644
--- a/src/google/protobuf/compiler/cpp/field_generators/primitive_field.cc
+++ b/src/google/protobuf/compiler/cpp/field_generators/primitive_field.cc
@@ -56,7 +56,7 @@
 using ::google::protobuf::internal::WireFormat;
 using ::google::protobuf::internal::WireFormatLite;
 using Sub = ::google::protobuf::io::Printer::Sub;
-using Annotation = ::google::protobuf::GeneratedCodeInfo::Annotation;
+using Semantic = ::google::protobuf::io::AnnotationCollector::Semantic;
 
 // For encodings with fixed sizes, returns that size in bytes.
 absl::optional<size_t> FixedSize(FieldDescriptor::Type type) {
@@ -193,18 +193,10 @@
 };
 
 void SingularPrimitive::GenerateAccessorDeclarations(io::Printer* p) const {
+  auto v = p->WithVars(
+      AnnotatedAccessors(field_, {"", "_internal_", "_internal_set_"}));
+  auto vs = p->WithVars(AnnotatedAccessors(field_, {"set_"}, Semantic::kSet));
   p->Emit(
-      {
-          Sub("name", p->LookupVar("name")).AnnotatedAs(field_),
-          Sub("set_name", absl::StrCat("set_", p->LookupVar("name")))
-              .AnnotatedAs(field_),
-          Sub("_internal_name",
-              absl::StrCat("_internal_", p->LookupVar("name")))
-              .AnnotatedAs(field_),
-          Sub("_internal_set_name",
-              absl::StrCat("_internal_set_", p->LookupVar("name")))
-              .AnnotatedAs(field_),
-      },
       R"cc(
         $DEPRECATED$ $Type$ $name$() const;
         $DEPRECATED$ void $set_name$($Type$ value);
@@ -397,41 +389,27 @@
 }
 
 void RepeatedPrimitive::GenerateAccessorDeclarations(io::Printer* p) const {
-  p->Emit(
-      {
-          Sub("name", p->LookupVar("name")).AnnotatedAs(field_),
-          Sub("set_name", absl::StrCat("set_", p->LookupVar("name")))
-              .AnnotatedAs(field_),
-          Sub("add_name", absl::StrCat("add_", p->LookupVar("name")))
-              .AnnotatedAs(field_),
-          Sub("mutable_name", absl::StrCat("mutable_", p->LookupVar("name")))
-              .AnnotatedAs(field_),
+  auto v = p->WithVars(AnnotatedAccessors(
+      field_, {"", "_internal_", "_internal_add_", "_internal_mutable_"}));
+  auto vs =
+      p->WithVars(AnnotatedAccessors(field_, {"set_", "add_"}, Semantic::kSet));
+  auto va =
+      p->WithVars(AnnotatedAccessors(field_, {"mutable_"}, Semantic::kAlias));
+  p->Emit(R"cc(
+    $DEPRECATED$ $Type$ $name$(int index) const;
+    $DEPRECATED$ void $set_name$(int index, $Type$ value);
+    $DEPRECATED$ void $add_name$($Type$ value);
+    $DEPRECATED$ const $pb$::RepeatedField<$Type$>& $name$() const;
+    $DEPRECATED$ $pb$::RepeatedField<$Type$>* $mutable_name$();
 
-          Sub("_internal_name",
-              absl::StrCat("_internal_", p->LookupVar("name")))
-              .AnnotatedAs(field_),
-          Sub("_internal_add_name",
-              absl::StrCat("_internal_add_", p->LookupVar("name")))
-              .AnnotatedAs(field_),
-          Sub("_internal_mutable_name",
-              absl::StrCat("_internal_mutable_", p->LookupVar("name")))
-              .AnnotatedAs(field_),
-      },
-      R"cc(
-        $DEPRECATED$ $Type$ $name$(int index) const;
-        $DEPRECATED$ void $set_name$(int index, $Type$ value);
-        $DEPRECATED$ void $add_name$($Type$ value);
-        $DEPRECATED$ const $pb$::RepeatedField<$Type$>& $name$() const;
-        $DEPRECATED$ $pb$::RepeatedField<$Type$>* $mutable_name$();
+    private:
+    $Type$ $_internal_name$(int index) const;
+    void $_internal_add_name$($Type$ value);
+    const $pb$::RepeatedField<$Type$>& $_internal_name$() const;
+    $pb$::RepeatedField<$Type$>* $_internal_mutable_name$();
 
-        private:
-        $Type$ $_internal_name$(int index) const;
-        void $_internal_add_name$($Type$ value);
-        const $pb$::RepeatedField<$Type$>& $_internal_name$() const;
-        $pb$::RepeatedField<$Type$>* $_internal_mutable_name$();
-
-        public:
-      )cc");
+    public:
+  )cc");
 }
 
 void RepeatedPrimitive::GenerateInlineAccessorDefinitions(
diff --git a/src/google/protobuf/compiler/cpp/generator.cc b/src/google/protobuf/compiler/cpp/generator.cc
index c485cdb..ab26320 100644
--- a/src/google/protobuf/compiler/cpp/generator.cc
+++ b/src/google/protobuf/compiler/cpp/generator.cc
@@ -52,6 +52,7 @@
 namespace compiler {
 namespace cpp {
 namespace {
+
 std::string NumberedCcFileName(absl::string_view basename, int number) {
   return absl::StrCat(basename, ".out/", number, ".cc");
 }
@@ -161,6 +162,14 @@
     } else if (key == "proto_static_reflection_h") {
     } else if (key == "annotate_accessor") {
       file_options.annotate_accessor = true;
+    } else if (key == "protos_for_field_listener_events") {
+      for (absl::string_view proto : absl::StrSplit(value, ':')) {
+        if (proto == file->name()) {
+          file_options.field_listener_options.inject_field_listener_events =
+              true;
+          break;
+        }
+      }
     } else if (key == "inject_field_listener_events") {
       file_options.field_listener_options.inject_field_listener_events = true;
     } else if (key == "forbidden_field_listener_events") {
diff --git a/src/google/protobuf/compiler/cpp/helpers.cc b/src/google/protobuf/compiler/cpp/helpers.cc
index e815e8b..cd4919c 100644
--- a/src/google/protobuf/compiler/cpp/helpers.cc
+++ b/src/google/protobuf/compiler/cpp/helpers.cc
@@ -211,9 +211,9 @@
 
 // Returns true if "field" is a message field that is backed by LazyField per
 // profile (go/pdlazy).
-inline bool IsEagerlyVerifiedLazyByProfile(const FieldDescriptor* field,
-                                           const Options& options,
-                                           MessageSCCAnalyzer* scc_analyzer) {
+inline bool IsLazyByProfile(const FieldDescriptor* field,
+                            const Options& options,
+                            MessageSCCAnalyzer* scc_analyzer) {
   return false;
 }
 
@@ -1092,10 +1092,6 @@
   return false;
 }
 
-bool IsUtf8String(const FieldDescriptor* field) {
-  return IsProto3(field->file()) &&
-         field->type() == FieldDescriptor::TYPE_STRING;
-}
 
 VerifySimpleType ShouldVerifySimple(const Descriptor* descriptor) {
   (void)descriptor;
diff --git a/src/google/protobuf/compiler/cpp/helpers.h b/src/google/protobuf/compiler/cpp/helpers.h
index df9fba5..fd2ea7b 100644
--- a/src/google/protobuf/compiler/cpp/helpers.h
+++ b/src/google/protobuf/compiler/cpp/helpers.h
@@ -82,7 +82,7 @@
 
 inline std::string DeprecatedAttribute(const Options& /* options */,
                                        const EnumValueDescriptor* d) {
-  return d->options().deprecated() ? "PROTOBUF_DEPRECATED_ENUM " : "";
+  return d->options().deprecated() ? "PROTOBUF_DEPRECATED " : "";
 }
 
 // Commonly-used separator comments.  Thick is a line of '=', thin is a line
@@ -323,11 +323,6 @@
                              const FieldDescriptor* field,
                              absl::string_view prefix);
 
-// Returns true if generated messages have public unknown fields accessors
-inline bool PublicUnknownFieldsAccessors(const Descriptor* message) {
-  return message->file()->syntax() != FileDescriptor::SYNTAX_PROTO3;
-}
-
 // Returns the optimize mode for <file>, respecting <options.enforce_lite>.
 FileOptions_OptimizeMode GetOptimizeFor(const FileDescriptor* file,
                                         const Options& options);
@@ -470,10 +465,6 @@
 std::string UnderscoresToCamelCase(absl::string_view input,
                                    bool cap_next_letter);
 
-inline bool IsProto3(const FileDescriptor* file) {
-  return file->syntax() == FileDescriptor::SYNTAX_PROTO3;
-}
-
 inline bool IsCrossFileMessage(const FieldDescriptor* field) {
   return field->type() == FieldDescriptor::TYPE_MESSAGE &&
          field->message_type()->file() != field->file();
@@ -1050,8 +1041,6 @@
 // Returns VerifySimpleType if messages can be verified by predefined methods.
 VerifySimpleType ShouldVerifySimple(const Descriptor* descriptor);
 
-bool IsUtf8String(const FieldDescriptor* field);
-
 bool HasMessageFieldOrExtension(const Descriptor* desc);
 
 // Generates a vector of substitutions for use with Printer::WithVars that
diff --git a/src/google/protobuf/compiler/cpp/message.cc b/src/google/protobuf/compiler/cpp/message.cc
index 88bb015..e34f19b 100644
--- a/src/google/protobuf/compiler/cpp/message.cc
+++ b/src/google/protobuf/compiler/cpp/message.cc
@@ -85,6 +85,7 @@
 using ::google::protobuf::internal::WireFormatLite;
 using ::google::protobuf::internal::cpp::HasHasbit;
 using ::google::protobuf::internal::cpp::Utf8CheckMode;
+using Semantic = ::google::protobuf::io::AnnotationCollector::Semantic;
 using Sub = ::google::protobuf::io::Printer::Sub;
 
 static constexpr int kNoHasbit = -1;
@@ -251,18 +252,6 @@
   return false;
 }
 
-// Does the given field have a has_$name$() method?
-bool HasHasMethod(const FieldDescriptor* field) {
-  if (!IsProto3(field->file())) {
-    // In proto1/proto2, every field has a has_$name$() method.
-    return true;
-  }
-  // For message types without true field presence, only fields with a message
-  // type or inside an one-of have a has_$name$() method.
-  return field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ||
-         field->has_optional_keyword() || field->real_containing_oneof();
-}
-
 bool HasInternalHasMethod(const FieldDescriptor* field) {
   return !HasHasbit(field) &&
          field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE;
@@ -730,7 +719,10 @@
          {"clearer",
           [&] {
             p->Emit({Sub("clear_name", absl::StrCat("clear_", name))
-                         .AnnotatedAs(field)},
+                         .AnnotatedAs({
+                             field,
+                             Semantic::kSet,
+                         })},
                     R"cc(
                       $deprecated_attr $void $clear_name$() $impl$;
                     )cc");
@@ -1036,7 +1028,7 @@
   // N.B.: Without field presence, we do not use has-bits or generate
   // has_$name$() methods, but oneofs still have set_has_$name$().
   // Oneofs also have private _internal_has_$name$() a helper method.
-  if (HasHasMethod(field)) {
+  if (field->has_presence()) {
     format(
         "inline bool $classname$::has_$name$() const {\n"
         "$annotate_has$"
@@ -1284,16 +1276,14 @@
       "}\n"
       "\n");
 
-  if (PublicUnknownFieldsAccessors(descriptor_)) {
-    format(
-        "inline const $unknown_fields_type$& unknown_fields() const {\n"
-        "  return $unknown_fields$;\n"
-        "}\n"
-        "inline $unknown_fields_type$* mutable_unknown_fields() {\n"
-        "  return $mutable_unknown_fields$;\n"
-        "}\n"
-        "\n");
-  }
+  format(
+      "inline const $unknown_fields_type$& unknown_fields() const {\n"
+      "  return $unknown_fields$;\n"
+      "}\n"
+      "inline $unknown_fields_type$* mutable_unknown_fields() {\n"
+      "  return $mutable_unknown_fields$;\n"
+      "}\n"
+      "\n");
 
   // Only generate this member if it's not disabled.
   if (HasDescriptorMethods(descriptor_->file(), options_) &&
@@ -3649,7 +3639,6 @@
     LazySerializerEmitter(MessageGenerator* mg, io::Printer* p)
         : mg_(mg),
           p_(p),
-          eager_(IsProto3(mg->descriptor_->file())),
           cached_has_bit_index_(kNoHasbit) {}
 
     ~LazySerializerEmitter() { Flush(); }
@@ -3658,13 +3647,14 @@
     // oneof, and handle them at the next Flush().
     void Emit(const FieldDescriptor* field) {
       Formatter format(p_);
-      if (eager_ || MustFlush(field)) {
+
+      if (!field->has_presence() || MustFlush(field)) {
         Flush();
       }
       if (!field->real_containing_oneof()) {
         // TODO(ckennelly): Defer non-oneof fields similarly to oneof fields.
 
-        if (!field->options().weak() && !field->is_repeated() && !eager_) {
+        if (HasHasbit(field) && field->has_presence()) {
           // We speculatively load the entire _has_bits_[index] contents, even
           // if it is for only one field.  Deferring non-oneof emitting would
           // allow us to determine whether this is going to be useful.
@@ -3708,7 +3698,6 @@
 
     MessageGenerator* mg_;
     io::Printer* p_;
-    bool eager_;
     std::vector<const FieldDescriptor*> v_;
 
     // cached_has_bit_index_ maintains that:
diff --git a/src/google/protobuf/compiler/cpp/metadata_test.cc b/src/google/protobuf/compiler/cpp/metadata_test.cc
index feb5a7f..e95ee56 100644
--- a/src/google/protobuf/compiler/cpp/metadata_test.cc
+++ b/src/google/protobuf/compiler/cpp/metadata_test.cc
@@ -177,6 +177,270 @@
   EXPECT_FALSE(atu::GetAnnotationSubstring(test, annotation).has_value());
 }
 
+constexpr absl::string_view kEnumFieldTestFile = R"(
+  syntax = "proto2";
+  package foo;
+  enum Enum { VALUE = 0; }
+  message Message {
+    optional Enum efield = 1;
+    repeated Enum refield = 2;
+  }
+)";
+
+TEST_F(CppMetadataTest, AnnotatesEnumSemantics) {
+  FileDescriptorProto file;
+  GeneratedCodeInfo info;
+  std::string pb_h;
+  atu::AddFile("test.proto", kEnumFieldTestFile);
+  EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
+                              nullptr, nullptr));
+  EXPECT_EQ("Message", file.message_type(0).name());
+  std::vector<int> field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0,
+                              DescriptorProto::kFieldFieldNumber, 0};
+  std::vector<const GeneratedCodeInfo::Annotation*> annotations;
+  atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
+  EXPECT_TRUE(!annotations.empty());
+  for (const auto* annotation : annotations) {
+    auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
+    ASSERT_TRUE(substring.has_value());
+    if (*substring == "efield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
+                annotation->semantic());
+    } else if (*substring == "set_efield" || *substring == "clear_efield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
+                annotation->semantic());
+    }
+  }
+  field_path.back() = 1;
+  annotations.clear();
+  atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
+  EXPECT_TRUE(!annotations.empty());
+  for (const auto* annotation : annotations) {
+    auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
+    ASSERT_TRUE(substring.has_value());
+    if (*substring == "refield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
+                annotation->semantic());
+    } else if (*substring == "set_refield" || *substring == "clear_refield" ||
+               *substring == "add_refield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
+                annotation->semantic());
+    } else if (*substring == "mutable_refield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
+                annotation->semantic());
+    }
+  }
+}
+
+constexpr absl::string_view kMapFieldTestFile = R"(
+  syntax = "proto2";
+  package foo;
+  message Message {
+    map<string, int32> mfield = 1;
+  }
+)";
+
+TEST_F(CppMetadataTest, AnnotatesMapSemantics) {
+  FileDescriptorProto file;
+  GeneratedCodeInfo info;
+  std::string pb_h;
+  atu::AddFile("test.proto", kMapFieldTestFile);
+  EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
+                              nullptr, nullptr));
+  EXPECT_EQ("Message", file.message_type(0).name());
+  std::vector<int> field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0,
+                              DescriptorProto::kFieldFieldNumber, 0};
+  std::vector<const GeneratedCodeInfo::Annotation*> annotations;
+  atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
+  EXPECT_TRUE(!annotations.empty());
+  for (const auto* annotation : annotations) {
+    auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
+    ASSERT_TRUE(substring.has_value());
+    if (*substring == "mfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
+                annotation->semantic());
+    } else if (*substring == "clear_mfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
+                annotation->semantic());
+    } else if (*substring == "mutable_mfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
+                annotation->semantic());
+    }
+  }
+}
+
+constexpr absl::string_view kPrimFieldTestFile = R"(
+  syntax = "proto2";
+  package foo;
+  message Message {
+    optional int32 ifield = 1;
+    repeated int32 rifield = 2;
+  }
+)";
+
+TEST_F(CppMetadataTest, AnnotatesPrimSemantics) {
+  FileDescriptorProto file;
+  GeneratedCodeInfo info;
+  std::string pb_h;
+  atu::AddFile("test.proto", kPrimFieldTestFile);
+  EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
+                              nullptr, nullptr));
+  EXPECT_EQ("Message", file.message_type(0).name());
+  std::vector<int> field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0,
+                              DescriptorProto::kFieldFieldNumber, 0};
+  std::vector<const GeneratedCodeInfo::Annotation*> annotations;
+  atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
+  EXPECT_TRUE(!annotations.empty());
+  for (const auto* annotation : annotations) {
+    auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
+    ASSERT_TRUE(substring.has_value());
+    if (*substring == "ifield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
+                annotation->semantic());
+    } else if (*substring == "set_ifield" || *substring == "clear_ifield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
+                annotation->semantic());
+    }
+  }
+  field_path.back() = 1;
+  annotations.clear();
+  atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
+  EXPECT_TRUE(!annotations.empty());
+  for (const auto* annotation : annotations) {
+    auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
+    ASSERT_TRUE(substring.has_value());
+    if (*substring == "rifield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
+                annotation->semantic());
+    } else if (*substring == "set_rifield" || *substring == "clear_rifield" ||
+               *substring == "add_rifield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
+                annotation->semantic());
+    } else if (*substring == "mutable_rifield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
+                annotation->semantic());
+    }
+  }
+}
+
+constexpr absl::string_view kCordFieldTestFile = R"(
+    syntax = "proto2";
+    package foo;
+    message Message {
+      optional string sfield = 1 [ctype = CORD];
+      repeated string rsfield = 2 [ctype = CORD];
+    }
+)";
+
+TEST_F(CppMetadataTest, AnnotatesCordSemantics) {
+  FileDescriptorProto file;
+  GeneratedCodeInfo info;
+  std::string pb_h;
+  atu::AddFile("test.proto", kCordFieldTestFile);
+  EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
+                              nullptr, nullptr));
+  EXPECT_EQ("Message", file.message_type(0).name());
+  std::vector<int> field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0,
+                              DescriptorProto::kFieldFieldNumber, 0};
+  std::vector<const GeneratedCodeInfo::Annotation*> annotations;
+  atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
+  EXPECT_TRUE(!annotations.empty());
+  for (const auto* annotation : annotations) {
+    auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
+    ASSERT_TRUE(substring.has_value());
+    if (*substring == "sfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
+                annotation->semantic());
+    } else if (*substring == "set_sfield" || *substring == "clear_sfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
+                annotation->semantic());
+    } else if (*substring == "mutable_sfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
+                annotation->semantic());
+    }
+  }
+  field_path.back() = 1;
+  annotations.clear();
+  atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
+  EXPECT_TRUE(!annotations.empty());
+  for (const auto* annotation : annotations) {
+    auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
+    ASSERT_TRUE(substring.has_value());
+    if (*substring == "rsfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
+                annotation->semantic());
+    } else if (*substring == "set_rsfield" || *substring == "clear_rsfield" ||
+               *substring == "add_rsfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
+                annotation->semantic());
+    } else if (*substring == "mutable_rsfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
+                annotation->semantic());
+    }
+  }
+}
+
+constexpr absl::string_view kStringPieceFieldTestFile = R"(
+    syntax = "proto2";
+    package foo;
+    message Message {
+      optional string sfield = 1 [ctype = STRING_PIECE];
+      repeated string rsfield = 2 [ctype = STRING_PIECE];
+    }
+)";
+
+TEST_F(CppMetadataTest, AnnotatesStringPieceSemantics) {
+  FileDescriptorProto file;
+  GeneratedCodeInfo info;
+  std::string pb_h;
+  atu::AddFile("test.proto", kStringPieceFieldTestFile);
+  EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
+                              nullptr, nullptr));
+  EXPECT_EQ("Message", file.message_type(0).name());
+  std::vector<int> field_path{FileDescriptorProto::kMessageTypeFieldNumber, 0,
+                              DescriptorProto::kFieldFieldNumber, 0};
+  std::vector<const GeneratedCodeInfo::Annotation*> annotations;
+  atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
+  EXPECT_TRUE(!annotations.empty());
+  for (const auto* annotation : annotations) {
+    auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
+    ASSERT_TRUE(substring.has_value());
+    if (*substring == "sfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
+                annotation->semantic());
+    } else if (*substring == "set_sfield" || *substring == "set_alias_sfield" ||
+               *substring == "clear_sfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
+                annotation->semantic());
+    } else if (*substring == "mutable_sfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
+                annotation->semantic());
+    }
+  }
+  field_path.back() = 1;
+  annotations.clear();
+  atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
+  EXPECT_TRUE(!annotations.empty());
+  for (const auto* annotation : annotations) {
+    auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
+    ASSERT_TRUE(substring.has_value());
+    if (*substring == "rsfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
+                annotation->semantic());
+    } else if (*substring == "set_rsfield" ||
+               *substring == "set_alias_rsfield" ||
+               *substring == "clear_rsfield" ||
+               *substring == "add_alias_rsfield" ||
+               *substring == "add_rsfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
+                annotation->semantic());
+    } else if (*substring == "mutable_rsfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
+                annotation->semantic());
+    }
+  }
+}
+
 constexpr absl::string_view kStringFieldTestFile = R"(
     syntax = "proto2";
     package foo;
@@ -205,7 +469,7 @@
     if (*substring == "sfield") {
       EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
                 annotation->semantic());
-    } else if (*substring == "set_sfield") {
+    } else if (*substring == "set_sfield" || *substring == "clear_sfield") {
       EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
                 annotation->semantic());
     } else if (*substring == "mutable_sfield") {
@@ -223,7 +487,8 @@
     if (*substring == "rsfield") {
       EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
                 annotation->semantic());
-    } else if (*substring == "set_rsfield") {
+    } else if (*substring == "set_rsfield" || *substring == "clear_rsfield" ||
+               *substring == "add_rsfield") {
       EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
                 annotation->semantic());
     } else if (*substring == "mutable_rsfield") {
@@ -265,6 +530,9 @@
     if (*substring == "mfield") {
       EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
                 annotation->semantic());
+    } else if (*substring == "clear_mfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
+                annotation->semantic());
     } else if (*substring == "mutable_mfield") {
       EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
                 annotation->semantic());
@@ -277,15 +545,60 @@
   for (const auto* annotation : annotations) {
     auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
     ASSERT_TRUE(substring.has_value());
-    if (substring == "rmfield") {
+    if (*substring == "rmfield") {
       EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
                 annotation->semantic());
-    } else if (substring == "mutable_rmfield") {
+    } else if (*substring == "add_rmfield" || *substring == "clear_rmfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
+                annotation->semantic());
+    } else if (*substring == "mutable_rmfield") {
       EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
                 annotation->semantic());
     }
   }
 }
+
+constexpr absl::string_view kLazyMessageFieldTestFile = R"(
+    syntax = "proto2";
+    package foo;
+    message SMessage { }
+    message Message {
+      optional SMessage mfield = 1 [lazy=true];
+    }
+)";
+
+TEST_F(CppMetadataTest, AnnotatesLazyMessageSemantics) {
+  FileDescriptorProto file;
+  GeneratedCodeInfo info;
+  std::string pb_h;
+  atu::AddFile("test.proto", kLazyMessageFieldTestFile);
+  EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
+                              nullptr, nullptr));
+  EXPECT_EQ("Message", file.message_type(1).name());
+  std::vector<int> field_path;
+  field_path.push_back(FileDescriptorProto::kMessageTypeFieldNumber);
+  field_path.push_back(1);
+  field_path.push_back(DescriptorProto::kFieldFieldNumber);
+  field_path.push_back(0);
+  std::vector<const GeneratedCodeInfo::Annotation*> annotations;
+  atu::FindAnnotationsOnPath(info, "test.proto", field_path, &annotations);
+  EXPECT_TRUE(!annotations.empty());
+  for (const auto* annotation : annotations) {
+    auto substring = atu::GetAnnotationSubstring(pb_h, *annotation);
+    ASSERT_TRUE(substring.has_value());
+    if (*substring == "mfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_NONE,
+                annotation->semantic());
+    } else if (*substring == "mutable_mfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_ALIAS,
+                annotation->semantic());
+    } else if (*substring == "set_encoded_mfield" ||
+               *substring == "clear_mfield") {
+      EXPECT_EQ(GeneratedCodeInfo_Annotation_Semantic_SET,
+                annotation->semantic());
+    }
+  }
+}
 }  // namespace
 }  // namespace cpp
 }  // namespace compiler
diff --git a/src/google/protobuf/compiler/cpp/parse_function_generator.cc b/src/google/protobuf/compiler/cpp/parse_function_generator.cc
index 7e93fcd..864ea45 100644
--- a/src/google/protobuf/compiler/cpp/parse_function_generator.cc
+++ b/src/google/protobuf/compiler/cpp/parse_function_generator.cc
@@ -96,7 +96,8 @@
             UseDirectTcParserTable(field, gen_->options_),
             GetOptimizeFor(field->file(), gen_->options_) ==
                 FileOptions::LITE_RUNTIME,
-            ShouldSplit(field, gen_->options_)};
+            ShouldSplit(field, gen_->options_),
+            true};
   }
 
  private:
@@ -216,17 +217,6 @@
       "}\n\n");
 }
 
-static bool NeedsUnknownEnumSupport(const Descriptor* descriptor) {
-  for (int i = 0; i < descriptor->field_count(); ++i) {
-    auto* field = descriptor->field(i);
-    if (field->is_repeated() && field->cpp_type() == field->CPPTYPE_ENUM &&
-        !internal::cpp::HasPreservingUnknownEnumSemantics(field)) {
-      return true;
-    }
-  }
-  return false;
-}
-
 void ParseFunctionGenerator::GenerateTailcallFallbackFunction(
     Formatter& format) {
   ABSL_CHECK(should_generate_tctable());
@@ -236,21 +226,18 @@
   format.Indent();
   format("auto* typed_msg = static_cast<$classname$*>(msg);\n");
 
-  // If we need a side channel, generate the check to jump to the generic
-  // handler to deal with the side channel data.
-  if (NeedsUnknownEnumSupport(descriptor_)) {
-    format(
-        "if (PROTOBUF_PREDICT_FALSE(\n"
-        "    _pbi::TcParser::MustFallbackToGeneric(PROTOBUF_TC_PARAM_PASS))) "
-        "{\n"
-        "  PROTOBUF_MUSTTAIL return "
-        "::_pbi::TcParser::GenericFallback$1$(PROTOBUF_TC_PARAM_PASS);\n"
-        "}\n",
-        GetOptimizeFor(descriptor_->file(), options_) ==
-                FileOptions::LITE_RUNTIME
-            ? "Lite"
-            : "");
-  }
+  // Generate the check to jump to the generic handler to deal with the side
+  // channel data.
+  format(
+      "if (PROTOBUF_PREDICT_FALSE(\n"
+      "    _pbi::TcParser::MustFallbackToGeneric(PROTOBUF_TC_PARAM_PASS))) "
+      "{\n"
+      "  PROTOBUF_MUSTTAIL return "
+      "::_pbi::TcParser::GenericFallback$1$(PROTOBUF_TC_PARAM_PASS);\n"
+      "}\n",
+      GetOptimizeFor(descriptor_->file(), options_) == FileOptions::LITE_RUNTIME
+          ? "Lite"
+          : "");
 
   if (num_hasbits_ > 0) {
     // Sync hasbits
@@ -494,14 +481,10 @@
       } else {
         format("0,  // no _has_bits_\n");
       }
-      if (descriptor_->extension_range_count() == 1) {
-        format(
-            "PROTOBUF_FIELD_OFFSET($classname$, $extensions$),\n"
-            "$1$, $2$,  // extension_range_{low,high}\n",
-            descriptor_->extension_range(0)->start,
-            descriptor_->extension_range(0)->end);
+      if (descriptor_->extension_range_count() != 0) {
+        format("PROTOBUF_FIELD_OFFSET($classname$, $extensions$),\n");
       } else {
-        format("0, 0, 0,  // no _extensions_\n");
+        format("0, // no _extensions_\n");
       }
       format("$1$, $2$,  // max_field_number, fast_idx_mask\n",
              (ordered_fields_.empty() ? 0 : ordered_fields_.back()->number()),
@@ -634,6 +617,29 @@
               case TailCallTableInfo::kNumericOffset:
                 format("{_fl::Offset{$1$}},\n", aux_entry.offset);
                 break;
+              case TailCallTableInfo::kMapAuxInfo: {
+                auto utf8_check = internal::cpp::GetUtf8CheckMode(
+                    aux_entry.field,
+                    GetOptimizeFor(aux_entry.field->file(), options_) ==
+                        FileOptions::LITE_RUNTIME);
+                auto* map_value = aux_entry.field->message_type()->map_value();
+                const bool validated_enum =
+                    map_value->type() == FieldDescriptor::TYPE_ENUM &&
+                    !internal::cpp::HasPreservingUnknownEnumSemantics(
+                        map_value);
+                format(
+                    "{::_pbi::TcParser::GetMapAuxInfo<decltype($classname$("
+                    ").$1$)>($2$, $3$, $4$)},\n",
+                    FieldMemberName(aux_entry.field, /*split=*/false),
+                    utf8_check == internal::cpp::Utf8CheckMode::kStrict,
+                    utf8_check == internal::cpp::Utf8CheckMode::kVerify,
+                    validated_enum);
+                break;
+              }
+              case TailCallTableInfo::kCreateInArena:
+                format("{::_pbi::TcParser::CreateInArenaStorageCb<$1$>},\n",
+                       QualifiedClassName(aux_entry.desc, options_));
+                break;
             }
           }
         }
@@ -1151,7 +1157,7 @@
         format.Set("enum_type",
                    QualifiedClassName(field->enum_type(), options_));
         format(
-            "$uint32$ val = ::$proto_ns$::internal::ReadVarint32(&ptr);\n"
+            "$int32$ val = ::$proto_ns$::internal::ReadVarint32(&ptr);\n"
             "CHK_(ptr);\n");
         if (!internal::cpp::HasPreservingUnknownEnumSemantics(field)) {
           format(
diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.h b/src/google/protobuf/compiler/csharp/csharp_helpers.h
index 5960aa1..f3b165a 100644
--- a/src/google/protobuf/compiler/csharp/csharp_helpers.h
+++ b/src/google/protobuf/compiler/csharp/csharp_helpers.h
@@ -140,28 +140,16 @@
       descriptor->message_type()->file()->name() == "google/protobuf/wrappers.proto";
 }
 
-inline bool IsProto2(const FileDescriptor* descriptor) {
-  return descriptor->syntax() == FileDescriptor::SYNTAX_PROTO2;
-}
-
 inline bool SupportsPresenceApi(const FieldDescriptor* descriptor) {
   // Unlike most languages, we don't generate Has/Clear members for message
   // types, because they can always be set to null in C#. They're not really
   // needed for oneof fields in proto2 either, as everything can be done via
-  // oneof case, but we follow the convention from other languages. Proto3
-  // oneof fields never have Has/Clear members - but will also never have
-  // the explicit optional keyword either.
-  //
-  // None of the built-in helpers (descriptor->has_presence() etc) describe
-  // quite the behavior we want, so the rules are explicit below.
-
-  if (descriptor->is_repeated() ||
-      descriptor->type() == FieldDescriptor::TYPE_MESSAGE) {
+  // oneof case, but we follow the convention from other languages.
+  if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) {
     return false;
   }
-  // has_optional_keyword() has more complex rules for proto2, but that
-  // doesn't matter given the first part of this condition.
-  return IsProto2(descriptor->file()) || descriptor->has_optional_keyword();
+
+  return descriptor->has_presence();
 }
 
 inline bool RequiresPresenceBit(const FieldDescriptor* descriptor) {
diff --git a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
index a0bd2e4..457dd97 100644
--- a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
@@ -67,18 +67,16 @@
 }
 
 void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
-  
   // Note: in multiple places, this code assumes that all fields
   // that support presence are either nullable, or use a presence field bit.
   // Fields which are oneof members are not generated here; they're generated in PrimitiveOneofFieldGenerator below.
   // Extensions are not generated here either.
 
-
-  // Proto2 allows different default values to be specified. These are retained
-  // via static fields. They don't particularly need to be, but we don't need
-  // to change that. In Proto3 the default value we don't generate these
-  // fields, just using the literal instead.
-  if (IsProto2(descriptor_->file())) {
+  // Explicit presence allows different default values to be specified. These
+  // are retained via static fields. They don't particularly need to be, but we
+  // don't need to change that. Under implicit presence we don't use static
+  // fields for default values and just use the literals instead.
+  if (descriptor_->has_presence()) {
     // Note: "private readonly static" isn't as idiomatic as
     // "private static readonly", but changing this now would create a lot of
     // churn in generated code with near-to-zero benefit.
diff --git a/src/google/protobuf/compiler/java/enum.cc b/src/google/protobuf/compiler/java/enum.cc
index 84c99e6..f64d89e 100644
--- a/src/google/protobuf/compiler/java/enum.cc
+++ b/src/google/protobuf/compiler/java/enum.cc
@@ -119,7 +119,7 @@
     printer->Annotate("name", canonical_values_[i]);
   }
 
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (!descriptor_->is_closed()) {
     if (ordinal_is_index) {
       printer->Print("${$UNRECOGNIZED$}$(-1),\n", "{", "", "}", "");
     } else {
@@ -167,7 +167,7 @@
   printer->Print(
       "\n"
       "public final int getNumber() {\n");
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (!descriptor_->is_closed()) {
     if (ordinal_is_index) {
       printer->Print(
           "  if (this == UNRECOGNIZED) {\n"
@@ -251,7 +251,7 @@
     printer->Print(
         "public final com.google.protobuf.Descriptors.EnumValueDescriptor\n"
         "    getValueDescriptor() {\n");
-    if (SupportUnknownEnumValue(descriptor_->file())) {
+    if (!descriptor_->is_closed()) {
       if (ordinal_is_index) {
         printer->Print(
             "  if (this == UNRECOGNIZED) {\n"
@@ -346,7 +346,7 @@
         "      \"EnumValueDescriptor is not for this type.\");\n"
         "  }\n",
         "classname", descriptor_->name());
-    if (SupportUnknownEnumValue(descriptor_->file())) {
+    if (!descriptor_->is_closed()) {
       printer->Print(
           "  if (desc.getIndex() == -1) {\n"
           "    return UNRECOGNIZED;\n"
diff --git a/src/google/protobuf/compiler/java/enum_field.cc b/src/google/protobuf/compiler/java/enum_field.cc
index 59d56d4..935350c 100644
--- a/src/google/protobuf/compiler/java/enum_field.cc
+++ b/src/google/protobuf/compiler/java/enum_field.cc
@@ -120,7 +120,7 @@
   (*variables)["get_has_field_bit_from_local"] =
       GenerateGetBitFromLocal(builderBitIndex);
 
-  if (SupportUnknownEnumValue(descriptor->file())) {
+  if (SupportUnknownEnumValue(descriptor)) {
     variables->insert(
         {"unknown", absl::StrCat((*variables)["type"], ".UNRECOGNIZED")});
   } else {
@@ -162,12 +162,12 @@
 
 void ImmutableEnumFieldGenerator::GenerateInterfaceMembers(
     io::Printer* printer) const {
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(variables_,
                    "$deprecation$boolean has$capitalized_name$();\n");
   }
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, GETTER);
     printer->Print(variables_,
                    "$deprecation$int get$capitalized_name$Value();\n");
@@ -179,7 +179,7 @@
 void ImmutableEnumFieldGenerator::GenerateMembers(io::Printer* printer) const {
   printer->Print(variables_, "private int $name$_ = $default_number$;\n");
   PrintExtraFieldInfo(variables_, printer);
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(variables_,
                    "@java.lang.Override $deprecation$public boolean "
@@ -188,7 +188,7 @@
                    "}\n");
     printer->Annotate("{", "}", descriptor_);
   }
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, GETTER);
     printer->Print(variables_,
                    "@java.lang.Override $deprecation$public int "
@@ -210,7 +210,7 @@
 void ImmutableEnumFieldGenerator::GenerateBuilderMembers(
     io::Printer* printer) const {
   printer->Print(variables_, "private int $name$_ = $default_number$;\n");
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(variables_,
                    "@java.lang.Override $deprecation$public boolean "
@@ -219,7 +219,7 @@
                    "}\n");
     printer->Annotate("{", "}", descriptor_);
   }
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, GETTER);
     printer->Print(variables_,
                    "@java.lang.Override $deprecation$public int "
@@ -286,7 +286,7 @@
                  "    $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\n"
                  "  }\n");
 
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     printer->Print(
         variables_,
         "$kt_deprecation$public var $kt_name$Value: kotlin.Int\n"
@@ -305,7 +305,7 @@
                  "  $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n"
                  "}\n");
 
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER,
                                  /* builder */ false, /* kdoc */ true);
     printer->Print(
@@ -333,12 +333,12 @@
 
 void ImmutableEnumFieldGenerator::GenerateMergingCode(
     io::Printer* printer) const {
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     printer->Print(variables_,
                    "if (other.has$capitalized_name$()) {\n"
                    "  set$capitalized_name$(other.get$capitalized_name$());\n"
                    "}\n");
-  } else if (SupportUnknownEnumValue(descriptor_->file())) {
+  } else if (SupportUnknownEnumValue(descriptor_)) {
     printer->Print(
         variables_,
         "if (other.$name$_ != $default_number$) {\n"
@@ -362,7 +362,7 @@
 
 void ImmutableEnumFieldGenerator::GenerateBuilderParsingCode(
     io::Printer* printer) const {
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     printer->Print(variables_,
                    "$name$_ = input.readEnum();\n"
                    "$set_has_field_bit_builder$\n");
@@ -429,7 +429,7 @@
 void ImmutableEnumOneofFieldGenerator::GenerateMembers(
     io::Printer* printer) const {
   PrintExtraFieldInfo(variables_, printer);
-  ABSL_DCHECK(HasHazzer(descriptor_));
+  ABSL_DCHECK(descriptor_->has_presence());
   WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
   printer->Print(variables_,
                  "$deprecation$public boolean ${$has$capitalized_name$$}$() {\n"
@@ -437,7 +437,7 @@
                  "}\n");
   printer->Annotate("{", "}", descriptor_);
 
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, GETTER);
     printer->Print(
         variables_,
@@ -464,7 +464,7 @@
 
 void ImmutableEnumOneofFieldGenerator::GenerateBuilderMembers(
     io::Printer* printer) const {
-  ABSL_DCHECK(HasHazzer(descriptor_));
+  ABSL_DCHECK(descriptor_->has_presence());
   WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
   printer->Print(variables_,
                  "@java.lang.Override\n"
@@ -473,7 +473,7 @@
                  "}\n");
   printer->Annotate("{", "}", descriptor_);
 
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, GETTER);
     printer->Print(
         variables_,
@@ -552,7 +552,7 @@
 
 void ImmutableEnumOneofFieldGenerator::GenerateMergingCode(
     io::Printer* printer) const {
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     printer->Print(
         variables_,
         "set$capitalized_name$Value(other.get$capitalized_name$Value());\n");
@@ -564,7 +564,7 @@
 
 void ImmutableEnumOneofFieldGenerator::GenerateBuilderParsingCode(
     io::Printer* printer) const {
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     printer->Print(variables_,
                    "int rawValue = input.readEnum();\n"
                    "$set_oneof_case_message$;\n"
@@ -604,7 +604,7 @@
 
 void ImmutableEnumOneofFieldGenerator::GenerateEqualsCode(
     io::Printer* printer) const {
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     printer->Print(
         variables_,
         "if (get$capitalized_name$Value()\n"
@@ -619,7 +619,7 @@
 
 void ImmutableEnumOneofFieldGenerator::GenerateHashCode(
     io::Printer* printer) const {
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     printer->Print(variables_,
                    "hash = (37 * hash) + $constant_name$;\n"
                    "hash = (53 * hash) + get$capitalized_name$Value();\n");
@@ -661,7 +661,7 @@
   WriteFieldAccessorDocComment(printer, descriptor_, LIST_INDEXED_GETTER);
   printer->Print(variables_,
                  "$deprecation$$type$ get$capitalized_name$(int index);\n");
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, LIST_GETTER);
     printer->Print(variables_,
                    "$deprecation$java.util.List<java.lang.Integer>\n"
@@ -716,7 +716,7 @@
       "  return $name$_converter_.convert($name$_.get(index));\n"
       "}\n");
   printer->Annotate("{", "}", descriptor_);
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, LIST_GETTER);
     printer->Print(variables_,
                    "@java.lang.Override\n"
@@ -844,7 +844,7 @@
       "}\n");
   printer->Annotate("{", "}", descriptor_);
 
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, LIST_GETTER);
     printer->Print(variables_,
                    "$deprecation$public java.util.List<java.lang.Integer>\n"
@@ -954,7 +954,7 @@
 void RepeatedImmutableEnumFieldGenerator::GenerateBuilderParsingCode(
     io::Printer* printer) const {
   // Read and store the enum
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     printer->Print(variables_,
                    "int tmpRaw = input.readEnum();\n"
                    "ensure$capitalized_name$IsMutable();\n"
diff --git a/src/google/protobuf/compiler/java/enum_field_lite.cc b/src/google/protobuf/compiler/java/enum_field_lite.cc
index fc3cb1f..162311c 100644
--- a/src/google/protobuf/compiler/java/enum_field_lite.cc
+++ b/src/google/protobuf/compiler/java/enum_field_lite.cc
@@ -123,12 +123,7 @@
                                     (*variables)["default"], ".getNumber()")});
   }
 
-  (*variables)["get_has_field_bit_from_local"] =
-      GenerateGetBitFromLocal(builderBitIndex);
-  (*variables)["set_has_field_bit_to_local"] =
-      GenerateSetBitToLocal(messageBitIndex);
-
-  if (SupportUnknownEnumValue(descriptor->file())) {
+  if (SupportUnknownEnumValue(descriptor)) {
     variables->insert(
         {"unknown", absl::StrCat((*variables)["type"], ".UNRECOGNIZED")});
   } else {
@@ -166,13 +161,13 @@
 
 void ImmutableEnumFieldLiteGenerator::GenerateInterfaceMembers(
     io::Printer* printer) const {
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(variables_,
                    "$deprecation$boolean ${$has$capitalized_name$$}$();\n");
     printer->Annotate("{", "}", descriptor_);
   }
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, GETTER);
     printer->Print(variables_,
                    "$deprecation$int ${$get$capitalized_name$Value$}$();\n");
@@ -193,7 +188,7 @@
         "  fieldNumber=$number$,\n"
         "  type=com.google.protobuf.FieldType.$annotation_field_type$,\n"
         "  isRequired=$required$)\n");
-    if (HasHazzer(descriptor_)) {
+    if (HasHasbit(descriptor_)) {
       printer->Print(variables_,
                      "@com.google.protobuf.ProtoPresenceCheckedField(\n"
                      "  presenceBitsId=$bit_field_id$,\n"
@@ -202,7 +197,7 @@
   }
   printer->Print(variables_, "private int $name$_;\n");
   PrintExtraFieldInfo(variables_, printer);
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(
         variables_,
@@ -212,7 +207,7 @@
         "}\n");
     printer->Annotate("{", "}", descriptor_);
   }
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, GETTER);
     printer->Print(
         variables_,
@@ -232,7 +227,7 @@
   printer->Annotate("{", "}", descriptor_);
 
   // Generate private setters for the builder to proxy into.
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, SETTER);
     printer->Print(variables_,
                    "private void set$capitalized_name$Value(int value) {\n"
@@ -256,7 +251,7 @@
 
 void ImmutableEnumFieldLiteGenerator::GenerateBuilderMembers(
     io::Printer* printer) const {
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(
         variables_,
@@ -266,7 +261,7 @@
         "}\n");
     printer->Annotate("{", "}", descriptor_);
   }
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, GETTER);
     printer->Print(
         variables_,
@@ -327,7 +322,7 @@
                  "    $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\n"
                  "  }\n");
 
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     printer->Print(
         variables_,
         "$kt_deprecation$public var $kt_name$Value: kotlin.Int\n"
@@ -346,7 +341,7 @@
                  "  $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n"
                  "}\n");
 
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER,
                                  /* builder */ false, /* kdoc */ true);
     printer->Print(
@@ -400,7 +395,7 @@
 void ImmutableEnumOneofFieldLiteGenerator::GenerateMembers(
     io::Printer* printer) const {
   PrintExtraFieldInfo(variables_, printer);
-  ABSL_DCHECK(HasHazzer(descriptor_));
+  ABSL_DCHECK(descriptor_->has_presence());
   WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
   printer->Print(variables_,
                  "@java.lang.Override\n"
@@ -409,7 +404,7 @@
                  "}\n");
   printer->Annotate("{", "}", descriptor_);
 
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, GETTER);
     printer->Print(
         variables_,
@@ -436,7 +431,7 @@
   printer->Annotate("{", "}", descriptor_);
 
   // Generate private setters for the builder to proxy into.
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, SETTER);
     printer->Print(variables_,
                    "private void set$capitalized_name$Value(int value) {\n"
@@ -476,7 +471,7 @@
 
 void ImmutableEnumOneofFieldLiteGenerator::GenerateBuilderMembers(
     io::Printer* printer) const {
-  ABSL_DCHECK(HasHazzer(descriptor_));
+  ABSL_DCHECK(descriptor_->has_presence());
   WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
   printer->Print(variables_,
                  "@java.lang.Override\n"
@@ -485,7 +480,7 @@
                  "}\n");
   printer->Annotate("{", "}", descriptor_);
 
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, GETTER);
     printer->Print(
         variables_,
@@ -571,7 +566,7 @@
       variables_,
       "$deprecation$$type$ ${$get$capitalized_name$$}$(int index);\n");
   printer->Annotate("{", "}", descriptor_);
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, LIST_GETTER);
     printer->Print(variables_,
                    "$deprecation$java.util.List<java.lang.Integer>\n"
@@ -641,7 +636,7 @@
       "  return result == null ? $unknown$ : result;\n"
       "}\n");
   printer->Annotate("{", "}", descriptor_);
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, LIST_GETTER);
     printer->Print(variables_,
                    "@java.lang.Override\n"
@@ -707,7 +702,7 @@
                  "  $name$_ = emptyIntList();\n"
                  "}\n");
 
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, SETTER);
     printer->Print(variables_,
                    "private void set$capitalized_name$Value(\n"
@@ -740,7 +735,7 @@
   WriteIntToUtf16CharSequence(GetExperimentalJavaFieldType(descriptor_),
                               output);
   printer->Print(variables_, "\"$name$_\",\n");
-  if (!SupportUnknownEnumValue(descriptor_->file())) {
+  if (!SupportUnknownEnumValue(descriptor_)) {
     PrintEnumVerifierLogic(printer, descriptor_, variables_,
                            /*var_name=*/"$type$",
                            /*terminating_string=*/",\n",
@@ -815,7 +810,7 @@
       "}\n");
   printer->Annotate("{", "}", descriptor_);
 
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (SupportUnknownEnumValue(descriptor_)) {
     WriteFieldEnumValueAccessorDocComment(printer, descriptor_, LIST_GETTER);
     printer->Print(variables_,
                    "@java.lang.Override\n"
diff --git a/src/google/protobuf/compiler/java/enum_lite.cc b/src/google/protobuf/compiler/java/enum_lite.cc
index 5d10a68..abadc78 100644
--- a/src/google/protobuf/compiler/java/enum_lite.cc
+++ b/src/google/protobuf/compiler/java/enum_lite.cc
@@ -97,7 +97,7 @@
     printer->Annotate("name", canonical_values_[i]);
   }
 
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (!descriptor_->is_closed()) {
     printer->Print("${$UNRECOGNIZED$}$(-1),\n", "{", "", "}", "");
     printer->Annotate("{", "}", descriptor_);
   }
@@ -142,7 +142,7 @@
       "\n"
       "@java.lang.Override\n"
       "public final int getNumber() {\n");
-  if (SupportUnknownEnumValue(descriptor_->file())) {
+  if (!descriptor_->is_closed()) {
     printer->Print(
         "  if (this == UNRECOGNIZED) {\n"
         "    throw new java.lang.IllegalArgumentException(\n"
@@ -235,7 +235,7 @@
         "  result.append(getClass().getName()).append('@')\n"
         "      .append(java.lang.Integer.toHexString(\n"
         "        java.lang.System.identityHashCode(this)));\n");
-    if (SupportUnknownEnumValue(descriptor_->file())) {
+    if (!descriptor_->is_closed()) {
       printer->Print(
           "  if (this != UNRECOGNIZED) {\n"
           "    result.append(\" number=\").append(getNumber());\n"
diff --git a/src/google/protobuf/compiler/java/file.cc b/src/google/protobuf/compiler/java/file.cc
index e90bbfe..adaf2a6 100644
--- a/src/google/protobuf/compiler/java/file.cc
+++ b/src/google/protobuf/compiler/java/file.cc
@@ -51,6 +51,7 @@
 #include "google/protobuf/compiler/java/name_resolver.h"
 #include "google/protobuf/compiler/java/service.h"
 #include "google/protobuf/compiler/java/shared_code_generator.h"
+#include "google/protobuf/compiler/retention.h"
 #include "google/protobuf/descriptor.pb.h"
 #include "google/protobuf/dynamic_message.h"
 #include "google/protobuf/io/printer.h"
@@ -452,8 +453,7 @@
   // To find those extensions, we need to parse the data into a dynamic message
   // of the FileDescriptor based on the builder-pool, then we can use
   // reflections to find all extension fields
-  FileDescriptorProto file_proto;
-  file_->CopyTo(&file_proto);
+  FileDescriptorProto file_proto = StripSourceRetentionOptions(*file_);
   std::string file_data;
   file_proto.SerializeToString(&file_data);
   FieldDescriptorSet extensions;
@@ -521,8 +521,7 @@
 
   // Check if custom options exist. If any, try to load immutable classes since
   // custom options are only represented with immutable messages.
-  FileDescriptorProto file_proto;
-  file_->CopyTo(&file_proto);
+  FileDescriptorProto file_proto = StripSourceRetentionOptions(*file_);
   std::string file_data;
   file_proto.SerializeToString(&file_data);
   FieldDescriptorSet extensions;
diff --git a/src/google/protobuf/compiler/java/helpers.cc b/src/google/protobuf/compiler/java/helpers.cc
index 8c3784d..eef40ee 100644
--- a/src/google/protobuf/compiler/java/helpers.cc
+++ b/src/google/protobuf/compiler/java/helpers.cc
@@ -932,7 +932,7 @@
   }
 
   if (field->is_map()) {
-    if (!SupportUnknownEnumValue(field)) {
+    if (!SupportUnknownEnumValue(MapValueField(field))) {
       const FieldDescriptor* value = field->message_type()->map_value();
       if (GetJavaType(value) == JAVATYPE_ENUM) {
         extra_bits |= kMapWithProto2EnumValue;
@@ -977,6 +977,20 @@
   }
 }
 
+const FieldDescriptor* MapKeyField(const FieldDescriptor* descriptor) {
+  ABSL_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, descriptor->type());
+  const Descriptor* message = descriptor->message_type();
+  ABSL_CHECK(message->options().map_entry());
+  return message->map_key();
+}
+
+const FieldDescriptor* MapValueField(const FieldDescriptor* descriptor) {
+  ABSL_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, descriptor->type());
+  const Descriptor* message = descriptor->message_type();
+  ABSL_CHECK(message->options().map_entry());
+  return message->map_value();
+}
+
 }  // namespace java
 }  // namespace compiler
 }  // namespace protobuf
diff --git a/src/google/protobuf/compiler/java/helpers.h b/src/google/protobuf/compiler/java/helpers.h
index 0ebb3b9..7cd37e0 100644
--- a/src/google/protobuf/compiler/java/helpers.h
+++ b/src/google/protobuf/compiler/java/helpers.h
@@ -354,28 +354,13 @@
 // them has a required field. Return true if a required field is found.
 bool HasRequiredFields(const Descriptor* descriptor);
 
-inline bool IsProto2(const FileDescriptor* descriptor) {
-  return descriptor->syntax() == FileDescriptor::SYNTAX_PROTO2;
-}
-
 inline bool IsRealOneof(const FieldDescriptor* descriptor) {
   return descriptor->containing_oneof() &&
          !descriptor->containing_oneof()->is_synthetic();
 }
 
-inline bool HasHazzer(const FieldDescriptor* descriptor) {
-  return !descriptor->is_repeated() &&
-         (descriptor->message_type() || descriptor->has_optional_keyword() ||
-          IsProto2(descriptor->file()) || IsRealOneof(descriptor));
-}
-
 inline bool HasHasbit(const FieldDescriptor* descriptor) {
-  // Note that currently message fields inside oneofs have hasbits. This is
-  // surprising, as the oneof case should avoid any need for a hasbit. But if
-  // you change this method to remove hasbits for oneofs, a few tests fail.
-  // TODO(b/124347790): remove hasbits for oneofs
-  return !descriptor->is_repeated() &&
-         (descriptor->has_optional_keyword() || IsProto2(descriptor->file()));
+  return internal::cpp::HasHasbit(descriptor);
 }
 
 // Whether generate classes expose public PARSER instances.
@@ -387,12 +372,8 @@
 // Whether unknown enum values are kept (i.e., not stored in UnknownFieldSet
 // but in the message and can be queried using additional getters that return
 // ints.
-inline bool SupportUnknownEnumValue(const FileDescriptor* descriptor) {
-  return descriptor->syntax() == FileDescriptor::SYNTAX_PROTO3;
-}
-
 inline bool SupportUnknownEnumValue(const FieldDescriptor* field) {
-  return field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3;
+  return !field->legacy_enum_field_treated_as_closed();
 }
 
 // Check whether a message has repeated fields.
@@ -415,7 +396,7 @@
 }
 
 inline bool CheckUtf8(const FieldDescriptor* descriptor) {
-  return descriptor->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 ||
+  return descriptor->requires_utf8_validation() ||
          descriptor->file()->options().java_string_check_utf8();
 }
 
@@ -448,6 +429,11 @@
 // and the first field number that are not in the table part
 std::pair<int, int> GetTableDrivenNumberOfEntriesAndLookUpStartFieldNumber(
     const FieldDescriptor** fields, int count);
+
+const FieldDescriptor* MapKeyField(const FieldDescriptor* descriptor);
+
+const FieldDescriptor* MapValueField(const FieldDescriptor* descriptor);
+
 }  // namespace java
 }  // namespace compiler
 }  // namespace protobuf
diff --git a/src/google/protobuf/compiler/java/map_field.cc b/src/google/protobuf/compiler/java/map_field.cc
index 13354c4..041690b 100644
--- a/src/google/protobuf/compiler/java/map_field.cc
+++ b/src/google/protobuf/compiler/java/map_field.cc
@@ -47,20 +47,6 @@
 
 namespace {
 
-const FieldDescriptor* KeyField(const FieldDescriptor* descriptor) {
-  ABSL_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, descriptor->type());
-  const Descriptor* message = descriptor->message_type();
-  ABSL_CHECK(message->options().map_entry());
-  return message->map_key();
-}
-
-const FieldDescriptor* ValueField(const FieldDescriptor* descriptor) {
-  ABSL_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, descriptor->type());
-  const Descriptor* message = descriptor->message_type();
-  ABSL_CHECK(message->options().map_entry());
-  return message->map_value();
-}
-
 std::string TypeName(const FieldDescriptor* field,
                      ClassNameResolver* name_resolver, bool boxed) {
   if (GetJavaType(field) == JAVATYPE_MESSAGE) {
@@ -98,8 +84,8 @@
 
   (*variables)["type"] =
       name_resolver->GetImmutableClassName(descriptor->message_type());
-  const FieldDescriptor* key = KeyField(descriptor);
-  const FieldDescriptor* value = ValueField(descriptor);
+  const FieldDescriptor* key = MapKeyField(descriptor);
+  const FieldDescriptor* value = MapValueField(descriptor);
   const JavaType keyJavaType = GetJavaType(key);
   const JavaType valueJavaType = GetJavaType(value);
 
@@ -147,7 +133,7 @@
         {"value_enum_type_pass_through_nullness",
          absl::StrCat(pass_through_nullness, (*variables)["value_enum_type"])});
 
-    if (SupportUnknownEnumValue(descriptor->file())) {
+    if (SupportUnknownEnumValue(value)) {
       // Map unknown values to a special UNRECOGNIZED value if supported.
       variables->insert(
           {"unrecognized_value",
@@ -244,7 +230,9 @@
                  "$deprecation$boolean ${$contains$capitalized_name$$}$(\n"
                  "    $key_type$ key);\n");
   printer->Annotate("{", "}", descriptor_);
-  if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) {
+
+  const FieldDescriptor* value = MapValueField(descriptor_);
+  if (GetJavaType(value) == JAVATYPE_ENUM) {
     if (context_->options().opensource_runtime) {
       printer->Print(variables_,
                      "/**\n"
@@ -275,7 +263,7 @@
         "$deprecation$$value_enum_type$ ${$get$capitalized_name$OrThrow$}$(\n"
         "    $key_type$ key);\n");
     printer->Annotate("{", "}", descriptor_);
-    if (SupportUnknownEnumValue(descriptor_->file())) {
+    if (SupportUnknownEnumValue(value)) {
       printer->Print(
           variables_,
           "/**\n"
@@ -362,7 +350,7 @@
                  "  }\n"
                  "  return $name$_;\n"
                  "}\n");
-  if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) {
+  if (GetJavaType(MapValueField(descriptor_)) == JAVATYPE_ENUM) {
     printer->Print(
         variables_,
         "private static final\n"
@@ -434,7 +422,8 @@
                  "}\n");
   printer->Annotate("{", "}", descriptor_);
 
-  if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) {
+  const FieldDescriptor* value = MapValueField(descriptor_);
+  if (GetJavaType(value) == JAVATYPE_ENUM) {
     if (context_->options().opensource_runtime) {
       printer->Print(
           variables_,
@@ -478,7 +467,7 @@
         "}\n");
     printer->Annotate("{", "}", descriptor_);
 
-    if (SupportUnknownEnumValue(descriptor_->file())) {
+    if (SupportUnknownEnumValue(value)) {
       if (context_->options().opensource_runtime) {
         printer->Print(
             variables_,
@@ -585,7 +574,8 @@
       "}\n");
   printer->Annotate("{", "}", descriptor_);
 
-  if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) {
+  const FieldDescriptor* value = MapValueField(descriptor_);
+  if (GetJavaType(value) == JAVATYPE_ENUM) {
     if (context_->options().opensource_runtime) {
       printer->Print(
           variables_,
@@ -645,7 +635,7 @@
         "}\n");
     printer->Annotate("{", "}", descriptor_);
 
-    if (SupportUnknownEnumValue(descriptor_->file())) {
+    if (SupportUnknownEnumValue(value)) {
       printer->Print(
           variables_,
           "/**\n"
@@ -870,8 +860,8 @@
 
 void ImmutableMapFieldGenerator::GenerateBuilderParsingCode(
     io::Printer* printer) const {
-  if (!SupportUnknownEnumValue(descriptor_->file()) &&
-      GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) {
+  const FieldDescriptor* value = MapValueField(descriptor_);
+  if (!SupportUnknownEnumValue(value) && GetJavaType(value) == JAVATYPE_ENUM) {
     printer->Print(
         variables_,
         "com.google.protobuf.ByteString bytes = input.readBytes();\n"
diff --git a/src/google/protobuf/compiler/java/map_field_lite.cc b/src/google/protobuf/compiler/java/map_field_lite.cc
index ca4091a..8e61a5b 100644
--- a/src/google/protobuf/compiler/java/map_field_lite.cc
+++ b/src/google/protobuf/compiler/java/map_field_lite.cc
@@ -49,20 +49,6 @@
 
 namespace {
 
-const FieldDescriptor* KeyField(const FieldDescriptor* descriptor) {
-  ABSL_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, descriptor->type());
-  const Descriptor* message = descriptor->message_type();
-  ABSL_CHECK(message->options().map_entry());
-  return message->map_key();
-}
-
-const FieldDescriptor* ValueField(const FieldDescriptor* descriptor) {
-  ABSL_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, descriptor->type());
-  const Descriptor* message = descriptor->message_type();
-  ABSL_CHECK(message->options().map_entry());
-  return message->map_value();
-}
-
 std::string TypeName(const FieldDescriptor* field,
                      ClassNameResolver* name_resolver, bool boxed) {
   if (GetJavaType(field) == JAVATYPE_MESSAGE) {
@@ -100,8 +86,8 @@
   ClassNameResolver* name_resolver = context->GetNameResolver();
   (*variables)["type"] =
       name_resolver->GetImmutableClassName(descriptor->message_type());
-  const FieldDescriptor* key = KeyField(descriptor);
-  const FieldDescriptor* value = ValueField(descriptor);
+  const FieldDescriptor* key = MapKeyField(descriptor);
+  const FieldDescriptor* value = MapValueField(descriptor);
   const JavaType keyJavaType = GetJavaType(key);
   const JavaType valueJavaType = GetJavaType(value);
 
@@ -144,7 +130,7 @@
         {"value_enum_type_pass_through_nullness",
          absl::StrCat(pass_through_nullness, (*variables)["value_enum_type"])});
 
-    if (SupportUnknownEnumValue(descriptor->file())) {
+    if (SupportUnknownEnumValue(value)) {
       // Map unknown values to a special UNRECOGNIZED value if supported.
       variables->insert(
           {"unrecognized_value",
@@ -217,7 +203,8 @@
                  "$deprecation$boolean ${$contains$capitalized_name$$}$(\n"
                  "    $key_type$ key);\n");
   printer->Annotate("{", "}", descriptor_);
-  if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) {
+  const FieldDescriptor* value = MapValueField(descriptor_);
+  if (GetJavaType(value) == JAVATYPE_ENUM) {
     if (context_->options().opensource_runtime) {
       printer->Print(variables_,
                      "/**\n"
@@ -248,7 +235,7 @@
         "$deprecation$$value_enum_type$ ${$get$capitalized_name$OrThrow$}$(\n"
         "    $key_type$ key);\n");
     printer->Annotate("{", "}", descriptor_);
-    if (SupportUnknownEnumValue(descriptor_->file())) {
+    if (SupportUnknownEnumValue(value)) {
       printer->Print(
           variables_,
           "/**\n"
@@ -363,7 +350,9 @@
                  "  return internalGet$capitalized_name$().containsKey(key);\n"
                  "}\n");
   printer->Annotate("{", "}", descriptor_);
-  if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) {
+
+  const FieldDescriptor* value = MapValueField(descriptor_);
+  if (GetJavaType(value) == JAVATYPE_ENUM) {
     printer->Print(
         variables_,
         "private static final\n"
@@ -432,7 +421,7 @@
         "  return $name$ValueConverter.doForward(map.get(key));\n"
         "}\n");
     printer->Annotate("{", "}", descriptor_);
-    if (SupportUnknownEnumValue(descriptor_->file())) {
+    if (SupportUnknownEnumValue(value)) {
       printer->Print(
           variables_,
           "/**\n"
@@ -545,7 +534,7 @@
   }
 
   // Generate private setters for the builder to proxy into.
-  if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) {
+  if (GetJavaType(value) == JAVATYPE_ENUM) {
     WriteFieldDocComment(printer, descriptor_);
     printer->Print(
         variables_,
@@ -556,7 +545,7 @@
         "          internalGetMutable$capitalized_name$(),\n"
         "          $name$ValueConverter);\n"
         "}\n");
-    if (SupportUnknownEnumValue(descriptor_->file())) {
+    if (SupportUnknownEnumValue(value)) {
       WriteFieldDocComment(printer, descriptor_);
       printer->Print(
           variables_,
@@ -583,9 +572,9 @@
   printer->Print(variables_,
                  "\"$name$_\",\n"
                  "$default_entry$,\n");
-  if (!SupportUnknownEnumValue(descriptor_) &&
-      GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) {
-    PrintEnumVerifierLogic(printer, ValueField(descriptor_), variables_,
+  const FieldDescriptor* value = MapValueField(descriptor_);
+  if (!SupportUnknownEnumValue(value) && GetJavaType(value) == JAVATYPE_ENUM) {
+    PrintEnumVerifierLogic(printer, MapValueField(descriptor_), variables_,
                            /*var_name=*/"$value_enum_type$",
                            /*terminating_string=*/",\n",
                            /*enforce_lite=*/context_->EnforceLite());
@@ -631,7 +620,8 @@
                  "  return this;\n"
                  "}\n");
   printer->Annotate("{", "}", descriptor_);
-  if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) {
+  const FieldDescriptor* value = MapValueField(descriptor_);
+  if (GetJavaType(value) == JAVATYPE_ENUM) {
     if (context_->options().opensource_runtime) {
       printer->Print(
           variables_,
@@ -711,7 +701,7 @@
         "  return this;\n"
         "}\n");
     printer->Annotate("{", "}", descriptor_);
-    if (SupportUnknownEnumValue(descriptor_->file())) {
+    if (SupportUnknownEnumValue(value)) {
       printer->Print(
           variables_,
           "/**\n"
diff --git a/src/google/protobuf/compiler/java/message.cc b/src/google/protobuf/compiler/java/message.cc
index 5d6a446..ff97d5b 100644
--- a/src/google/protobuf/compiler/java/message.cc
+++ b/src/google/protobuf/compiler/java/message.cc
@@ -971,18 +971,6 @@
 
 // ===================================================================
 
-namespace {
-bool CheckHasBitsForEqualsAndHashCode(const FieldDescriptor* field) {
-  if (field->is_repeated()) {
-    return false;
-  }
-  if (HasHasbit(field)) {
-    return true;
-  }
-  return GetJavaType(field) == JAVATYPE_MESSAGE && !IsRealOneof(field);
-}
-}  // namespace
-
 void ImmutableMessageGenerator::GenerateEqualsAndHashCode(
     io::Printer* printer) {
   printer->Print(
@@ -1011,8 +999,7 @@
     const FieldDescriptor* field = descriptor_->field(i);
     if (!IsRealOneof(field)) {
       const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field);
-      bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field);
-      if (check_has_bits) {
+      if (field->has_presence()) {
         printer->Print(
             "if (has$name$() != other.has$name$()) return false;\n"
             "if (has$name$()) {\n",
@@ -1020,7 +1007,7 @@
         printer->Indent();
       }
       field_generators_.get(field).GenerateEqualsCode(printer);
-      if (check_has_bits) {
+      if (field->has_presence()) {
         printer->Outdent();
         printer->Print("}\n");
       }
@@ -1095,13 +1082,12 @@
     const FieldDescriptor* field = descriptor_->field(i);
     if (!IsRealOneof(field)) {
       const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field);
-      bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field);
-      if (check_has_bits) {
+      if (field->has_presence()) {
         printer->Print("if (has$name$()) {\n", "name", info->capitalized_name);
         printer->Indent();
       }
       field_generators_.get(field).GenerateHashCode(printer);
-      if (check_has_bits) {
+      if (field->has_presence()) {
         printer->Outdent();
         printer->Print("}\n");
       }
diff --git a/src/google/protobuf/compiler/java/message_lite.cc b/src/google/protobuf/compiler/java/message_lite.cc
index 721a463..b23f0e0 100644
--- a/src/google/protobuf/compiler/java/message_lite.cc
+++ b/src/google/protobuf/compiler/java/message_lite.cc
@@ -507,7 +507,7 @@
   std::vector<uint16_t> chars;
 
   int flags = 0;
-  if (IsProto2(descriptor_->file())) {
+  if (descriptor_->file()->syntax() == FileDescriptor::SYNTAX_PROTO2) {
     flags |= 0x1;
   }
   if (descriptor_->options().message_set_wire_format()) {
diff --git a/src/google/protobuf/compiler/java/primitive_field.cc b/src/google/protobuf/compiler/java/primitive_field.cc
index 15a1edb..352499e 100644
--- a/src/google/protobuf/compiler/java/primitive_field.cc
+++ b/src/google/protobuf/compiler/java/primitive_field.cc
@@ -221,7 +221,7 @@
 
 void ImmutablePrimitiveFieldGenerator::GenerateInterfaceMembers(
     io::Printer* printer) const {
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(variables_,
                    "$deprecation$boolean has$capitalized_name$();\n");
@@ -234,7 +234,7 @@
     io::Printer* printer) const {
   printer->Print(variables_, "private $field_type$ $name$_ = $default$;\n");
   PrintExtraFieldInfo(variables_, printer);
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(
         variables_,
@@ -258,7 +258,7 @@
     io::Printer* printer) const {
   printer->Print(variables_, "private $field_type$ $name$_ $default_init$;\n");
 
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(
         variables_,
@@ -332,7 +332,7 @@
                  "  $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n"
                  "}\n");
 
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER,
                                  /* builder */ false, /* kdoc */ true);
     printer->Print(
@@ -363,7 +363,7 @@
 
 void ImmutablePrimitiveFieldGenerator::GenerateMergingCode(
     io::Printer* printer) const {
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     printer->Print(variables_,
                    "if (other.has$capitalized_name$()) {\n"
                    "  set$capitalized_name$(other.get$capitalized_name$());\n"
@@ -527,7 +527,7 @@
 void ImmutablePrimitiveOneofFieldGenerator::GenerateMembers(
     io::Printer* printer) const {
   PrintExtraFieldInfo(variables_, printer);
-  ABSL_DCHECK(HasHazzer(descriptor_));
+  ABSL_DCHECK(descriptor_->has_presence());
   WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
   printer->Print(variables_,
                  "@java.lang.Override\n"
@@ -550,7 +550,7 @@
 
 void ImmutablePrimitiveOneofFieldGenerator::GenerateBuilderMembers(
     io::Printer* printer) const {
-  ABSL_DCHECK(HasHazzer(descriptor_));
+  ABSL_DCHECK(descriptor_->has_presence());
   WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
   printer->Print(variables_,
                  "$deprecation$public boolean ${$has$capitalized_name$$}$() {\n"
diff --git a/src/google/protobuf/compiler/java/primitive_field_lite.cc b/src/google/protobuf/compiler/java/primitive_field_lite.cc
index b7197b8..b494130 100644
--- a/src/google/protobuf/compiler/java/primitive_field_lite.cc
+++ b/src/google/protobuf/compiler/java/primitive_field_lite.cc
@@ -187,10 +187,6 @@
     }
   }
 
-  (*variables)["get_has_field_bit_from_local"] =
-      GenerateGetBitFromLocal(builderBitIndex);
-  (*variables)["set_has_field_bit_to_local"] =
-      GenerateSetBitToLocal(messageBitIndex);
   // Annotations often use { and } variables to denote ranges.
   (*variables)["{"] = "";
   (*variables)["}"] = "";
@@ -219,7 +215,7 @@
 
 void ImmutablePrimitiveFieldLiteGenerator::GenerateInterfaceMembers(
     io::Printer* printer) const {
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(variables_,
                    "$deprecation$boolean has$capitalized_name$();\n");
@@ -246,7 +242,7 @@
         "  fieldNumber=$number$,\n"
         "  type=com.google.protobuf.FieldType.$annotation_field_type$,\n"
         "  isRequired=$required$)\n");
-    if (HasHazzer(descriptor_)) {
+    if (HasHasbit(descriptor_)) {
       printer->Print(variables_,
                      "@com.google.protobuf.ProtoPresenceCheckedField(\n"
                      "  presenceBitsId=$bit_field_id$,\n"
@@ -255,7 +251,7 @@
   }
   printer->Print(variables_, "private $field_type$ $name$_;\n");
   PrintExtraFieldInfo(variables_, printer);
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(
         variables_,
@@ -301,7 +297,7 @@
 
 void ImmutablePrimitiveFieldLiteGenerator::GenerateBuilderMembers(
     io::Printer* printer) const {
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(
         variables_,
@@ -362,7 +358,7 @@
                  "  $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n"
                  "}\n");
 
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER,
                                  /* builder */ false, /* kdoc */ true);
     printer->Print(
@@ -416,7 +412,7 @@
 void ImmutablePrimitiveOneofFieldLiteGenerator::GenerateMembers(
     io::Printer* printer) const {
   PrintExtraFieldInfo(variables_, printer);
-  ABSL_DCHECK(HasHazzer(descriptor_));
+  ABSL_DCHECK(descriptor_->has_presence());
   WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
   printer->Print(variables_,
                  "@java.lang.Override\n"
@@ -464,7 +460,7 @@
 
 void ImmutablePrimitiveOneofFieldLiteGenerator::GenerateBuilderMembers(
     io::Printer* printer) const {
-  ABSL_DCHECK(HasHazzer(descriptor_));
+  ABSL_DCHECK(descriptor_->has_presence());
   WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
   printer->Print(variables_,
                  "@java.lang.Override\n"
diff --git a/src/google/protobuf/compiler/java/string_field.cc b/src/google/protobuf/compiler/java/string_field.cc
index 0913e0a..77336e0 100644
--- a/src/google/protobuf/compiler/java/string_field.cc
+++ b/src/google/protobuf/compiler/java/string_field.cc
@@ -197,7 +197,7 @@
 // repeated fields, the logic is done in LazyStringArrayList.
 void ImmutableStringFieldGenerator::GenerateInterfaceMembers(
     io::Printer* printer) const {
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(variables_,
                    "$deprecation$boolean has$capitalized_name$();\n");
@@ -218,7 +218,7 @@
                  "private volatile java.lang.Object $name$_ = $default$;\n");
   PrintExtraFieldInfo(variables_, printer);
 
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(
         variables_,
@@ -277,7 +277,7 @@
     io::Printer* printer) const {
   printer->Print(variables_,
                  "private java.lang.Object $name$_ $default_init$;\n");
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(
         variables_,
@@ -395,7 +395,7 @@
                  "  $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n"
                  "}\n");
 
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER,
                                  /* builder */ false, /* kdoc */ true);
     printer->Print(
@@ -423,7 +423,7 @@
 
 void ImmutableStringFieldGenerator::GenerateMergingCode(
     io::Printer* printer) const {
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     // Allow a slight breach of abstraction here in order to avoid forcing
     // all string fields to Strings when copying fields from a Message.
     printer->Print(variables_,
@@ -517,7 +517,7 @@
 void ImmutableStringOneofFieldGenerator::GenerateMembers(
     io::Printer* printer) const {
   PrintExtraFieldInfo(variables_, printer);
-  ABSL_DCHECK(HasHazzer(descriptor_));
+  ABSL_DCHECK(descriptor_->has_presence());
   WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
   printer->Print(variables_,
                  "$deprecation$public boolean ${$has$capitalized_name$$}$() {\n"
@@ -581,7 +581,7 @@
 
 void ImmutableStringOneofFieldGenerator::GenerateBuilderMembers(
     io::Printer* printer) const {
-  ABSL_DCHECK(HasHazzer(descriptor_));
+  ABSL_DCHECK(descriptor_->has_presence());
   WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
   printer->Print(variables_,
                  "@java.lang.Override\n"
diff --git a/src/google/protobuf/compiler/java/string_field_lite.cc b/src/google/protobuf/compiler/java/string_field_lite.cc
index 274df53..3aecb44 100644
--- a/src/google/protobuf/compiler/java/string_field_lite.cc
+++ b/src/google/protobuf/compiler/java/string_field_lite.cc
@@ -122,10 +122,6 @@
                        absl::StrCat("!", (*variables)["name"], "_.isEmpty()")});
   }
 
-  (*variables)["get_has_field_bit_from_local"] =
-      GenerateGetBitFromLocal(builderBitIndex);
-  (*variables)["set_has_field_bit_to_local"] =
-      GenerateSetBitToLocal(messageBitIndex);
   // Annotations often use { and } variables to denote text ranges.
   (*variables)["{"] = "";
   (*variables)["}"] = "";
@@ -179,7 +175,7 @@
 //     shouldn't be necessary or used on devices.
 void ImmutableStringFieldLiteGenerator::GenerateInterfaceMembers(
     io::Printer* printer) const {
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(variables_,
                    "$deprecation$boolean ${$has$capitalized_name$$}$();\n");
@@ -207,7 +203,7 @@
         "  type=com.google.protobuf.FieldType.$annotation_field_type$,\n"
         "  isRequired=$required$,\n"
         "  isEnforceUtf8=$enforce_utf8$)\n");
-    if (HasHazzer(descriptor_)) {
+    if (HasHasbit(descriptor_)) {
       printer->Print(variables_,
                      "@com.google.protobuf.ProtoPresenceCheckedField(\n"
                      "  presenceBitsId=$bit_field_id$,\n"
@@ -217,7 +213,7 @@
   printer->Print(variables_, "private java.lang.String $name$_;\n");
   PrintExtraFieldInfo(variables_, printer);
 
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(
         variables_,
@@ -279,7 +275,7 @@
 
 void ImmutableStringFieldLiteGenerator::GenerateBuilderMembers(
     io::Printer* printer) const {
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
     printer->Print(
         variables_,
@@ -361,7 +357,7 @@
                  "  $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n"
                  "}\n");
 
-  if (HasHazzer(descriptor_)) {
+  if (descriptor_->has_presence()) {
     WriteFieldAccessorDocComment(printer, descriptor_, HAZZER,
                                  /* builder */ false, /* kdoc */ true);
     printer->Print(
@@ -408,7 +404,7 @@
 void ImmutableStringOneofFieldLiteGenerator::GenerateMembers(
     io::Printer* printer) const {
   PrintExtraFieldInfo(variables_, printer);
-  ABSL_DCHECK(HasHazzer(descriptor_));
+  ABSL_DCHECK(descriptor_->has_presence());
   WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
   printer->Print(variables_,
                  "@java.lang.Override\n"
@@ -486,7 +482,7 @@
 
 void ImmutableStringOneofFieldLiteGenerator::GenerateBuilderMembers(
     io::Printer* printer) const {
-  ABSL_DCHECK(HasHazzer(descriptor_));
+  ABSL_DCHECK(descriptor_->has_presence());
   WriteFieldAccessorDocComment(printer, descriptor_, HAZZER);
   printer->Print(variables_,
                  "@java.lang.Override\n"
diff --git a/src/google/protobuf/compiler/main.cc b/src/google/protobuf/compiler/main.cc
index ea9adfb..f5a064b 100644
--- a/src/google/protobuf/compiler/main.cc
+++ b/src/google/protobuf/compiler/main.cc
@@ -39,6 +39,7 @@
 #include "google/protobuf/compiler/python/generator.h"
 #include "google/protobuf/compiler/python/pyi_generator.h"
 #include "google/protobuf/compiler/ruby/ruby_generator.h"
+#include "google/protobuf/compiler/rust/generator.h"
 
 // Must be included last.
 #include "google/protobuf/port_def.inc"
@@ -112,6 +113,10 @@
   cli.RegisterGenerator("--objc_out", "--objc_opt", &objc_generator,
                         "Generate Objective-C header and source.");
 
+  // Rust
+  rust::RustGenerator rust_generator;
+  cli.RegisterGenerator("--rust_out", &rust_generator,
+                        "Generate Rust sources.");
   return cli.Run(argc, argv);
 }
 
diff --git a/src/google/protobuf/compiler/objectivec/import_writer.cc b/src/google/protobuf/compiler/objectivec/import_writer.cc
index d7ef3b9..ca0718b 100644
--- a/src/google/protobuf/compiler/objectivec/import_writer.cc
+++ b/src/google/protobuf/compiler/objectivec/import_writer.cc
@@ -140,15 +140,11 @@
     return;
   }
 
-  // Lazy parse any mappings.
-  if (need_to_parse_mapping_file_) {
-    ParseFrameworkMappings();
-  }
+  auto module_name = ModuleForFile(file);
 
-  auto proto_lookup = proto_file_to_framework_name_.find(file->name());
-  if (proto_lookup != proto_file_to_framework_name_.end()) {
+  if (!module_name.empty()) {
     other_framework_imports_.emplace_back(absl::StrCat(
-        proto_lookup->second, "/", FilePathBasename(file), header_extension));
+        module_name, "/", FilePathBasename(file), header_extension));
     return;
   }
 
@@ -166,6 +162,23 @@
   protobuf_imports_.push_back(header_name);
 }
 
+std::string ImportWriter::ModuleForFile(const FileDescriptor* file) {
+  ABSL_DCHECK(!IsProtobufLibraryBundledProtoFile(file));
+
+  // Lazy parse any mappings.
+  if (need_to_parse_mapping_file_) {
+    ParseFrameworkMappings();
+  }
+
+  auto proto_lookup = proto_file_to_framework_name_.find(file->name());
+
+  if (proto_lookup != proto_file_to_framework_name_.end()) {
+    return proto_lookup->second;
+  }
+
+  return "";
+}
+
 void ImportWriter::PrintFileImports(io::Printer* p) const {
   if (!other_framework_imports_.empty()) {
     for (const auto& header : other_framework_imports_) {
diff --git a/src/google/protobuf/compiler/objectivec/import_writer.h b/src/google/protobuf/compiler/objectivec/import_writer.h
index 9f5e0a0..1c168b1 100644
--- a/src/google/protobuf/compiler/objectivec/import_writer.h
+++ b/src/google/protobuf/compiler/objectivec/import_writer.h
@@ -55,6 +55,9 @@
 
   void AddFile(const FileDescriptor* file, const std::string& header_extension);
   void AddRuntimeImport(const std::string& header_name);
+  // This can return an empty string if there is no module for the file. It also
+  // does not handle bundled proto files.
+  std::string ModuleForFile(const FileDescriptor* file);
 
   void PrintFileImports(io::Printer* p) const;
   void PrintRuntimeImports(io::Printer* p, bool default_cpp_symbol) const;
diff --git a/src/google/protobuf/compiler/plugin.pb.h b/src/google/protobuf/compiler/plugin.pb.h
index bc6d390..5e3404e 100644
--- a/src/google/protobuf/compiler/plugin.pb.h
+++ b/src/google/protobuf/compiler/plugin.pb.h
@@ -9,7 +9,7 @@
 #include <type_traits>
 
 #include "google/protobuf/port_def.inc"
-#if PROTOBUF_VERSION < 4022000
+#if PROTOBUF_VERSION < 3021000
 #error "This file was generated by a newer version of protoc which is"
 #error "incompatible with your Protocol Buffer headers. Please update"
 #error "your headers."
diff --git a/src/google/protobuf/compiler/python/BUILD.bazel b/src/google/protobuf/compiler/python/BUILD.bazel
index de1e06c..90d5d21 100644
--- a/src/google/protobuf/compiler/python/BUILD.bazel
+++ b/src/google/protobuf/compiler/python/BUILD.bazel
@@ -28,6 +28,7 @@
     deps = [
         "//src/google/protobuf:protobuf_nowkt",
         "//src/google/protobuf/compiler:code_generator",
+        "//src/google/protobuf/compiler:retention",
         "@com_google_absl//absl/strings",
         "@com_google_absl//absl/synchronization",
     ],
@@ -61,9 +62,12 @@
 
 filegroup(
     name = "test_srcs",
-    srcs = glob([
-        "*_test.cc",
-        "*unittest.cc",
-    ], allow_empty = True),
+    srcs = glob(
+        [
+            "*_test.cc",
+            "*unittest.cc",
+        ],
+        allow_empty = True,
+    ),
     visibility = ["//src/google/protobuf/compiler:__pkg__"],
 )
diff --git a/src/google/protobuf/compiler/python/generator.cc b/src/google/protobuf/compiler/python/generator.cc
index 58ffe07..651aaa9 100644
--- a/src/google/protobuf/compiler/python/generator.cc
+++ b/src/google/protobuf/compiler/python/generator.cc
@@ -64,6 +64,7 @@
 #include "absl/strings/substitute.h"
 #include "google/protobuf/compiler/python/helpers.h"
 #include "google/protobuf/compiler/python/pyi_generator.h"
+#include "google/protobuf/compiler/retention.h"
 #include "google/protobuf/descriptor.h"
 #include "google/protobuf/descriptor.pb.h"
 #include "google/protobuf/io/printer.h"
@@ -249,8 +250,7 @@
 
   std::string filename = GetFileName(file, ".py");
 
-  FileDescriptorProto fdp;
-  file_->CopyTo(&fdp);
+  FileDescriptorProto fdp = StripSourceRetentionOptions(*file_);
   fdp.SerializeToString(&file_descriptor_serialized_);
 
   if (!opensource_runtime_ && GeneratingDescriptorProto()) {
@@ -262,9 +262,9 @@
       std::unique_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
       io::Printer printer(output.get(), '$');
       printer.Print(
-          "from $internal_package$ import descriptor_pb2\n"
-          "\n",
-          "internal_package", InternalPackage());
+          "from google3.net.google.protobuf.python.internal import "
+          "descriptor_pb2\n"
+          "\n");
 
       // For static checkers, we need to explicitly assign to the symbols we
       // publicly export.
@@ -342,7 +342,7 @@
   FixAllDescriptorOptions();
 
   // Set serialized_start and serialized_end.
-  SetSerializedPbInterval();
+  SetSerializedPbInterval(fdp);
 
   printer_->Outdent();
   if (HasGenericServices(file)) {
@@ -373,11 +373,10 @@
     printer_->Print("import google3\n");
   }
   printer_->Print(
-      "from $internal_package$ import builder as _builder\n"
-      "from $public_package$ import descriptor as _descriptor\n"
-      "from $public_package$ import descriptor_pool as _descriptor_pool\n"
-      "from $public_package$ import symbol_database as _symbol_database\n",
-      "internal_package", InternalPackage(), "public_package", PublicPackage());
+      "from google.protobuf import descriptor as _descriptor\n"
+      "from google.protobuf import descriptor_pool as _descriptor_pool\n"
+      "from google.protobuf import symbol_database as _symbol_database\n"
+      "from google.protobuf.internal import builder as _builder\n");
 
   printer_->Print("# @@protoc_insertion_point(imports)\n\n");
   printer_->Print("_sym_db = _symbol_database.Default()\n");
@@ -442,7 +441,8 @@
   m["name"] = file_->name();
   m["package"] = file_->package();
   m["syntax"] = StringifySyntax(file_->syntax());
-  m["options"] = OptionsValue(file_->options().SerializeAsString());
+  m["options"] = OptionsValue(
+      StripLocalSourceRetentionOptions(*file_).SerializeAsString());
   m["serialized_descriptor"] = absl::CHexEscape(file_descriptor_serialized_);
   if (GeneratingDescriptorProto()) {
     printer_->Print("if _descriptor._USE_C_DESCRIPTORS == False:\n");
@@ -528,7 +528,8 @@
       "  create_key=_descriptor._internal_create_key,\n"
       "  values=[\n";
   std::string options_string;
-  enum_descriptor.options().SerializeToString(&options_string);
+  StripLocalSourceRetentionOptions(enum_descriptor)
+      .SerializeToString(&options_string);
   printer_->Print(m, enum_descriptor_template);
   printer_->Indent();
   printer_->Indent();
@@ -681,7 +682,8 @@
   printer_->Outdent();
   printer_->Print("],\n");
   std::string options_string;
-  message_descriptor.options().SerializeToString(&options_string);
+  StripLocalSourceRetentionOptions(message_descriptor)
+      .SerializeToString(&options_string);
   printer_->Print(
       "serialized_options=$options_value$,\n"
       "is_extendable=$extendable$,\n"
@@ -708,7 +710,8 @@
     m["name"] = desc->name();
     m["full_name"] = desc->full_name();
     m["index"] = absl::StrCat(desc->index());
-    options_string = OptionsValue(desc->options().SerializeAsString());
+    options_string = OptionsValue(
+        StripLocalSourceRetentionOptions(*desc).SerializeAsString());
     if (options_string == "None") {
       m["serialized_options"] = "";
     } else {
@@ -1050,7 +1053,8 @@
   // TODO(robinson): Fix up EnumValueDescriptor "type" fields.
   // More circular references.  ::sigh::
   std::string options_string;
-  descriptor.options().SerializeToString(&options_string);
+  StripLocalSourceRetentionOptions(descriptor)
+      .SerializeToString(&options_string);
   absl::flat_hash_map<absl::string_view, std::string> m;
   m["name"] = descriptor.name();
   m["index"] = absl::StrCat(descriptor.index());
@@ -1078,7 +1082,7 @@
 void Generator::PrintFieldDescriptor(const FieldDescriptor& field,
                                      bool is_extension) const {
   std::string options_string;
-  field.options().SerializeToString(&options_string);
+  StripLocalSourceRetentionOptions(field).SerializeToString(&options_string);
   absl::flat_hash_map<absl::string_view, std::string> m;
   m["name"] = field.name();
   m["full_name"] = field.full_name();
@@ -1206,31 +1210,17 @@
   return name;
 }
 
-std::string Generator::PublicPackage() const {
-  return opensource_runtime_ ? "google.protobuf"
-                             : "google3.net.google.protobuf.python.public";
-}
-
-std::string Generator::InternalPackage() const {
-  return opensource_runtime_ ? "google.protobuf.internal"
-                             : "google3.net.google.protobuf.python.internal";
-}
-
-// Prints standard constructor arguments serialized_start and serialized_end.
+// Prints descriptor offsets _serialized_start and _serialized_end.
 // Args:
-//   descriptor: The cpp descriptor to have a serialized reference.
-//   proto: A proto
+//   descriptor_proto: The descriptor proto to have a serialized reference.
 // Example printer output:
-// serialized_start=41,
-// serialized_end=43,
-//
-template <typename DescriptorT, typename DescriptorProtoT>
-void Generator::PrintSerializedPbInterval(const DescriptorT& descriptor,
-                                          DescriptorProtoT& proto,
-                                          absl::string_view name) const {
-  descriptor.CopyTo(&proto);
+// _globals['_MYMESSAGE']._serialized_start=47
+// _globals['_MYMESSAGE']._serialized_end=76
+template <typename DescriptorProtoT>
+void Generator::PrintSerializedPbInterval(
+    const DescriptorProtoT& descriptor_proto, absl::string_view name) const {
   std::string sp;
-  proto.SerializeToString(&sp);
+  descriptor_proto.SerializeToString(&sp);
   int offset = file_descriptor_serialized_.find(sp);
   ABSL_CHECK_GE(offset, 0);
 
@@ -1254,43 +1244,47 @@
 }
 }  // namespace
 
-void Generator::SetSerializedPbInterval() const {
+// Generates the start and end offsets for each entity in the serialized file
+// descriptor. The file argument must exactly match what was serialized into
+// file_descriptor_serialized_, and should already have had any
+// source-retention options stripped out. This is important because we need an
+// exact byte-for-byte match so that we can successfully find the correct
+// offsets in the serialized descriptors.
+void Generator::SetSerializedPbInterval(const FileDescriptorProto& file) const {
   // Top level enums.
   for (int i = 0; i < file_->enum_type_count(); ++i) {
-    EnumDescriptorProto proto;
     const EnumDescriptor& descriptor = *file_->enum_type(i);
-    PrintSerializedPbInterval(descriptor, proto,
+    PrintSerializedPbInterval(file.enum_type(i),
                               ModuleLevelDescriptorName(descriptor));
   }
 
   // Messages.
   for (int i = 0; i < file_->message_type_count(); ++i) {
-    SetMessagePbInterval(*file_->message_type(i));
+    SetMessagePbInterval(file.message_type(i), *file_->message_type(i));
   }
 
   // Services.
   for (int i = 0; i < file_->service_count(); ++i) {
-    ServiceDescriptorProto proto;
     const ServiceDescriptor& service = *file_->service(i);
-    PrintSerializedPbInterval(service, proto,
+    PrintSerializedPbInterval(file.service(i),
                               ModuleLevelServiceDescriptorName(service));
   }
 }
 
-void Generator::SetMessagePbInterval(const Descriptor& descriptor) const {
-  DescriptorProto message_proto;
-  PrintSerializedPbInterval(descriptor, message_proto,
+void Generator::SetMessagePbInterval(const DescriptorProto& message_proto,
+                                     const Descriptor& descriptor) const {
+  PrintSerializedPbInterval(message_proto,
                             ModuleLevelDescriptorName(descriptor));
 
   // Nested messages.
   for (int i = 0; i < descriptor.nested_type_count(); ++i) {
-    SetMessagePbInterval(*descriptor.nested_type(i));
+    SetMessagePbInterval(message_proto.nested_type(i),
+                         *descriptor.nested_type(i));
   }
 
   for (int i = 0; i < descriptor.enum_type_count(); ++i) {
-    EnumDescriptorProto proto;
     const EnumDescriptor& enum_des = *descriptor.enum_type(i);
-    PrintSerializedPbInterval(enum_des, proto,
+    PrintSerializedPbInterval(message_proto.enum_type(i),
                               ModuleLevelDescriptorName(enum_des));
   }
 }
@@ -1298,7 +1292,8 @@
 // Prints expressions that set the options field of all descriptors.
 void Generator::FixAllDescriptorOptions() const {
   // Prints an expression that sets the file descriptor's options.
-  std::string file_options = OptionsValue(file_->options().SerializeAsString());
+  std::string file_options = OptionsValue(
+      StripLocalSourceRetentionOptions(*file_).SerializeAsString());
   if (file_options != "None") {
     PrintDescriptorOptionsFixingCode(kDescriptorKey, file_options, printer_);
   } else {
@@ -1326,7 +1321,8 @@
 }
 
 void Generator::FixOptionsForOneof(const OneofDescriptor& oneof) const {
-  std::string oneof_options = OptionsValue(oneof.options().SerializeAsString());
+  std::string oneof_options =
+      OptionsValue(StripLocalSourceRetentionOptions(oneof).SerializeAsString());
   if (oneof_options != "None") {
     std::string oneof_name = absl::Substitute(
         "$0.$1['$2']", ModuleLevelDescriptorName(*oneof.containing_type()),
@@ -1339,15 +1335,15 @@
 // value descriptors.
 void Generator::FixOptionsForEnum(const EnumDescriptor& enum_descriptor) const {
   std::string descriptor_name = ModuleLevelDescriptorName(enum_descriptor);
-  std::string enum_options =
-      OptionsValue(enum_descriptor.options().SerializeAsString());
+  std::string enum_options = OptionsValue(
+      StripLocalSourceRetentionOptions(enum_descriptor).SerializeAsString());
   if (enum_options != "None") {
     PrintDescriptorOptionsFixingCode(descriptor_name, enum_options, printer_);
   }
   for (int i = 0; i < enum_descriptor.value_count(); ++i) {
     const EnumValueDescriptor& value_descriptor = *enum_descriptor.value(i);
-    std::string value_options =
-        OptionsValue(value_descriptor.options().SerializeAsString());
+    std::string value_options = OptionsValue(
+        StripLocalSourceRetentionOptions(value_descriptor).SerializeAsString());
     if (value_options != "None") {
       PrintDescriptorOptionsFixingCode(
           absl::StrFormat("%s.values_by_name[\"%s\"]", descriptor_name.c_str(),
@@ -1363,8 +1359,8 @@
     const ServiceDescriptor& service_descriptor) const {
   std::string descriptor_name =
       ModuleLevelServiceDescriptorName(service_descriptor);
-  std::string service_options =
-      OptionsValue(service_descriptor.options().SerializeAsString());
+  std::string service_options = OptionsValue(
+      StripLocalSourceRetentionOptions(service_descriptor).SerializeAsString());
   if (service_options != "None") {
     PrintDescriptorOptionsFixingCode(descriptor_name, service_options,
                                      printer_);
@@ -1372,8 +1368,8 @@
 
   for (int i = 0; i < service_descriptor.method_count(); ++i) {
     const MethodDescriptor* method = service_descriptor.method(i);
-    std::string method_options =
-        OptionsValue(method->options().SerializeAsString());
+    std::string method_options = OptionsValue(
+        StripLocalSourceRetentionOptions(*method).SerializeAsString());
     if (method_options != "None") {
       std::string method_name = absl::StrCat(
           descriptor_name, ".methods_by_name['", method->name(), "']");
@@ -1385,7 +1381,8 @@
 // Prints expressions that set the options for field descriptors (including
 // extensions).
 void Generator::FixOptionsForField(const FieldDescriptor& field) const {
-  std::string field_options = OptionsValue(field.options().SerializeAsString());
+  std::string field_options =
+      OptionsValue(StripLocalSourceRetentionOptions(field).SerializeAsString());
   if (field_options != "None") {
     std::string field_name;
     if (field.is_extension()) {
@@ -1430,8 +1427,8 @@
     FixOptionsForField(field);
   }
   // Message option for this message.
-  std::string message_options =
-      OptionsValue(descriptor.options().SerializeAsString());
+  std::string message_options = OptionsValue(
+      StripLocalSourceRetentionOptions(descriptor).SerializeAsString());
   if (message_options != "None") {
     std::string descriptor_name = ModuleLevelDescriptorName(descriptor);
     PrintDescriptorOptionsFixingCode(descriptor_name, message_options,
diff --git a/src/google/protobuf/compiler/python/generator.h b/src/google/protobuf/compiler/python/generator.h
index c5182c4..65c16f5 100644
--- a/src/google/protobuf/compiler/python/generator.h
+++ b/src/google/protobuf/compiler/python/generator.h
@@ -41,6 +41,7 @@
 #include "absl/strings/string_view.h"
 #include "absl/synchronization/mutex.h"
 #include "google/protobuf/compiler/code_generator.h"
+#include "google/protobuf/descriptor.pb.h"
 
 // Must be included last.
 #include "google/protobuf/port_def.inc"
@@ -163,12 +164,9 @@
   std::string ModuleLevelMessageName(const Descriptor& descriptor) const;
   std::string ModuleLevelServiceDescriptorName(
       const ServiceDescriptor& descriptor) const;
-  std::string PublicPackage() const;
-  std::string InternalPackage() const;
 
-  template <typename DescriptorT, typename DescriptorProtoT>
-  void PrintSerializedPbInterval(const DescriptorT& descriptor,
-                                 DescriptorProtoT& proto,
+  template <typename DescriptorProtoT>
+  void PrintSerializedPbInterval(const DescriptorProtoT& descriptor_proto,
                                  absl::string_view name) const;
 
   void FixAllDescriptorOptions() const;
@@ -178,8 +176,9 @@
   void FixOptionsForService(const ServiceDescriptor& descriptor) const;
   void FixOptionsForMessage(const Descriptor& descriptor) const;
 
-  void SetSerializedPbInterval() const;
-  void SetMessagePbInterval(const Descriptor& descriptor) const;
+  void SetSerializedPbInterval(const FileDescriptorProto& file) const;
+  void SetMessagePbInterval(const DescriptorProto& message_proto,
+                            const Descriptor& descriptor) const;
 
   void CopyPublicDependenciesAliases(absl::string_view copy_from,
                                      const FileDescriptor* file) const;
diff --git a/src/google/protobuf/compiler/retention.cc b/src/google/protobuf/compiler/retention.cc
index 087af72..04937a4 100644
--- a/src/google/protobuf/compiler/retention.cc
+++ b/src/google/protobuf/compiler/retention.cc
@@ -42,6 +42,7 @@
 namespace compiler {
 
 namespace {
+
 // Recursively strips any options with source retention from the message.
 void StripMessage(Message& m) {
   const Reflection* reflection = m.GetReflection();
@@ -62,43 +63,107 @@
     }
   }
 }
+
+// Converts the descriptor to a dynamic message if necessary, and then strips
+// out all source-retention options.
+//
+// The options message may have custom options set on it, and these would
+// ordinarily appear as unknown fields since they are not linked into protoc.
+// Using a dynamic message allows us to see these custom options. To convert
+// back and forth between the generated type and the dynamic message, we have
+// to serialize one and parse that into the other.
+void ConvertToDynamicMessageAndStripOptions(Message& m,
+                                            const DescriptorPool& pool) {
+  // We need to look up the descriptor in the pool so that we can get a
+  // descriptor which knows about any custom options that were used in the
+  // .proto file.
+  const Descriptor* descriptor = pool.FindMessageTypeByName(m.GetTypeName());
+
+  if (descriptor == nullptr) {
+    // If the pool does not contain the descriptor, then this proto file does
+    // not transitively depend on descriptor.proto, in which case we know there
+    // are no custom options to worry about.
+    StripMessage(m);
+  } else {
+    DynamicMessageFactory factory;
+    std::unique_ptr<Message> dynamic_message(
+        factory.GetPrototype(descriptor)->New());
+    std::string serialized;
+    ABSL_CHECK(m.SerializeToString(&serialized));
+    ABSL_CHECK(dynamic_message->ParseFromString(serialized));
+    StripMessage(*dynamic_message);
+    ABSL_CHECK(dynamic_message->SerializeToString(&serialized));
+    ABSL_CHECK(m.ParseFromString(serialized));
+  }
+}
+
+// Returns a const reference to the descriptor pool associated with the given
+// descriptor.
+template <typename DescriptorType>
+const google::protobuf::DescriptorPool& GetPool(const DescriptorType& descriptor) {
+  return *descriptor.file()->pool();
+}
+
+// Specialization for FileDescriptor.
+const google::protobuf::DescriptorPool& GetPool(const FileDescriptor& descriptor) {
+  return *descriptor.pool();
+}
+
+// Returns the options associated with the given descriptor, with all
+// source-retention options stripped out.
+template <typename DescriptorType>
+auto StripLocalOptions(const DescriptorType& descriptor) {
+  auto options = descriptor.options();
+  ConvertToDynamicMessageAndStripOptions(options, GetPool(descriptor));
+  return options;
+}
+
 }  // namespace
 
 FileDescriptorProto StripSourceRetentionOptions(const FileDescriptor& file) {
   FileDescriptorProto file_proto;
   file.CopyTo(&file_proto);
-
-  // We need to look up the descriptor in file.pool() so that we can get a
-  // descriptor which knows about any custom options that were used in the
-  // .proto file.
-  const Descriptor* descriptor =
-      file.pool()->FindMessageTypeByName(FileDescriptorProto().GetTypeName());
-
-  if (descriptor == nullptr) {
-    // If the pool does not contain the descriptor for FileDescriptorProto,
-    // then this proto file does not transitively depend on descriptor.proto,
-    // in which case we know there are no custom options to worry about.
-    StripMessage(file_proto);
-  } else {
-    // The options message may have custom options set on it, and these would
-    // ordinarily appear as unknown fields since they are not linked into
-    // protoc. Using a dynamic message allows us to see these custom options.
-    // To convert back and forth between the generated type and the dynamic
-    // message, we have to serialize one and parse that into the other.
-    DynamicMessageFactory factory;
-    std::unique_ptr<Message> dynamic_message(
-        factory.GetPrototype(descriptor)->New());
-    std::string serialized;
-    ABSL_CHECK(file_proto.SerializeToString(&serialized));
-    ABSL_CHECK(dynamic_message->ParseFromString(serialized));
-    StripMessage(*dynamic_message);
-    ABSL_CHECK(dynamic_message->SerializeToString(&serialized));
-    ABSL_CHECK(file_proto.ParseFromString(serialized));
-  }
-
+  ConvertToDynamicMessageAndStripOptions(file_proto, *file.pool());
   return file_proto;
 }
 
+EnumOptions StripLocalSourceRetentionOptions(const EnumDescriptor& descriptor) {
+  return StripLocalOptions(descriptor);
+}
+
+EnumValueOptions StripLocalSourceRetentionOptions(
+    const EnumValueDescriptor& descriptor) {
+  return StripLocalOptions(descriptor);
+}
+
+FieldOptions StripLocalSourceRetentionOptions(
+    const FieldDescriptor& descriptor) {
+  return StripLocalOptions(descriptor);
+}
+
+FileOptions StripLocalSourceRetentionOptions(const FileDescriptor& descriptor) {
+  return StripLocalOptions(descriptor);
+}
+
+MessageOptions StripLocalSourceRetentionOptions(const Descriptor& descriptor) {
+  return StripLocalOptions(descriptor);
+}
+
+MethodOptions StripLocalSourceRetentionOptions(
+    const MethodDescriptor& descriptor) {
+  return StripLocalOptions(descriptor);
+}
+
+OneofOptions StripLocalSourceRetentionOptions(
+    const OneofDescriptor& descriptor) {
+  return StripLocalOptions(descriptor);
+}
+
+ServiceOptions StripLocalSourceRetentionOptions(
+    const ServiceDescriptor& descriptor) {
+  return StripLocalOptions(descriptor);
+}
+
 }  // namespace compiler
 }  // namespace protobuf
 }  // namespace google
diff --git a/src/google/protobuf/compiler/retention.h b/src/google/protobuf/compiler/retention.h
index b97ab1e..0579b06 100644
--- a/src/google/protobuf/compiler/retention.h
+++ b/src/google/protobuf/compiler/retention.h
@@ -46,6 +46,28 @@
 PROTOC_EXPORT FileDescriptorProto
 StripSourceRetentionOptions(const FileDescriptor& file);
 
+// The following functions take a descriptor and strip all source-retention
+// options from just the local entity (e.g. message, enum, field). Most code
+// generators should not need these functions, but they are sometimes useful if
+// you need to strip the options on a single entity rather than handling the
+// entire file at once.
+PROTOC_EXPORT EnumOptions
+StripLocalSourceRetentionOptions(const EnumDescriptor& descriptor);
+PROTOC_EXPORT EnumValueOptions
+StripLocalSourceRetentionOptions(const EnumValueDescriptor& descriptor);
+PROTOC_EXPORT FieldOptions
+StripLocalSourceRetentionOptions(const FieldDescriptor& descriptor);
+PROTOC_EXPORT FileOptions
+StripLocalSourceRetentionOptions(const FileDescriptor& descriptor);
+PROTOC_EXPORT MessageOptions
+StripLocalSourceRetentionOptions(const Descriptor& descriptor);
+PROTOC_EXPORT MethodOptions
+StripLocalSourceRetentionOptions(const MethodDescriptor& descriptor);
+PROTOC_EXPORT OneofOptions
+StripLocalSourceRetentionOptions(const OneofDescriptor& descriptor);
+PROTOC_EXPORT ServiceOptions
+StripLocalSourceRetentionOptions(const ServiceDescriptor& descriptor);
+
 }  // namespace compiler
 }  // namespace protobuf
 }  // namespace google
diff --git a/src/google/protobuf/compiler/ruby/ruby_generator.cc b/src/google/protobuf/compiler/ruby/ruby_generator.cc
index fbeb2ef..5013fbd 100644
--- a/src/google/protobuf/compiler/ruby/ruby_generator.cc
+++ b/src/google/protobuf/compiler/ruby/ruby_generator.cc
@@ -79,8 +79,7 @@
 }
 
 std::string LabelForField(const FieldDescriptor* field) {
-  if (field->has_optional_keyword() &&
-      field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3) {
+  if (field->has_optional_keyword() && field->containing_oneof() != nullptr) {
     return "proto3_optional";
   }
   switch (field->label()) {
@@ -520,11 +519,9 @@
     printer->Print("\n");
   }
 
-  // TODO: Remove this when ruby supports extensions for proto2 syntax.
-  if (file->syntax() == FileDescriptor::SYNTAX_PROTO2 &&
-      file->extension_count() > 0) {
-    ABSL_LOG(WARNING)
-        << "Extensions are not yet supported for proto2 .proto files.";
+  // TODO: Remove this when ruby supports extensions.
+  if (file->extension_count() > 0) {
+    ABSL_LOG(WARNING) << "Extensions are not yet supported in Ruby.";
   }
 
   bool use_raw_descriptor = file->name() == "google/protobuf/descriptor.proto";
@@ -560,9 +557,7 @@
     const std::string& parameter,
     GeneratorContext* generator_context,
     std::string* error) const {
-
-  if (file->syntax() != FileDescriptor::SYNTAX_PROTO3 &&
-      file->syntax() != FileDescriptor::SYNTAX_PROTO2) {
+  if (file->syntax() == FileDescriptor::SYNTAX_UNKNOWN) {
     *error = "Invalid or unsupported proto syntax";
     return false;
   }
diff --git a/src/google/protobuf/compiler/rust/BUILD.bazel b/src/google/protobuf/compiler/rust/BUILD.bazel
index 2a26dd5..faddffc 100644
--- a/src/google/protobuf/compiler/rust/BUILD.bazel
+++ b/src/google/protobuf/compiler/rust/BUILD.bazel
@@ -7,6 +7,7 @@
 
 cc_library(
     name = "rust",
+    srcs = ["generator.cc"],
     hdrs = ["generator.h"],
     copts = COPTS,
     include_prefix = "google/protobuf/compiler/rust",
diff --git a/src/google/protobuf/compiler/rust/generator.cc b/src/google/protobuf/compiler/rust/generator.cc
new file mode 100644
index 0000000..08a14eb
--- /dev/null
+++ b/src/google/protobuf/compiler/rust/generator.cc
@@ -0,0 +1,123 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "google/protobuf/compiler/rust/generator.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/algorithm/container.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_replace.h"
+#include "absl/strings/string_view.h"
+#include "google/protobuf/descriptor.h"
+#include "google/protobuf/io/printer.h"
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace rust {
+
+bool ExperimentalRustGeneratorEnabled(
+    const std::vector<std::pair<std::string, std::string>>& options) {
+  static constexpr std::pair<absl::string_view, absl::string_view> kMagicValue =
+      {"experimental-codegen", "enabled"};
+
+  return absl::c_any_of(
+      options, [](std::pair<absl::string_view, absl::string_view> pair) {
+        return pair == kMagicValue;
+      });
+}
+
+std::string get_crate_name(const FileDescriptor* dependency) {
+  absl::string_view path = dependency->name();
+  auto basename = path.substr(path.rfind('/') + 1);
+  return absl::StrReplaceAll(basename, {
+                                           {".", "_"},
+                                           {"-", "_"},
+                                       });
+}
+
+bool RustGenerator::Generate(const FileDescriptor* file,
+                             const std::string& parameter,
+                             GeneratorContext* generator_context,
+                             std::string* error) const {
+  std::vector<std::pair<std::string, std::string>> options;
+  ParseGeneratorParameter(parameter, &options);
+
+  if (!ExperimentalRustGeneratorEnabled(options)) {
+    *error =
+        "The Rust codegen is highly experimental. Future versions will break "
+        "existing code. Use at your own risk. You can opt-in by passing "
+        "'experimental-codegen=enabled' to '--rust_out'.";
+    return false;
+  }
+
+  auto basename = StripProto(file->name());
+  auto outfile = absl::WrapUnique(
+      generator_context->Open(absl::StrCat(basename, ".pb.rs")));
+
+  google::protobuf::io::Printer p(outfile.get());
+  // TODO(b/270138878): Remove `do_nothing` import once we have real logic. This
+  // is there only to smoke test rustc actions in rust_proto_library.
+  p.Emit(R"rs(
+#[allow(unused_imports)]
+    use protobuf::do_nothing;
+  )rs");
+  for (int i = 0; i < file->message_type_count(); ++i) {
+    // TODO(b/270138878): Implement real logic
+    p.Emit({{"Msg", file->message_type(i)->name()}}, R"rs(
+                    pub struct $Msg$ {}
+                  )rs");
+  }
+  // TODO(b/270124215): Delete the following "placeholder impl" of `import
+  // public`. Also make sure to figure out how to map FileDescriptor#name to
+  // Rust crate names (currently Bazel labels).
+  for (int i = 0; i < file->public_dependency_count(); ++i) {
+    const FileDescriptor* dep = file->public_dependency(i);
+    std::string crate_name = get_crate_name(dep);
+    for (int j = 0; j < dep->message_type_count(); ++j) {
+      // TODO(b/270138878): Implement real logic
+      p.Emit(
+          {{"crate", crate_name}, {"type_name", dep->message_type(j)->name()}},
+          R"rs(
+                pub use $crate$::$type_name$;
+              )rs");
+    }
+  }
+
+  return true;
+}
+
+}  // namespace rust
+}  // namespace compiler
+}  // namespace protobuf
+}  // namespace google
diff --git a/src/google/protobuf/compiler/rust/generator.h b/src/google/protobuf/compiler/rust/generator.h
index a01c6ae..5b3d0c4 100644
--- a/src/google/protobuf/compiler/rust/generator.h
+++ b/src/google/protobuf/compiler/rust/generator.h
@@ -33,7 +33,6 @@
 
 #include <string>
 
-#include "absl/log/absl_check.h"
 #include "google/protobuf/compiler/code_generator.h"
 
 // Must be included last.
@@ -44,7 +43,8 @@
 namespace compiler {
 namespace rust {
 
-class RustGenerator final : public google::protobuf::compiler::CodeGenerator {
+class PROTOC_EXPORT RustGenerator final
+    : public google::protobuf::compiler::CodeGenerator {
  public:
   RustGenerator() = default;
   RustGenerator(const RustGenerator&) = delete;
@@ -53,10 +53,7 @@
 
   bool Generate(const FileDescriptor* file, const std::string& parameter,
                 GeneratorContext* generator_context,
-                std::string* error) const override {
-    ABSL_CHECK(false) << "not yet implemented";
-    return false;
-  }
+                std::string* error) const override;
 
   uint64_t GetSupportedFeatures() const override {
     return FEATURE_PROTO3_OPTIONAL;
diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc
index 9e3a49d..efcd86e 100644
--- a/src/google/protobuf/descriptor.cc
+++ b/src/google/protobuf/descriptor.cc
@@ -5375,6 +5375,7 @@
 
 }  // namespace
 
+
 void DescriptorBuilder::BuildMessage(const DescriptorProto& proto,
                                      const Descriptor* parent,
                                      Descriptor* result,
diff --git a/src/google/protobuf/descriptor.h b/src/google/protobuf/descriptor.h
index f6359e8..f1d2032 100644
--- a/src/google/protobuf/descriptor.h
+++ b/src/google/protobuf/descriptor.h
@@ -65,6 +65,7 @@
 #include "google/protobuf/port.h"
 #include "absl/base/attributes.h"
 #include "absl/base/call_once.h"
+#include "absl/container/btree_map.h"
 #include "absl/container/flat_hash_map.h"
 #include "absl/log/absl_check.h"
 #include "absl/log/absl_log.h"
@@ -84,6 +85,7 @@
 namespace google {
 namespace protobuf {
 
+
 // Defined in this file.
 class Descriptor;
 class FieldDescriptor;
@@ -117,7 +119,6 @@
 class FileOptions;
 class UninterpretedOption;
 class SourceCodeInfo;
-class ExtensionMetadata;
 
 // Defined in message.h
 class Message;
@@ -768,6 +769,25 @@
   // parse.
   bool requires_utf8_validation() const;
 
+  // Determines if the given enum field is treated as closed based on legacy
+  // non-conformant behavior.
+  //
+  // Conformant behavior determines closedness based on the enum and
+  // can be queried using EnumDescriptor::is_closed().
+  //
+  // Some runtimes currently have a quirk where non-closed enums are
+  // treated as closed when used as the type of fields defined in a
+  // `syntax = proto2;` file. This quirk is not present in all runtimes; as of
+  // writing, we know that:
+  //
+  // - C++, Java, and C++-based Python share this quirk.
+  // - UPB and UPB-based Python do not.
+  // - PHP and Ruby treat all enums as open regardless of declaration.
+  //
+  // Care should be taken when using this function to respect the target
+  // runtime's enum handling quirks.
+  bool legacy_enum_field_treated_as_closed() const;
+
   // Index of this field within the message's field array, or the file or
   // extension scope's extensions array.
   int index() const;
@@ -1902,7 +1922,7 @@
     // in a .proto file.
     enum ErrorLocation {
       NAME,           // the symbol name, or the package name for files
-      NUMBER,         // field or extension range number
+      NUMBER,         // field, extension range or extension decl number
       TYPE,           // field type
       EXTENDEE,       // field extendee
       DEFAULT_VALUE,  // field default value
@@ -2442,6 +2462,11 @@
          file()->syntax() == FileDescriptor::SYNTAX_PROTO3;
 }
 
+inline bool FieldDescriptor::legacy_enum_field_treated_as_closed() const {
+  return type() == TYPE_ENUM &&
+         file()->syntax() == FileDescriptor::SYNTAX_PROTO2;
+}
+
 // To save space, index() is computed by looking at the descriptor's position
 // in the parent's array of children.
 inline int FieldDescriptor::index() const {
diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc
index ce8901e..88e9833 100644
--- a/src/google/protobuf/descriptor.pb.cc
+++ b/src/google/protobuf/descriptor.pb.cc
@@ -4252,7 +4252,7 @@
       // optional .google.protobuf.FieldDescriptorProto.Label label = 4;
       case 4:
         if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 32)) {
-          ::uint32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          ::int32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
           CHK_(ptr);
           if (PROTOBUF_PREDICT_TRUE(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Label_IsValid(static_cast<int>(val)))) {
             _internal_set_label(static_cast<::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Label>(val));
@@ -4266,7 +4266,7 @@
       // optional .google.protobuf.FieldDescriptorProto.Type type = 5;
       case 5:
         if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 40)) {
-          ::uint32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          ::int32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
           CHK_(ptr);
           if (PROTOBUF_PREDICT_TRUE(::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Type_IsValid(static_cast<int>(val)))) {
             _internal_set_type(static_cast<::PROTOBUF_NAMESPACE_ID::FieldDescriptorProto_Type>(val));
@@ -7026,7 +7026,7 @@
       // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED];
       case 9:
         if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 72)) {
-          ::uint32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          ::int32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
           CHK_(ptr);
           if (PROTOBUF_PREDICT_TRUE(::PROTOBUF_NAMESPACE_ID::FileOptions_OptimizeMode_IsValid(static_cast<int>(val)))) {
             _internal_set_optimize_for(static_cast<::PROTOBUF_NAMESPACE_ID::FileOptions_OptimizeMode>(val));
@@ -8297,7 +8297,7 @@
       // optional .google.protobuf.FieldOptions.CType ctype = 1 [default = STRING];
       case 1:
         if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 8)) {
-          ::uint32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          ::int32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
           CHK_(ptr);
           if (PROTOBUF_PREDICT_TRUE(::PROTOBUF_NAMESPACE_ID::FieldOptions_CType_IsValid(static_cast<int>(val)))) {
             _internal_set_ctype(static_cast<::PROTOBUF_NAMESPACE_ID::FieldOptions_CType>(val));
@@ -8341,7 +8341,7 @@
       // optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL];
       case 6:
         if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 48)) {
-          ::uint32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          ::int32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
           CHK_(ptr);
           if (PROTOBUF_PREDICT_TRUE(::PROTOBUF_NAMESPACE_ID::FieldOptions_JSType_IsValid(static_cast<int>(val)))) {
             _internal_set_jstype(static_cast<::PROTOBUF_NAMESPACE_ID::FieldOptions_JSType>(val));
@@ -8385,7 +8385,7 @@
       // optional .google.protobuf.FieldOptions.OptionRetention retention = 17;
       case 17:
         if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 136)) {
-          ::uint32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          ::int32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
           CHK_(ptr);
           if (PROTOBUF_PREDICT_TRUE(::PROTOBUF_NAMESPACE_ID::FieldOptions_OptionRetention_IsValid(static_cast<int>(val)))) {
             _internal_set_retention(static_cast<::PROTOBUF_NAMESPACE_ID::FieldOptions_OptionRetention>(val));
@@ -8399,7 +8399,7 @@
       // optional .google.protobuf.FieldOptions.OptionTargetType target = 18;
       case 18:
         if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 144)) {
-          ::uint32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          ::int32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
           CHK_(ptr);
           if (PROTOBUF_PREDICT_TRUE(::PROTOBUF_NAMESPACE_ID::FieldOptions_OptionTargetType_IsValid(static_cast<int>(val)))) {
             _internal_set_target(static_cast<::PROTOBUF_NAMESPACE_ID::FieldOptions_OptionTargetType>(val));
@@ -9877,7 +9877,7 @@
       // optional .google.protobuf.MethodOptions.IdempotencyLevel idempotency_level = 34 [default = IDEMPOTENCY_UNKNOWN];
       case 34:
         if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 16)) {
-          ::uint32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          ::int32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
           CHK_(ptr);
           if (PROTOBUF_PREDICT_TRUE(::PROTOBUF_NAMESPACE_ID::MethodOptions_IdempotencyLevel_IsValid(static_cast<int>(val)))) {
             _internal_set_idempotency_level(static_cast<::PROTOBUF_NAMESPACE_ID::MethodOptions_IdempotencyLevel>(val));
@@ -11581,7 +11581,7 @@
       // optional .google.protobuf.GeneratedCodeInfo.Annotation.Semantic semantic = 5;
       case 5:
         if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 40)) {
-          ::uint32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          ::int32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
           CHK_(ptr);
           if (PROTOBUF_PREDICT_TRUE(::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation_Semantic_IsValid(static_cast<int>(val)))) {
             _internal_set_semantic(static_cast<::PROTOBUF_NAMESPACE_ID::GeneratedCodeInfo_Annotation_Semantic>(val));
diff --git a/src/google/protobuf/descriptor.pb.h b/src/google/protobuf/descriptor.pb.h
index ff15dcb..455021a 100644
--- a/src/google/protobuf/descriptor.pb.h
+++ b/src/google/protobuf/descriptor.pb.h
@@ -9,7 +9,7 @@
 #include <type_traits>
 
 #include "google/protobuf/port_def.inc"
-#if PROTOBUF_VERSION < 4022000
+#if PROTOBUF_VERSION < 3021000
 #error "This file was generated by a newer version of protoc which is"
 #error "incompatible with your Protocol Buffer headers. Please update"
 #error "your headers."
diff --git a/src/google/protobuf/descriptor_unittest.cc b/src/google/protobuf/descriptor_unittest.cc
index 80eebfe..c4c7e93 100644
--- a/src/google/protobuf/descriptor_unittest.cc
+++ b/src/google/protobuf/descriptor_unittest.cc
@@ -1120,6 +1120,66 @@
   EXPECT_FALSE(bar3->requires_utf8_validation());
 }
 
+TEST_F(DescriptorTest, EnumFieldTreatedAsClosed) {
+  // Make an open enum definition.
+  FileDescriptorProto open_enum_file;
+  open_enum_file.set_name("open_enum.proto");
+  open_enum_file.set_syntax("proto3");
+  AddEnumValue(AddEnum(&open_enum_file, "TestEnumOpen"), "TestEnumOpen_VALUE0",
+               0);
+
+  const EnumDescriptor* open_enum =
+      pool_.BuildFile(open_enum_file)->enum_type(0);
+  EXPECT_FALSE(open_enum->is_closed());
+
+  // Create a message that treats enum fields as closed.
+  FileDescriptorProto closed_file;
+  closed_file.set_name("closed_enum_field.proto");
+  closed_file.add_dependency("open_enum.proto");
+  closed_file.add_dependency("foo.proto");
+
+  DescriptorProto* message = AddMessage(&closed_file, "TestClosedEnumField");
+  AddField(message, "int_field", 1, FieldDescriptorProto::LABEL_OPTIONAL,
+           FieldDescriptorProto::TYPE_INT32);
+  AddField(message, "open_enum", 2, FieldDescriptorProto::LABEL_OPTIONAL,
+           FieldDescriptorProto::TYPE_ENUM)
+      ->set_type_name("TestEnumOpen");
+  AddField(message, "closed_enum", 3, FieldDescriptorProto::LABEL_OPTIONAL,
+           FieldDescriptorProto::TYPE_ENUM)
+      ->set_type_name("TestEnum");
+  const Descriptor* closed_message =
+      pool_.BuildFile(closed_file)->message_type(0);
+
+  EXPECT_FALSE(closed_message->FindFieldByName("int_field")
+                   ->legacy_enum_field_treated_as_closed());
+  EXPECT_TRUE(closed_message->FindFieldByName("closed_enum")
+                  ->legacy_enum_field_treated_as_closed());
+  EXPECT_TRUE(closed_message->FindFieldByName("open_enum")
+                  ->legacy_enum_field_treated_as_closed());
+}
+
+TEST_F(DescriptorTest, EnumFieldTreatedAsOpen) {
+  FileDescriptorProto open_enum_file;
+  open_enum_file.set_name("open_enum.proto");
+  open_enum_file.set_syntax("proto3");
+  AddEnumValue(AddEnum(&open_enum_file, "TestEnumOpen"), "TestEnumOpen_VALUE0",
+               0);
+  DescriptorProto* message = AddMessage(&open_enum_file, "TestOpenEnumField");
+  AddField(message, "int_field", 1, FieldDescriptorProto::LABEL_OPTIONAL,
+           FieldDescriptorProto::TYPE_INT32);
+  AddField(message, "open_enum", 2, FieldDescriptorProto::LABEL_OPTIONAL,
+           FieldDescriptorProto::TYPE_ENUM)
+      ->set_type_name("TestEnumOpen");
+  const FileDescriptor* open_enum_file_desc = pool_.BuildFile(open_enum_file);
+  const Descriptor* open_message = open_enum_file_desc->message_type(0);
+  const EnumDescriptor* open_enum = open_enum_file_desc->enum_type(0);
+  EXPECT_FALSE(open_enum->is_closed());
+  EXPECT_FALSE(open_message->FindFieldByName("int_field")
+                   ->legacy_enum_field_treated_as_closed());
+  EXPECT_FALSE(open_message->FindFieldByName("open_enum")
+                   ->legacy_enum_field_treated_as_closed());
+}
+
 TEST_F(DescriptorTest, IsMap) {
   EXPECT_TRUE(map_->is_map());
   EXPECT_FALSE(baz_->is_map());
@@ -3979,6 +4039,16 @@
     BuildFileInTestPool(DescriptorProto::descriptor()->file());
   }
 
+  void BuildDescriptorMessagesInTestPoolWithErrors(
+      absl::string_view expected_errors) {
+    FileDescriptorProto file_proto;
+    DescriptorProto::descriptor()->file()->CopyTo(&file_proto);
+    MockErrorCollector error_collector;
+    EXPECT_TRUE(pool_.BuildFileCollectingErrors(file_proto, &error_collector) ==
+                nullptr);
+    EXPECT_EQ(error_collector.text_, expected_errors);
+  }
+
   DescriptorPool pool_;
 };
 
diff --git a/src/google/protobuf/duration.pb.h b/src/google/protobuf/duration.pb.h
index 6609a9e..7121b53 100644
--- a/src/google/protobuf/duration.pb.h
+++ b/src/google/protobuf/duration.pb.h
@@ -9,7 +9,7 @@
 #include <type_traits>
 
 #include "google/protobuf/port_def.inc"
-#if PROTOBUF_VERSION < 4022000
+#if PROTOBUF_VERSION < 3021000
 #error "This file was generated by a newer version of protoc which is"
 #error "incompatible with your Protocol Buffer headers. Please update"
 #error "your headers."
@@ -96,6 +96,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
diff --git a/src/google/protobuf/empty.pb.h b/src/google/protobuf/empty.pb.h
index 16bacbf..c17b5ea 100644
--- a/src/google/protobuf/empty.pb.h
+++ b/src/google/protobuf/empty.pb.h
@@ -9,7 +9,7 @@
 #include <type_traits>
 
 #include "google/protobuf/port_def.inc"
-#if PROTOBUF_VERSION < 4022000
+#if PROTOBUF_VERSION < 3021000
 #error "This file was generated by a newer version of protoc which is"
 #error "incompatible with your Protocol Buffer headers. Please update"
 #error "your headers."
@@ -96,6 +96,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
diff --git a/src/google/protobuf/extension_set.h b/src/google/protobuf/extension_set.h
index d5ef61a..2178b1e 100644
--- a/src/google/protobuf/extension_set.h
+++ b/src/google/protobuf/extension_set.h
@@ -62,6 +62,7 @@
 #error "You cannot SWIG proto headers"
 #endif
 
+
 namespace google {
 namespace protobuf {
 class Arena;
@@ -75,6 +76,7 @@
 class UnknownFieldSet;  // unknown_field_set.h
 namespace internal {
 class FieldSkipper;  // wire_format_lite.h
+class WireFormat;
 enum class LazyVerifyOption;
 }  // namespace internal
 }  // namespace protobuf
@@ -514,6 +516,7 @@
   friend class RepeatedEnumTypeTraits;
 
   friend class google::protobuf::Reflection;
+  friend class google::protobuf::internal::WireFormat;
 
   const int32_t& GetRefInt32(int number, const int32_t& default_value) const;
   const int64_t& GetRefInt64(int number, const int64_t& default_value) const;
@@ -560,6 +563,8 @@
 
     virtual bool IsInitialized(const MessageLite* prototype,
                                Arena* arena) const = 0;
+    virtual bool IsEagerSerializeSafe(const MessageLite* prototype,
+                                      Arena* arena) const = 0;
 
     PROTOBUF_DEPRECATED_MSG("Please use ByteSizeLong() instead")
     virtual int ByteSize() const { return internal::ToIntSize(ByteSizeLong()); }
@@ -571,9 +576,9 @@
     virtual void MergeFromMessage(const MessageLite& msg, Arena* arena) = 0;
     virtual void Clear() = 0;
 
-    virtual const char* _InternalParse(const Message& prototype, Arena* arena,
-                                       LazyVerifyOption option, const char* ptr,
-                                       ParseContext* ctx) = 0;
+    virtual const char* _InternalParse(const MessageLite& prototype,
+                                       Arena* arena, LazyVerifyOption option,
+                                       const char* ptr, ParseContext* ctx) = 0;
     virtual uint8_t* WriteMessageToArray(
         const MessageLite* prototype, int number, uint8_t* target,
         io::EpsCopyOutputStream* stream) const = 0;
diff --git a/src/google/protobuf/extension_set_heavy.cc b/src/google/protobuf/extension_set_heavy.cc
index cdc4cc3..27b5dd6 100644
--- a/src/google/protobuf/extension_set_heavy.cc
+++ b/src/google/protobuf/extension_set_heavy.cc
@@ -285,7 +285,7 @@
   } else {
     output->type = extension->type();
     output->is_repeated = extension->is_repeated();
-    output->is_packed = extension->options().packed();
+    output->is_packed = extension->is_packed();
     output->descriptor = extension;
     if (extension->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
       output->message_info.prototype =
diff --git a/src/google/protobuf/extension_set_inl.h b/src/google/protobuf/extension_set_inl.h
index bd01e95..4554d95 100644
--- a/src/google/protobuf/extension_set_inl.h
+++ b/src/google/protobuf/extension_set_inl.h
@@ -141,14 +141,14 @@
 #undef HANDLE_FIXED_TYPE
 
       case WireFormatLite::TYPE_ENUM: {
-        uint64_t val;
-        ptr = VarintParse(ptr, &val);
+        uint64_t tmp;
+        ptr = VarintParse(ptr, &tmp);
         GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
-        int value = val;
+        int value = tmp;
 
         if (!extension.enum_validity_check.func(
                 extension.enum_validity_check.arg, value)) {
-          WriteVarint(number, val, metadata->mutable_unknown_fields<T>());
+          WriteVarint(number, value, metadata->mutable_unknown_fields<T>());
         } else if (extension.is_repeated) {
           AddEnum(number, WireFormatLite::TYPE_ENUM, extension.is_packed, value,
                   extension.descriptor);
diff --git a/src/google/protobuf/extension_set_unittest.cc b/src/google/protobuf/extension_set_unittest.cc
index ccd1c9b..7f6799f 100644
--- a/src/google/protobuf/extension_set_unittest.cc
+++ b/src/google/protobuf/extension_set_unittest.cc
@@ -57,7 +57,6 @@
 // Must be included last.
 #include "google/protobuf/port_def.inc"
 
-
 namespace google {
 namespace protobuf {
 namespace internal {
@@ -1359,6 +1358,54 @@
   }
 }
 
+TEST(ExtensionSetTest, Proto3PackedDynamicExtensions) {
+  // Regression test for b/271121265. This test case verifies that
+  // packed-by-default repeated custom options in proto3 are correctly
+  // serialized in packed form when dynamic extensions are used.
+
+  // Create a custom option in proto3 and load this into an overlay
+  // DescriptorPool with a DynamicMessageFactory.
+  google::protobuf::FileDescriptorProto file_descriptor_proto;
+  file_descriptor_proto.set_syntax("proto3");
+  file_descriptor_proto.set_name(
+      "third_party/protobuf/unittest_proto3_packed_extension.proto");
+  file_descriptor_proto.set_package("proto3_unittest");
+  file_descriptor_proto.add_dependency(
+      DescriptorProto::descriptor()->file()->name());
+  FieldDescriptorProto* extension = file_descriptor_proto.add_extension();
+  extension->set_name("repeated_int32_option");
+  extension->set_extendee(MessageOptions().GetTypeName());
+  extension->set_number(50009);
+  extension->set_label(FieldDescriptorProto::LABEL_REPEATED);
+  extension->set_type(FieldDescriptorProto::TYPE_INT32);
+  extension->set_json_name("repeatedInt32Option");
+  google::protobuf::DescriptorPool pool(DescriptorPool::generated_pool());
+  ASSERT_NE(pool.BuildFile(file_descriptor_proto), nullptr);
+  DynamicMessageFactory factory;
+  factory.SetDelegateToGeneratedFactory(true);
+
+  // Create a serialized MessageOptions proto equivalent to:
+  // [proto3_unittest.repeated_int32_option]: 1
+  UnknownFieldSet unknown_fields;
+  unknown_fields.AddVarint(50009, 1);
+  std::string serialized_options;
+  ASSERT_TRUE(unknown_fields.SerializeToString(&serialized_options));
+
+  // Parse the MessageOptions using our custom extension registry.
+  io::ArrayInputStream input_stream(serialized_options.data(),
+                                    serialized_options.size());
+  io::CodedInputStream coded_stream(&input_stream);
+  coded_stream.SetExtensionRegistry(&pool, &factory);
+  MessageOptions message_options;
+  ASSERT_TRUE(message_options.ParseFromCodedStream(&coded_stream));
+
+  // Finally, serialize the proto again and verify that the repeated option has
+  // been correctly serialized in packed form.
+  std::string reserialized_options;
+  ASSERT_TRUE(message_options.SerializeToString(&reserialized_options));
+  EXPECT_EQ(reserialized_options, "\xca\xb5\x18\x01\x01");
+}
+
 TEST(ExtensionSetTest, BoolExtension) {
   unittest::TestAllExtensions msg;
   uint8_t wire_bytes[2] = {13 * 8, 42 /* out of bounds payload for bool */};
diff --git a/src/google/protobuf/field_mask.pb.h b/src/google/protobuf/field_mask.pb.h
index 7b7833e..ddb95f5 100644
--- a/src/google/protobuf/field_mask.pb.h
+++ b/src/google/protobuf/field_mask.pb.h
@@ -9,7 +9,7 @@
 #include <type_traits>
 
 #include "google/protobuf/port_def.inc"
-#if PROTOBUF_VERSION < 4022000
+#if PROTOBUF_VERSION < 3021000
 #error "This file was generated by a newer version of protoc which is"
 #error "incompatible with your Protocol Buffer headers. Please update"
 #error "your headers."
@@ -96,6 +96,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
diff --git a/src/google/protobuf/generated_message_reflection.cc b/src/google/protobuf/generated_message_reflection.cc
index 80a3eb5..6fab214 100644
--- a/src/google/protobuf/generated_message_reflection.cc
+++ b/src/google/protobuf/generated_message_reflection.cc
@@ -1540,10 +1540,6 @@
           static_cast<uint32_t>(1)) != 0;
 }
 
-bool CreateUnknownEnumValues(const FileDescriptor* file) {
-  return file->syntax() == FileDescriptor::SYNTAX_PROTO3;
-}
-
 void CheckInOrder(const FieldDescriptor* field, uint32_t* last) {
   *last = *last <= static_cast<uint32_t>(field->number())
               ? static_cast<uint32_t>(field->number())
@@ -1760,6 +1756,7 @@
 }
 
 
+
 void Reflection::SetString(Message* message, const FieldDescriptor* field,
                            std::string value) const {
   USAGE_CHECK_ALL(SetString, SINGULAR, STRING);
@@ -2485,7 +2482,7 @@
 }
 
 bool Reflection::SupportsUnknownEnumValues() const {
-  return CreateUnknownEnumValues(descriptor_->file());
+  return descriptor_->file()->syntax() == FileDescriptor::SYNTAX_PROTO3;
 }
 
 // ===================================================================
@@ -2957,124 +2954,16 @@
 }
 
 static internal::TailCallParseFunc GetFastParseFunction(
-    absl::string_view name) {
-  // This list must be synchronized with TcParser.
-  // Missing entries are replaced with MiniParse in opt mode to avoid runtime
-  // failures. It check-fails in debug mode.
+    const internal::TailCallTableInfo::FastFieldInfo& field_info) {
+#define PROTOBUF_TC_PARSE_FUNCTION_X(value) \
+  {"::_pbi::TcParser::" #value, internal::TcParser::value},
   static const auto* const map =
       new absl::flat_hash_map<absl::string_view, internal::TailCallParseFunc>{
-          {"::_pbi::TcParser::FastF32S1", internal::TcParser::FastF32S1},
-          {"::_pbi::TcParser::FastF32S2", internal::TcParser::FastF32S2},
-          {"::_pbi::TcParser::FastF32R1", internal::TcParser::FastF32R1},
-          {"::_pbi::TcParser::FastF32R2", internal::TcParser::FastF32R2},
-          {"::_pbi::TcParser::FastF32P1", internal::TcParser::FastF32P1},
-          {"::_pbi::TcParser::FastF32P2", internal::TcParser::FastF32P2},
-          {"::_pbi::TcParser::FastF64S1", internal::TcParser::FastF64S1},
-          {"::_pbi::TcParser::FastF64S2", internal::TcParser::FastF64S2},
-          {"::_pbi::TcParser::FastF64R1", internal::TcParser::FastF64R1},
-          {"::_pbi::TcParser::FastF64R2", internal::TcParser::FastF64R2},
-          {"::_pbi::TcParser::FastF64P1", internal::TcParser::FastF64P1},
-          {"::_pbi::TcParser::FastF64P2", internal::TcParser::FastF64P2},
-          {"::_pbi::TcParser::FastV8S1", internal::TcParser::FastV8S1},
-          {"::_pbi::TcParser::FastV8S2", internal::TcParser::FastV8S2},
-          {"::_pbi::TcParser::FastV8R1", internal::TcParser::FastV8R1},
-          {"::_pbi::TcParser::FastV8R2", internal::TcParser::FastV8R2},
-          {"::_pbi::TcParser::FastV8P1", internal::TcParser::FastV8P1},
-          {"::_pbi::TcParser::FastV8P2", internal::TcParser::FastV8P2},
-          {"::_pbi::TcParser::FastV32S1", internal::TcParser::FastV32S1},
-          {"::_pbi::TcParser::FastV32S2", internal::TcParser::FastV32S2},
-          {"::_pbi::TcParser::FastV32R1", internal::TcParser::FastV32R1},
-          {"::_pbi::TcParser::FastV32R2", internal::TcParser::FastV32R2},
-          {"::_pbi::TcParser::FastV32P1", internal::TcParser::FastV32P1},
-          {"::_pbi::TcParser::FastV32P2", internal::TcParser::FastV32P2},
-          {"::_pbi::TcParser::FastV64S1", internal::TcParser::FastV64S1},
-          {"::_pbi::TcParser::FastV64S2", internal::TcParser::FastV64S2},
-          {"::_pbi::TcParser::FastV64R1", internal::TcParser::FastV64R1},
-          {"::_pbi::TcParser::FastV64R2", internal::TcParser::FastV64R2},
-          {"::_pbi::TcParser::FastV64P1", internal::TcParser::FastV64P1},
-          {"::_pbi::TcParser::FastV64P2", internal::TcParser::FastV64P2},
-          {"::_pbi::TcParser::FastZ32S1", internal::TcParser::FastZ32S1},
-          {"::_pbi::TcParser::FastZ32S2", internal::TcParser::FastZ32S2},
-          {"::_pbi::TcParser::FastZ32R1", internal::TcParser::FastZ32R1},
-          {"::_pbi::TcParser::FastZ32R2", internal::TcParser::FastZ32R2},
-          {"::_pbi::TcParser::FastZ32P1", internal::TcParser::FastZ32P1},
-          {"::_pbi::TcParser::FastZ32P2", internal::TcParser::FastZ32P2},
-          {"::_pbi::TcParser::FastZ64S1", internal::TcParser::FastZ64S1},
-          {"::_pbi::TcParser::FastZ64S2", internal::TcParser::FastZ64S2},
-          {"::_pbi::TcParser::FastZ64R1", internal::TcParser::FastZ64R1},
-          {"::_pbi::TcParser::FastZ64R2", internal::TcParser::FastZ64R2},
-          {"::_pbi::TcParser::FastZ64P1", internal::TcParser::FastZ64P1},
-          {"::_pbi::TcParser::FastZ64P2", internal::TcParser::FastZ64P2},
-          {"::_pbi::TcParser::FastErS1", internal::TcParser::FastErS1},
-          {"::_pbi::TcParser::FastErS2", internal::TcParser::FastErS2},
-          {"::_pbi::TcParser::FastErR1", internal::TcParser::FastErR1},
-          {"::_pbi::TcParser::FastErR2", internal::TcParser::FastErR2},
-          {"::_pbi::TcParser::FastErP1", internal::TcParser::FastErP1},
-          {"::_pbi::TcParser::FastErP2", internal::TcParser::FastErP2},
-          {"::_pbi::TcParser::FastEr0S1", internal::TcParser::FastEr0S1},
-          {"::_pbi::TcParser::FastEr0S2", internal::TcParser::FastEr0S2},
-          {"::_pbi::TcParser::FastEr0R1", internal::TcParser::FastEr0R1},
-          {"::_pbi::TcParser::FastEr0R2", internal::TcParser::FastEr0R2},
-          {"::_pbi::TcParser::FastEr0P1", internal::TcParser::FastEr0P1},
-          {"::_pbi::TcParser::FastEr0P2", internal::TcParser::FastEr0P2},
-          {"::_pbi::TcParser::FastEr1S1", internal::TcParser::FastEr1S1},
-          {"::_pbi::TcParser::FastEr1S2", internal::TcParser::FastEr1S2},
-          {"::_pbi::TcParser::FastEr1R1", internal::TcParser::FastEr1R1},
-          {"::_pbi::TcParser::FastEr1R2", internal::TcParser::FastEr1R2},
-          {"::_pbi::TcParser::FastEr1P1", internal::TcParser::FastEr1P1},
-          {"::_pbi::TcParser::FastEr1P2", internal::TcParser::FastEr1P2},
-          {"::_pbi::TcParser::FastEvS1", internal::TcParser::FastEvS1},
-          {"::_pbi::TcParser::FastEvS2", internal::TcParser::FastEvS2},
-          {"::_pbi::TcParser::FastEvR1", internal::TcParser::FastEvR1},
-          {"::_pbi::TcParser::FastEvR2", internal::TcParser::FastEvR2},
-          {"::_pbi::TcParser::FastEvP1", internal::TcParser::FastEvP1},
-          {"::_pbi::TcParser::FastEvP2", internal::TcParser::FastEvP2},
-          {"::_pbi::TcParser::FastBS1", internal::TcParser::FastBS1},
-          {"::_pbi::TcParser::FastBS2", internal::TcParser::FastBS2},
-          {"::_pbi::TcParser::FastBR1", internal::TcParser::FastBR1},
-          {"::_pbi::TcParser::FastBR2", internal::TcParser::FastBR2},
-          {"::_pbi::TcParser::FastSS1", internal::TcParser::FastSS1},
-          {"::_pbi::TcParser::FastSS2", internal::TcParser::FastSS2},
-          {"::_pbi::TcParser::FastSR1", internal::TcParser::FastSR1},
-          {"::_pbi::TcParser::FastSR2", internal::TcParser::FastSR2},
-          {"::_pbi::TcParser::FastUS1", internal::TcParser::FastUS1},
-          {"::_pbi::TcParser::FastUS2", internal::TcParser::FastUS2},
-          {"::_pbi::TcParser::FastUR1", internal::TcParser::FastUR1},
-          {"::_pbi::TcParser::FastUR2", internal::TcParser::FastUR2},
-          {"::_pbi::TcParser::FastBiS1", internal::TcParser::FastBiS1},
-          {"::_pbi::TcParser::FastBiS2", internal::TcParser::FastBiS2},
-          {"::_pbi::TcParser::FastSiS1", internal::TcParser::FastSiS1},
-          {"::_pbi::TcParser::FastSiS2", internal::TcParser::FastSiS2},
-          {"::_pbi::TcParser::FastUiS1", internal::TcParser::FastUiS1},
-          {"::_pbi::TcParser::FastUiS2", internal::TcParser::FastUiS2},
-          {"::_pbi::TcParser::FastBcS1", internal::TcParser::FastBcS1},
-          {"::_pbi::TcParser::FastBcS2", internal::TcParser::FastBcS2},
-          {"::_pbi::TcParser::FastScS1", internal::TcParser::FastScS1},
-          {"::_pbi::TcParser::FastScS2", internal::TcParser::FastScS2},
-          {"::_pbi::TcParser::FastUcS1", internal::TcParser::FastUcS1},
-          {"::_pbi::TcParser::FastUcS2", internal::TcParser::FastUcS2},
-          {"::_pbi::TcParser::FastMdS1", internal::TcParser::FastMdS1},
-          {"::_pbi::TcParser::FastMdS2", internal::TcParser::FastMdS2},
-          {"::_pbi::TcParser::FastGdS1", internal::TcParser::FastGdS1},
-          {"::_pbi::TcParser::FastGdS2", internal::TcParser::FastGdS2},
-          {"::_pbi::TcParser::FastMtS1", internal::TcParser::FastMtS1},
-          {"::_pbi::TcParser::FastMtS2", internal::TcParser::FastMtS2},
-          {"::_pbi::TcParser::FastGtS1", internal::TcParser::FastGtS1},
-          {"::_pbi::TcParser::FastGtS2", internal::TcParser::FastGtS2},
-          {"::_pbi::TcParser::FastMdR1", internal::TcParser::FastMdR1},
-          {"::_pbi::TcParser::FastMdR2", internal::TcParser::FastMdR2},
-          {"::_pbi::TcParser::FastGdR1", internal::TcParser::FastGdR1},
-          {"::_pbi::TcParser::FastGdR2", internal::TcParser::FastGdR2},
-          {"::_pbi::TcParser::FastMtR1", internal::TcParser::FastMtR1},
-          {"::_pbi::TcParser::FastMtR2", internal::TcParser::FastMtR2},
-          {"::_pbi::TcParser::FastGtR1", internal::TcParser::FastGtR1},
-          {"::_pbi::TcParser::FastGtR2", internal::TcParser::FastGtR2},
-          {"::_pbi::TcParser::FastEndG1", internal::TcParser::FastEndG1},
-          {"::_pbi::TcParser::FastEndG2", internal::TcParser::FastEndG2},
-      };
-  auto it = map->find(name);
+          PROTOBUF_TC_PARSE_FUNCTION_LIST};
+#undef PROTOBUF_TC_PARSE_FUNCTION_X
+  auto it = map->find(field_info.func_name);
   if (it == map->end()) {
-    ABSL_DLOG(FATAL) << "Failed to find function: " << name;
+    ABSL_DLOG(FATAL) << "Failed to find function: " << field_info.func_name;
     // Let's not crash in opt, just in case.
     // MiniParse is always a valid parser.
     return &internal::TcParser::MiniParse;
@@ -3092,9 +2981,9 @@
   // We use `operator new` here because the destruction will be done with
   // `operator delete` unconditionally.
   void* p = ::operator new(sizeof(Table));
-  auto* full_table = ::new (p) Table{
-      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, schema_.default_instance_, nullptr},
-      {{{&internal::TcParser::ReflectionParseLoop, {}}}}};
+  auto* full_table = ::new (p)
+      Table{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, schema_.default_instance_, nullptr},
+            {{{&internal::TcParser::ReflectionParseLoop, {}}}}};
   ABSL_DCHECK_EQ(static_cast<void*>(&full_table->header),
                  static_cast<void*>(full_table));
   return &full_table->header;
@@ -3110,7 +2999,7 @@
         *fast_entries++ = {internal::TcParser::MiniParse, {}};
       } else {
         // No field, but still a special entry.
-        *fast_entries++ = {GetFastParseFunction(fast_field.func_name),
+        *fast_entries++ = {GetFastParseFunction(fast_field),
                            {fast_field.coded_tag, fast_field.nonfield_info}};
       }
     } else if (fast_field.func_name.find("TcParser::FastEv") !=
@@ -3121,7 +3010,7 @@
       *fast_entries++ = {internal::TcParser::MiniParse, {}};
     } else {
       *fast_entries++ = {
-          GetFastParseFunction(fast_field.func_name),
+          GetFastParseFunction(fast_field),
           {fast_field.coded_tag, fast_field.hasbit_idx, fast_field.aux_idx,
            static_cast<uint16_t>(schema_.GetFieldOffset(fast_field.field))}};
     }
@@ -3197,8 +3086,16 @@
         break;
       case internal::TailCallTableInfo::kSubTable:
       case internal::TailCallTableInfo::kSubMessageWeak:
+      case internal::TailCallTableInfo::kCreateInArena:
         ABSL_LOG(FATAL) << "Not supported";
         break;
+      case internal::TailCallTableInfo::kMapAuxInfo:
+        // Default constructed info, which causes MpMap to call the fallback.
+        // DynamicMessage uses DynamicMapField, which uses variant keys and
+        // values. TcParser does not support them yet, so mark the field as
+        // unsupported to fallback to reflection.
+        field_aux++->map_info = internal::MapAuxInfo{};
+        break;
       case internal::TailCallTableInfo::kSubMessage:
         field_aux++->message_default_p =
             GetDefaultMessageInstance(aux_entry.field);
@@ -3251,18 +3148,21 @@
     explicit ReflectionOptionProvider(const Reflection& ref) : ref_(ref) {}
     internal::TailCallTableInfo::PerFieldOptions GetForField(
         const FieldDescriptor* field) const final {
-      return {ref_.IsLazyField(field),  //
-              ref_.IsInlined(field),    //
+      return {
+          ref_.IsLazyField(field),  //
+          ref_.IsInlined(field),    //
 
-              // Only LITE can be implicitly weak.
-              /* is_implicitly_weak */ false,
+          // Only LITE can be implicitly weak.
+          /* is_implicitly_weak */ false,
 
-              // We could change this to use direct table.
-              // Might be easier to do when all messages support TDP.
-              /* use_direct_tcparser_table */ false,
+          // We could change this to use direct table.
+          // Might be easier to do when all messages support TDP.
+          /* use_direct_tcparser_table */ false,
 
-              /* is_lite */ false,  //
-              ref_.schema_.IsSplit(field)};
+          /* is_lite */ false,          //
+          ref_.schema_.IsSplit(field),  //
+          /* uses_codegen */ false      //
+      };
     }
 
    private:
@@ -3292,14 +3192,19 @@
   void* p = ::operator new(byte_size);
   auto* res = ::new (p) TcParseTableBase{
       static_cast<uint16_t>(schema_.HasHasbits() ? schema_.HasBitsOffset() : 0),
-      // extensions handled through reflection.
-      0, 0, 0,
+      schema_.HasExtensionSet()
+          ? static_cast<uint16_t>(schema_.GetExtensionSetOffset())
+          : uint16_t{0},
       static_cast<uint32_t>(fields.empty() ? 0 : fields.back()->number()),
-      static_cast<uint8_t>((fast_entries_count - 1) << 3), lookup_table_offset,
-      table_info.num_to_entry_table.skipmap32, field_entry_offset,
+      static_cast<uint8_t>((fast_entries_count - 1) << 3),
+      lookup_table_offset,
+      table_info.num_to_entry_table.skipmap32,
+      field_entry_offset,
       static_cast<uint16_t>(fields.size()),
-      static_cast<uint16_t>(table_info.aux_entries.size()), aux_offset,
-      schema_.default_instance_, &internal::TcParser::ReflectionFallback};
+      static_cast<uint16_t>(table_info.aux_entries.size()),
+      aux_offset,
+      schema_.default_instance_,
+      &internal::TcParser::ReflectionFallback};
 
   // Now copy the rest of the payloads
   PopulateTcParseFastEntries(table_info, res->fast_entry(0));
diff --git a/src/google/protobuf/generated_message_reflection_unittest.cc b/src/google/protobuf/generated_message_reflection_unittest.cc
index f707cf5..3de242c 100644
--- a/src/google/protobuf/generated_message_reflection_unittest.cc
+++ b/src/google/protobuf/generated_message_reflection_unittest.cc
@@ -64,7 +64,6 @@
 // Must be included last.
 #include "google/protobuf/port_def.inc"
 
-
 namespace google {
 namespace protobuf {
 
diff --git a/src/google/protobuf/generated_message_tctable_decl.h b/src/google/protobuf/generated_message_tctable_decl.h
index dd4845b..f9affbe 100644
--- a/src/google/protobuf/generated_message_tctable_decl.h
+++ b/src/google/protobuf/generated_message_tctable_decl.h
@@ -54,6 +54,7 @@
 // Additional information about this field:
 struct TcFieldData {
   constexpr TcFieldData() : data(0) {}
+  explicit constexpr TcFieldData(uint64_t data) : data(data) {}
 
   // Fast table entry constructor:
   constexpr TcFieldData(uint16_t coded_tag, uint8_t hasbit_idx, uint8_t aux_idx,
@@ -63,6 +64,24 @@
              uint64_t{hasbit_idx} << 16 |  //
              uint64_t{coded_tag}) {}
 
+  // Constructor to create an explicit 'uninitialized' instance.
+  // This constructor can be used to pass an uninitialized `data` value to a
+  // table driven parser function that does not use `data`. The purpose of this
+  // is that it allows the compiler to reallocate and re-purpose the register
+  // that is currently holding its value for other data. This reduces register
+  // allocations inside the highly optimized varint parsing functions.
+  //
+  // Applications not using `data` use the `PROTOBUF_TC_PARAM_NO_DATA_DECL`
+  // macro to declare the standard input arguments with no name for the `data`
+  // argument. Callers then use the `PROTOBUF_TC_PARAM_NO_DATA_PASS` macro.
+  //
+  // Example:
+  //   if (ptr == nullptr) {
+  //      PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+  //   }
+  struct DefaultInit {};
+  TcFieldData(DefaultInit) {}  // NOLINT(google-explicit-constructor)
+
   // Fields used in fast table parsing:
   //
   //     Bit:
@@ -122,7 +141,9 @@
   uint32_t tag() const { return static_cast<uint32_t>(data); }
   uint32_t entry_offset() const { return static_cast<uint32_t>(data >> 32); }
 
-  uint64_t data;
+  union {
+    uint64_t data;
+  };
 };
 
 struct TcParseTableBase;
@@ -144,13 +165,119 @@
 
 struct FieldAuxDefaultMessage {};
 
+// Small type card used by mini parse to handle map entries.
+// Map key/values are very limited, so we can encode the whole thing in a single
+// byte.
+class MapTypeCard {
+ public:
+  enum CppType { kBool, k32, k64, kString, kMessage };
+  MapTypeCard() = default;
+  constexpr MapTypeCard(WireFormatLite::WireType wiretype, CppType cpp_type,
+                        bool is_zigzag_utf8)
+      : data_(
+            static_cast<uint8_t>((static_cast<uint8_t>(wiretype) << 0) |
+                                 (static_cast<uint8_t>(cpp_type) << 3) |
+                                 (static_cast<uint8_t>(is_zigzag_utf8) << 6))) {
+  }
+
+  WireFormatLite::WireType wiretype() const {
+    return static_cast<WireFormatLite::WireType>((data_ >> 0) & 0x7);
+  }
+
+  CppType cpp_type() const { return static_cast<CppType>((data_ >> 3) & 0x7); }
+
+  bool is_zigzag() const {
+    ABSL_DCHECK(wiretype() == WireFormatLite::WIRETYPE_VARINT);
+    ABSL_DCHECK(cpp_type() == CppType::k32 || cpp_type() == CppType::k64);
+    return is_zigzag_utf8();
+  }
+  bool is_utf8() const {
+    ABSL_DCHECK(wiretype() == WireFormatLite::WIRETYPE_LENGTH_DELIMITED);
+    ABSL_DCHECK(cpp_type() == CppType::kString);
+    return is_zigzag_utf8();
+  }
+
+ private:
+  bool is_zigzag_utf8() const { return static_cast<bool>((data_ >> 6) & 0x1); }
+  uint8_t data_;
+};
+static_assert(sizeof(MapTypeCard) == sizeof(uint8_t), "");
+
+// Make the map entry type card for a specified field type.
+constexpr MapTypeCard MakeMapTypeCard(WireFormatLite::FieldType type) {
+  switch (type) {
+    case WireFormatLite::TYPE_FLOAT:
+    case WireFormatLite::TYPE_FIXED32:
+    case WireFormatLite::TYPE_SFIXED32:
+      return {WireFormatLite::WIRETYPE_FIXED32, MapTypeCard::k32, false};
+
+    case WireFormatLite::TYPE_DOUBLE:
+    case WireFormatLite::TYPE_FIXED64:
+    case WireFormatLite::TYPE_SFIXED64:
+      return {WireFormatLite::WIRETYPE_FIXED64, MapTypeCard::k64, false};
+
+    case WireFormatLite::TYPE_BOOL:
+      return {WireFormatLite::WIRETYPE_VARINT, MapTypeCard::kBool, false};
+
+    case WireFormatLite::TYPE_ENUM:
+      // Enum validation is handled via `value_is_validated_enum` below.
+    case WireFormatLite::TYPE_INT32:
+    case WireFormatLite::TYPE_UINT32:
+      return {WireFormatLite::WIRETYPE_VARINT, MapTypeCard::k32, false};
+
+    case WireFormatLite::TYPE_INT64:
+    case WireFormatLite::TYPE_UINT64:
+      return {WireFormatLite::WIRETYPE_VARINT, MapTypeCard::k64, false};
+
+    case WireFormatLite::TYPE_SINT32:
+      return {WireFormatLite::WIRETYPE_VARINT, MapTypeCard::k32, true};
+    case WireFormatLite::TYPE_SINT64:
+      return {WireFormatLite::WIRETYPE_VARINT, MapTypeCard::k64, true};
+
+    case WireFormatLite::TYPE_STRING:
+      return {WireFormatLite::WIRETYPE_LENGTH_DELIMITED, MapTypeCard::kString,
+              true};
+    case WireFormatLite::TYPE_BYTES:
+      return {WireFormatLite::WIRETYPE_LENGTH_DELIMITED, MapTypeCard::kString,
+              false};
+
+    case WireFormatLite::TYPE_MESSAGE:
+      return {WireFormatLite::WIRETYPE_LENGTH_DELIMITED, MapTypeCard::kMessage,
+              false};
+
+    default:
+      PROTOBUF_ASSUME(false);
+  }
+}
+
+enum class MapNodeSizeInfoT : uint32_t;
+
+// Aux entry for map fields.
+struct MapAuxInfo {
+  MapTypeCard key_type_card;
+  MapTypeCard value_type_card;
+  // When off, we fall back to table->fallback to handle the parse. An example
+  // of this is for DynamicMessage.
+  uint8_t is_supported : 1;
+  // Determines if we are using LITE or the full runtime. When using the full
+  // runtime we have to synchronize with reflection before accessing the map.
+  uint8_t use_lite : 1;
+  // If true UTF8 errors cause the parsing to fail.
+  uint8_t fail_on_utf8_failure : 1;
+  // If true UTF8 errors are logged, but they are accepted.
+  uint8_t log_debug_utf8_failure : 1;
+  // If true the next aux contains the enum validator.
+  uint8_t value_is_validated_enum : 1;
+  // Size information derived from the actual node type.
+  MapNodeSizeInfoT node_size_info;
+};
+static_assert(sizeof(MapAuxInfo) <= 8, "");
+
 // Base class for message-level table with info for the tail-call parser.
 struct alignas(uint64_t) TcParseTableBase {
   // Common attributes for message layout:
   uint16_t has_bits_offset;
   uint16_t extension_offset;
-  uint32_t extension_range_low;
-  uint32_t extension_range_high;
   uint32_t max_field_number;
   uint8_t fast_idx_mask;
   uint16_t lookup_table_offset;
@@ -173,7 +300,6 @@
   // compiled.
   constexpr TcParseTableBase(
       uint16_t has_bits_offset, uint16_t extension_offset,
-      uint32_t extension_range_low, uint32_t extension_range_high,
       uint32_t max_field_number, uint8_t fast_idx_mask,
       uint16_t lookup_table_offset, uint32_t skipmap32,
       uint32_t field_entries_offset, uint16_t num_field_entries,
@@ -181,8 +307,6 @@
       const MessageLite* default_instance, TailCallParseFunc fallback)
       : has_bits_offset(has_bits_offset),
         extension_offset(extension_offset),
-        extension_range_low(extension_range_low),
-        extension_range_high(extension_range_high),
         max_field_number(max_field_number),
         fast_idx_mask(fast_idx_mask),
         lookup_table_offset(lookup_table_offset),
@@ -275,6 +399,9 @@
     constexpr FieldAux(FieldAuxDefaultMessage, const void* msg)
         : message_default_p(msg) {}
     constexpr FieldAux(const TcParseTableBase* table) : table(table) {}
+    constexpr FieldAux(MapAuxInfo map_info) : map_info(map_info) {}
+    constexpr FieldAux(void (*create_in_arena)(Arena*, void*))
+        : create_in_arena(create_in_arena) {}
     bool (*enum_validator)(int);
     struct {
       int16_t start;    // minimum enum number (if it fits)
@@ -283,6 +410,8 @@
     uint32_t offset;
     const void* message_default_p;
     const TcParseTableBase* table;
+    MapAuxInfo map_info;
+    void (*create_in_arena)(Arena*, void*);
 
     const MessageLite* message_default() const {
       return static_cast<const MessageLite*>(message_default_p);
diff --git a/src/google/protobuf/generated_message_tctable_gen.cc b/src/google/protobuf/generated_message_tctable_gen.cc
index b829bb7..987b669 100644
--- a/src/google/protobuf/generated_message_tctable_gen.cc
+++ b/src/google/protobuf/generated_message_tctable_gen.cc
@@ -78,100 +78,161 @@
   }
 }
 
+absl::string_view ParseFunctionValue(TcParseFunction function) {
+#define PROTOBUF_TC_PARSE_FUNCTION_X(value) #value,
+  static constexpr absl::string_view functions[] = {
+      {}, PROTOBUF_TC_PARSE_FUNCTION_LIST};
+#undef PROTOBUF_TC_PARSE_FUNCTION_X
+  return functions[static_cast<int>(function)];
+};
+
+enum class EnumRangeInfo {
+  kNone,         // No contiguous range
+  kContiguous,   // Has a contiguous range
+  kContiguous0,  // Has a small contiguous range starting at 0
+  kContiguous1,  // Has a small contiguous range starting at 1
+};
+
+// Returns enum validation range info, and sets `rmax_value` iff
+// the returned range is a small range. `rmax_value` is guaranteed
+// to remain unchanged if the enum range is not small.
+EnumRangeInfo GetEnumRangeInfo(const FieldDescriptor* field,
+                               uint8_t& rmax_value) {
+  int16_t start;
+  uint16_t size;
+  if (!GetEnumValidationRange(field->enum_type(), start, size)) {
+    return EnumRangeInfo::kNone;
+  }
+  int max_value = start + size - 1;
+  if (max_value <= 127 && (start == 0 || start == 1)) {
+    rmax_value = static_cast<uint8_t>(max_value);
+    return start == 0 ? EnumRangeInfo::kContiguous0
+                      : EnumRangeInfo::kContiguous1;
+  }
+  return EnumRangeInfo::kContiguous;
+}
+
 void PopulateFastFieldEntry(const TailCallTableInfo::FieldEntryInfo& entry,
                             const TailCallTableInfo::PerFieldOptions& options,
                             TailCallTableInfo::FastFieldInfo& info) {
+#define PROTOBUF_PICK_FUNCTION(fn) \
+  (field->number() < 16 ? TcParseFunction::fn##1 : TcParseFunction::fn##2)
+
+#define PROTOBUF_PICK_SINGLE_FUNCTION(fn) PROTOBUF_PICK_FUNCTION(fn##S)
+
+#define PROTOBUF_PICK_REPEATABLE_FUNCTION(fn)           \
+  (field->is_repeated() ? PROTOBUF_PICK_FUNCTION(fn##R) \
+                        : PROTOBUF_PICK_FUNCTION(fn##S))
+
+#define PROTOBUF_PICK_PACKABLE_FUNCTION(fn)               \
+  (field->is_packed()     ? PROTOBUF_PICK_FUNCTION(fn##P) \
+   : field->is_repeated() ? PROTOBUF_PICK_FUNCTION(fn##R) \
+                          : PROTOBUF_PICK_FUNCTION(fn##S))
+
+#define PROTOBUF_PICK_STRING_FUNCTION(fn)                       \
+  (field->options().ctype() == FieldOptions::CORD               \
+       ? PROTOBUF_PICK_FUNCTION(fn##cS)                         \
+   : options.is_string_inlined ? PROTOBUF_PICK_FUNCTION(fn##iS) \
+                               : PROTOBUF_PICK_REPEATABLE_FUNCTION(fn))
+
+#define PROTOBUF_PICK_MESSAGE_FUNCTION(fn)        \
+  (options.use_direct_tcparser_table              \
+       ? PROTOBUF_PICK_REPEATABLE_FUNCTION(fn##t) \
+       : PROTOBUF_PICK_REPEATABLE_FUNCTION(fn##d))
+
   const FieldDescriptor* field = entry.field;
-  std::string name = "::_pbi::TcParser::Fast";
-  uint8_t aux_idx = static_cast<uint8_t>(entry.aux_idx);
-
-  static const char* kPrefix[] = {
-      nullptr,  // 0
-      "F64",    // TYPE_DOUBLE = 1,
-      "F32",    // TYPE_FLOAT = 2,
-      "V64",    // TYPE_INT64 = 3,
-      "V64",    // TYPE_UINT64 = 4,
-      "V32",    // TYPE_INT32 = 5,
-      "F64",    // TYPE_FIXED64 = 6,
-      "F32",    // TYPE_FIXED32 = 7,
-      "V8",     // TYPE_BOOL = 8,
-      "",       // TYPE_STRING = 9,
-      "G",      // TYPE_GROUP = 10,
-      "M",      // TYPE_MESSAGE = 11,
-      "B",      // TYPE_BYTES = 12,
-      "V32",    // TYPE_UINT32 = 13,
-      "",       // TYPE_ENUM = 14,
-      "F32",    // TYPE_SFIXED32 = 15,
-      "F64",    // TYPE_SFIXED64 = 16,
-      "Z32",    // TYPE_SINT32 = 17,
-      "Z64",    // TYPE_SINT64 = 18,
-  };
-  name.append(kPrefix[field->type()]);
-
-  if (field->type() == field->TYPE_ENUM) {
-    // Enums are handled as:
-    //  - V32 for open enums
-    //  - Er (and Er0/Er1) for sequential enums
-    //  - Ev for the rest
-    if (cpp::HasPreservingUnknownEnumSemantics(field)) {
-      name.append("V32");
-    } else {
-      int16_t start;
-      uint16_t size;
-      if (GetEnumValidationRange(field->enum_type(), start, size)) {
-        name.append("Er");
-        int max_value = start + size - 1;
-        if (max_value <= 127 && (start == 0 || start == 1)) {
-          name.append(1, '0' + start);
-          aux_idx = max_value;
-        }
-      } else {
-        name.append("Ev");
-      }
-    }
-  }
-  if (field->type() == field->TYPE_STRING) {
-    switch (internal::cpp::GetUtf8CheckMode(field, options.is_lite)) {
-      case internal::cpp::Utf8CheckMode::kStrict:
-        name.append("U");
-        break;
-      case internal::cpp::Utf8CheckMode::kVerify:
-        name.append("S");
-        break;
-      case internal::cpp::Utf8CheckMode::kNone:
-        name.append("B");
-        break;
-    }
-  }
-  if (field->type() == field->TYPE_STRING ||
-      field->type() == field->TYPE_BYTES) {
-    if (field->options().ctype() == FieldOptions::CORD) {
-      name.append("c");
-    } else if (options.is_string_inlined) {
-      name.append("i");
+  info.aux_idx = static_cast<uint8_t>(entry.aux_idx);
+  if (field->type() == FieldDescriptor::TYPE_BYTES ||
+      field->type() == FieldDescriptor::TYPE_STRING) {
+    if (options.is_string_inlined) {
       ABSL_CHECK(!field->is_repeated());
-      aux_idx = static_cast<uint8_t>(entry.inlined_string_idx);
+      info.aux_idx = static_cast<uint8_t>(entry.inlined_string_idx);
     }
   }
-  if (field->type() == field->TYPE_MESSAGE ||
-      field->type() == field->TYPE_GROUP) {
-    name.append(options.use_direct_tcparser_table ? "t" : "d");
+
+  TcParseFunction picked = TcParseFunction::kNone;
+  switch (field->type()) {
+    case FieldDescriptor::TYPE_BOOL:
+      picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastV8);
+      break;
+    case FieldDescriptor::TYPE_INT32:
+    case FieldDescriptor::TYPE_UINT32:
+      picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastV32);
+      break;
+    case FieldDescriptor::TYPE_SINT32:
+      picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastZ32);
+      break;
+    case FieldDescriptor::TYPE_INT64:
+    case FieldDescriptor::TYPE_UINT64:
+      picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastV64);
+      break;
+    case FieldDescriptor::TYPE_SINT64:
+      picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastZ64);
+      break;
+    case FieldDescriptor::TYPE_FLOAT:
+    case FieldDescriptor::TYPE_FIXED32:
+    case FieldDescriptor::TYPE_SFIXED32:
+      picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastF32);
+      break;
+    case FieldDescriptor::TYPE_DOUBLE:
+    case FieldDescriptor::TYPE_FIXED64:
+    case FieldDescriptor::TYPE_SFIXED64:
+      picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastF64);
+      break;
+    case FieldDescriptor::TYPE_ENUM:
+      if (cpp::HasPreservingUnknownEnumSemantics(field)) {
+        picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastV32);
+      } else {
+        switch (GetEnumRangeInfo(field, info.aux_idx)) {
+          case EnumRangeInfo::kNone:
+            picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastEv);
+            break;
+          case EnumRangeInfo::kContiguous:
+            picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastEr);
+            break;
+          case EnumRangeInfo::kContiguous0:
+            picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastEr0);
+            break;
+          case EnumRangeInfo::kContiguous1:
+            picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastEr1);
+            break;
+        }
+      }
+      break;
+    case FieldDescriptor::TYPE_BYTES:
+      picked = PROTOBUF_PICK_STRING_FUNCTION(kFastB);
+      break;
+    case FieldDescriptor::TYPE_STRING:
+      switch (internal::cpp::GetUtf8CheckMode(field, options.is_lite)) {
+        case internal::cpp::Utf8CheckMode::kStrict:
+          picked = PROTOBUF_PICK_STRING_FUNCTION(kFastU);
+          break;
+        case internal::cpp::Utf8CheckMode::kVerify:
+          picked = PROTOBUF_PICK_STRING_FUNCTION(kFastS);
+          break;
+        case internal::cpp::Utf8CheckMode::kNone:
+          picked = PROTOBUF_PICK_STRING_FUNCTION(kFastB);
+          break;
+      }
+      break;
+    case FieldDescriptor::TYPE_MESSAGE:
+      picked = PROTOBUF_PICK_MESSAGE_FUNCTION(kFastM);
+      break;
+    case FieldDescriptor::TYPE_GROUP:
+      picked = PROTOBUF_PICK_MESSAGE_FUNCTION(kFastG);
+      break;
   }
 
-  // The field implementation functions are prefixed by cardinality:
-  //   `S` for optional or implicit fields.
-  //   `R` for non-packed repeated.
-  //   `P` for packed repeated.
-  name.append(field->is_packed()               ? "P"
-              : field->is_repeated()           ? "R"
-              : field->real_containing_oneof() ? "O"
-                                               : "S");
+  ABSL_CHECK(picked != TcParseFunction::kNone);
+  static constexpr absl::string_view ns = "::_pbi::TcParser::";
+  info.func_name = absl::StrCat(ns, ParseFunctionValue(picked));
 
-  // Append the tag length. Fast parsing only handles 1- or 2-byte tags.
-  name.append(field->number() < 16 ? "1" : "2");
-
-  info.func_name = std::move(name);
-  info.aux_idx = aux_idx;
+#undef PROTOBUF_PICK_FUNCTION
+#undef PROTOBUF_PICK_SINGLE_FUNCTION
+#undef PROTOBUF_PICK_REPEATABLE_FUNCTION
+#undef PROTOBUF_PICK_PACKABLE_FUNCTION
+#undef PROTOBUF_PICK_STRING_FUNCTION
+#undef PROTOBUF_PICK_MESSAGE_FUNCTION
 }
 
 bool IsFieldEligibleForFastParsing(
@@ -364,7 +425,7 @@
       case FieldDescriptor::TYPE_MESSAGE:
       case FieldDescriptor::TYPE_GROUP:
         // TODO(b/210762816): support remaining field types.
-        if (field->is_map() || field->options().weak() || options.is_lazy) {
+        if (field->options().weak() || options.is_lazy) {
           handled = false;
         } else {
           handled = true;
@@ -383,33 +444,38 @@
 
 // We only need field names for reporting UTF-8 parsing errors, so we only
 // emit them for string fields with Utf8 transform specified.
-absl::string_view FieldNameForTable(
-    const TailCallTableInfo::FieldEntryInfo& entry) {
-  const auto* field = entry.field;
-  if (field->type() == FieldDescriptor::TYPE_STRING) {
-    const uint16_t xform_val = entry.type_card & field_layout::kTvMask;
+bool NeedsFieldNameForTable(const FieldDescriptor* field, bool is_lite) {
+  if (cpp::GetUtf8CheckMode(field, is_lite) == cpp::Utf8CheckMode::kNone)
+    return false;
+  return field->type() == FieldDescriptor::TYPE_STRING ||
+         (field->is_map() && (field->message_type()->map_key()->type() ==
+                                  FieldDescriptor::TYPE_STRING ||
+                              field->message_type()->map_value()->type() ==
+                                  FieldDescriptor::TYPE_STRING));
+}
 
-    switch (xform_val) {
-      case field_layout::kTvUtf8:
-      case field_layout::kTvUtf8Debug:
-        return field->name();
-    }
+absl::string_view FieldNameForTable(
+    const TailCallTableInfo::FieldEntryInfo& entry,
+    const TailCallTableInfo::OptionProvider& option_provider) {
+  if (NeedsFieldNameForTable(
+          entry.field, option_provider.GetForField(entry.field).is_lite)) {
+    return entry.field->name();
   }
   return "";
 }
 
 std::vector<uint8_t> GenerateFieldNames(
     const Descriptor* descriptor,
-    const std::vector<TailCallTableInfo::FieldEntryInfo>& entries) {
+    const std::vector<TailCallTableInfo::FieldEntryInfo>& entries,
+    const TailCallTableInfo::OptionProvider& option_provider) {
   static constexpr int kMaxNameLength = 255;
   std::vector<uint8_t> out;
 
+  std::vector<absl::string_view> names;
   bool found_needed_name = false;
   for (const auto& entry : entries) {
-    if (!FieldNameForTable(entry).empty()) {
-      found_needed_name = true;
-      break;
-    }
+    names.push_back(FieldNameForTable(entry, option_provider));
+    if (!names.back().empty()) found_needed_name = true;
   }
 
   // No names needed. Omit the whole table.
@@ -422,8 +488,8 @@
   int count = 1;
   out.push_back(std::min(static_cast<int>(descriptor->full_name().size()),
                          kMaxNameLength));
-  for (const auto& entry : entries) {
-    out.push_back(FieldNameForTable(entry).size());
+  for (auto field_name : names) {
+    out.push_back(field_name.size());
     ++count;
   }
   while (count & 7) {  // align to an 8-byte boundary
@@ -440,8 +506,7 @@
   }
   out.insert(out.end(), message_name.begin(), message_name.end());
   // Then we output the actual field names
-  for (const auto& entry : entries) {
-    const auto& field_name = FieldNameForTable(entry);
+  for (auto field_name : names) {
     out.insert(out.end(), field_name.begin(), field_name.end());
   }
 
@@ -718,7 +783,19 @@
         field->type() == FieldDescriptor::TYPE_GROUP) {
       // Message-typed fields have a FieldAux with the default instance pointer.
       if (field->is_map()) {
-        // TODO(b/205904770): generate aux entries for maps
+        field_entries.back().aux_idx = aux_entries.size();
+        aux_entries.push_back({kMapAuxInfo, {field}});
+        if (options.uses_codegen) {
+          // If we don't use codegen we can't add these.
+          auto* map_value = field->message_type()->map_value();
+          if (auto* sub = map_value->message_type()) {
+            aux_entries.push_back({kCreateInArena});
+            aux_entries.back().desc = sub;
+          } else if (map_value->type() == FieldDescriptor::TYPE_ENUM &&
+                     !cpp::HasPreservingUnknownEnumSemantics(map_value)) {
+            aux_entries.push_back({kEnumValidator, {map_value}});
+          }
+        }
       } else if (field->options().weak()) {
         // Don't generate anything for weak fields. They are handled by the
         // generated fallback.
@@ -819,13 +896,13 @@
 
   num_to_entry_table = MakeNumToEntryTable(ordered_fields);
   ABSL_CHECK_EQ(field_entries.size(), ordered_fields.size());
-  field_name_data = GenerateFieldNames(descriptor, field_entries);
+  field_name_data =
+      GenerateFieldNames(descriptor, field_entries, option_provider);
 
   // If there are no fallback fields, and at most one extension range, the
   // parser can use a generic fallback function. Otherwise, a message-specific
   // fallback routine is needed.
-  use_generated_fallback =
-      !fallback_fields.empty() || descriptor->extension_range_count() > 1;
+  use_generated_fallback = !fallback_fields.empty();
 }
 
 }  // namespace internal
diff --git a/src/google/protobuf/generated_message_tctable_gen.h b/src/google/protobuf/generated_message_tctable_gen.h
index ac24dd7..3f5f7ec 100644
--- a/src/google/protobuf/generated_message_tctable_gen.h
+++ b/src/google/protobuf/generated_message_tctable_gen.h
@@ -59,6 +59,7 @@
     bool use_direct_tcparser_table;
     bool is_lite;
     bool should_split;
+    bool uses_codegen;
   };
   class OptionProvider {
    public:
@@ -106,6 +107,8 @@
     kEnumRange,
     kEnumValidator,
     kNumericOffset,
+    kMapAuxInfo,
+    kCreateInArena,
   };
   struct AuxEntry {
     AuxType type;
@@ -115,6 +118,7 @@
     };
     union {
       const FieldDescriptor* field;
+      const Descriptor* desc;
       uint32_t offset;
       EnumRange enum_range;
     };
diff --git a/src/google/protobuf/generated_message_tctable_impl.h b/src/google/protobuf/generated_message_tctable_impl.h
index a39245d..2527fd0 100644
--- a/src/google/protobuf/generated_message_tctable_impl.h
+++ b/src/google/protobuf/generated_message_tctable_impl.h
@@ -40,6 +40,7 @@
 #include "google/protobuf/port.h"
 #include "google/protobuf/extension_set.h"
 #include "google/protobuf/generated_message_tctable_decl.h"
+#include "google/protobuf/map.h"
 #include "google/protobuf/metadata_lite.h"
 #include "google/protobuf/parse_context.h"
 #include "google/protobuf/wire_format_lite.h"
@@ -84,6 +85,7 @@
 namespace field_layout {
 // clang-format off
 
+
 // Field kind (3 bits):
 // These values broadly represent a wire type and an in-memory storage class.
 enum FieldKind : uint16_t {
@@ -128,6 +130,7 @@
   kFcOneof    = 3 << kFcShift,
 };
 
+
 // Field representation (3 bits):
 // These values are the specific refinements of storage classes in FieldType.
 enum FieldRep : uint16_t {
@@ -267,11 +270,114 @@
                       std::uintptr_t address) {}
 #endif
 
+#define PROTOBUF_TC_PARSE_FUNCTION_LIST_SINGLE(fn) \
+  PROTOBUF_TC_PARSE_FUNCTION_X(fn##S1)             \
+  PROTOBUF_TC_PARSE_FUNCTION_X(fn##S2)
+
+#define PROTOBUF_TC_PARSE_FUNCTION_LIST_REPEATED(fn) \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_SINGLE(fn)         \
+  PROTOBUF_TC_PARSE_FUNCTION_X(fn##R1)               \
+  PROTOBUF_TC_PARSE_FUNCTION_X(fn##R2)
+
+#define PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(fn) \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_REPEATED(fn)     \
+  PROTOBUF_TC_PARSE_FUNCTION_X(fn##P1)             \
+  PROTOBUF_TC_PARSE_FUNCTION_X(fn##P2)
+
+#define PROTOBUF_TC_PARSE_FUNCTION_LIST_END_GROUP() \
+  PROTOBUF_TC_PARSE_FUNCTION_X(FastEndG1)           \
+  PROTOBUF_TC_PARSE_FUNCTION_X(FastEndG2)
+
+// TcParseFunction defines the set of table driven, tail call optimized parse
+// functions. This list currently does not include all types such as maps.
+//
+// This table identifies the logical set of functions, it does not imply that
+// functions of the same name do exist, and some entries may point to thunks or
+// generic implementations accepting multiple types of input.
+//
+// The names are encoded as follows:
+//   kFast<type>[<validation>][cardinality][tag_width]
+//
+//   type:
+//     V8  - bool
+//     V32 - int32/uint32 varint
+//     Z32 - int32/uint32 varint with zigzag encoding
+//     V64 - int64/uint64 varint
+//     Z64 - int64/uint64 varint with zigzag encoding
+//     F32 - int32/uint32/float fixed width value
+//     F64 - int64/uint64/double fixed width value
+//     E   - enum
+//     B   - string (bytes)*
+//     S   - utf8 string, verified in debug mode only*
+//     U   - utf8 string, strictly verified*
+//     Gd  - group
+//     Gt  - group width table driven parse tables
+//     Md  - message
+//     Mt  - message width table driven parse tables
+//     End - End group tag
+//
+// * string types can have a `c` or `i` suffix, indicating the
+//   underlying storage type to be cord or inlined respectively.
+//
+//  validation:
+//    For enums:
+//      v  - verify
+//      r  - verify; enum values are a contiguous range
+//      r0 - verify; enum values are a small contiguous range starting at 0
+//      r1 - verify; enum values are a small contiguous range starting at 1
+//    For strings:
+//      u - validate utf8 encoding
+//      v - validate utf8 encoding for debug only
+//
+//  cardinality:
+//    S - singular / optional
+//    R - repeated
+//    P - packed
+//    G - group terminated
+//
+//  tag_width:
+//    1: single byte encoded tag
+//    2: two byte encoded tag
+//
+// Examples:
+//   FastV8S1, FastZ64S2, FastEr1P2, FastBcS1, FastMtR2, FastEndG1
+//
+#define PROTOBUF_TC_PARSE_FUNCTION_LIST            \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastV8)   \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastV32)  \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastV64)  \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastZ32)  \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastZ64)  \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastF32)  \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastF64)  \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastEv)   \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastEr)   \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastEr0)  \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastEr1)  \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_REPEATED(FastB)  \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_REPEATED(FastS)  \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_REPEATED(FastU)  \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_SINGLE(FastBi)   \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_SINGLE(FastSi)   \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_SINGLE(FastUi)   \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_SINGLE(FastBc)   \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_SINGLE(FastSc)   \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_SINGLE(FastUc)   \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_REPEATED(FastGd) \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_REPEATED(FastGt) \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_REPEATED(FastMd) \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_REPEATED(FastMt) \
+  PROTOBUF_TC_PARSE_FUNCTION_LIST_END_GROUP()
+
+#define PROTOBUF_TC_PARSE_FUNCTION_X(value) k##value,
+enum class TcParseFunction { kNone, PROTOBUF_TC_PARSE_FUNCTION_LIST };
+#undef PROTOBUF_TC_PARSE_FUNCTION_X
+
 // TcParser implements most of the parsing logic for tailcall tables.
 class PROTOBUF_EXPORT TcParser final {
  public:
   template <typename T>
-  static constexpr const TcParseTableBase* GetTable() {
+  static constexpr auto GetTable() -> decltype(&T::_table_.header) {
     return &T::_table_.header;
   }
 
@@ -292,7 +398,7 @@
   //    the function is used as a way to get a UnknownFieldOps vtable, returned
   //    via the `const char*` return type. See `GetUnknownFieldOps()`
 
-  static bool MustFallbackToGeneric(PROTOBUF_TC_PARAM_DECL) {
+  static bool MustFallbackToGeneric(PROTOBUF_TC_PARAM_NO_DATA_DECL) {
     return ptr == nullptr;
   }
 
@@ -359,36 +465,16 @@
   static const char* FastZ64P1(PROTOBUF_TC_PARAM_DECL);
   static const char* FastZ64P2(PROTOBUF_TC_PARAM_DECL);
 
-  // Manually unrolled and specialized Varint parsing.
-  template <typename FieldType, int data_offset, int hasbit_idx>
-  static const char* FastTV32S1(PROTOBUF_TC_PARAM_DECL);
-  template <typename FieldType, int data_offset, int hasbit_idx>
-  static const char* FastTV64S1(PROTOBUF_TC_PARAM_DECL);
-  template <int data_offset, int hasbit_idx>
-  static const char* FastTV8S1(PROTOBUF_TC_PARAM_DECL);
-
-  template <typename FieldType, int data_offset, int hasbit_idx>
+  template <typename FieldType, int unused_data_offset, int unused_hasbit_idx>
   static constexpr TailCallParseFunc SingularVarintNoZag1() {
     if (sizeof(FieldType) == 1) {
-      if (data_offset < 100) {
-        return &FastTV8S1<data_offset, hasbit_idx>;
-      } else {
-        return &FastV8S1;
-      }
+      return &FastV8S1;
     }
     if (sizeof(FieldType) == 4) {
-      if (data_offset < 100) {
-        return &FastTV32S1<FieldType, data_offset, hasbit_idx>;
-      } else {  //
-        return &FastV32S1;
-      }
+      return &FastV32S1;
     }
     if (sizeof(FieldType) == 8) {
-      if (data_offset < 128) {
-        return &FastTV64S1<FieldType, data_offset, hasbit_idx>;
-      } else {
-        return &FastV64S1;
-      }
+      return &FastV64S1;
     }
     static_assert(sizeof(FieldType) == 1 || sizeof(FieldType) == 4 ||
                       sizeof(FieldType) == 8,
@@ -529,12 +615,43 @@
   // NOTE: Currently, this function only calls the table-level fallback
   // function, so it should only be called as the fallback from fast table
   // parsing.
-  static const char* MiniParse(PROTOBUF_TC_PARAM_DECL);
+  static const char* MiniParse(PROTOBUF_TC_PARAM_NO_DATA_DECL);
 
   static const char* FastEndG1(PROTOBUF_TC_PARAM_DECL);
   static const char* FastEndG2(PROTOBUF_TC_PARAM_DECL);
 
+  // For `map` mini parsing generate a type card for the key/value.
+  template <typename MapField>
+  static constexpr MapAuxInfo GetMapAuxInfo(bool fail_on_utf8_failure,
+                                            bool log_debug_utf8_failure,
+                                            bool validated_enum_value) {
+    using MapType = typename MapField::MapType;
+    using Node = typename MapType::Node;
+    static_assert(alignof(Node) == alignof(NodeBase), "");
+    // Verify the assumption made in MpMap, guaranteed by Map<>.
+    assert(PROTOBUF_FIELD_OFFSET(Node, kv.first) == sizeof(NodeBase));
+    return {
+        MakeMapTypeCard(MapField::kKeyFieldType),
+        MakeMapTypeCard(MapField::kValueFieldType),
+        true,
+        !std::is_base_of<MapFieldBaseForParse, MapField>::value,
+        fail_on_utf8_failure,
+        log_debug_utf8_failure,
+        validated_enum_value,
+        Node::size_info(),
+    };
+  }
+
+  template <typename T>
+  static void CreateInArenaStorageCb(Arena* arena, void* p) {
+    Arena::CreateInArenaStorage(static_cast<T*>(p), arena);
+  }
+
  private:
+  // Optimized small tag varint parser for int32/int64
+  template <typename FieldType>
+  static const char* FastVarintS1(PROTOBUF_TC_PARAM_DECL);
+
   friend class GeneratedTcTableLiteTest;
   static void* MaybeGetSplitBase(MessageLite* msg, const bool is_split,
                                  const TcParseTableBase* table);
@@ -569,12 +686,13 @@
     }
   }
 
-  static const char* TagDispatch(PROTOBUF_TC_PARAM_DECL);
-  static const char* ToTagDispatch(PROTOBUF_TC_PARAM_DECL);
-  static const char* ToParseLoop(PROTOBUF_TC_PARAM_DECL);
-  static const char* Error(PROTOBUF_TC_PARAM_DECL);
+  static const char* TagDispatch(PROTOBUF_TC_PARAM_NO_DATA_DECL);
+  static const char* ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_DECL);
+  static const char* ToParseLoop(PROTOBUF_TC_PARAM_NO_DATA_DECL);
+  static const char* Error(PROTOBUF_TC_PARAM_NO_DATA_DECL);
 
   static const char* FastUnknownEnumFallback(PROTOBUF_TC_PARAM_DECL);
+  static const char* MpUnknownEnumFallback(PROTOBUF_TC_PARAM_DECL);
 
   class ScopedArenaSwap;
 
@@ -618,18 +736,21 @@
       return ptr;
     }
 
-    uint32_t num = tag >> 3;
-    if (table->extension_range_low <= num &&
-        num <= table->extension_range_high) {
+    if (table->extension_offset != 0) {
+      // We don't need to check the extension ranges. If it is not an extension
+      // it will be handled just like if it was an unknown extension: sent to
+      // the unknown field set.
       return RefAt<ExtensionSet>(msg, table->extension_offset)
           .ParseField(tag, ptr,
                       static_cast<const MessageBaseT*>(table->default_instance),
                       &msg->_internal_metadata_, ctx);
+    } else {
+      // Otherwise, we directly put it on the unknown field set.
+      return UnknownFieldParse(
+          tag,
+          msg->_internal_metadata_.mutable_unknown_fields<UnknownFieldsT>(),
+          ptr, ctx);
     }
-
-    return UnknownFieldParse(
-        tag, msg->_internal_metadata_.mutable_unknown_fields<UnknownFieldsT>(),
-        ptr, ctx);
   }
 
   // Note: `inline` is needed on template function declarations below to avoid
@@ -680,8 +801,23 @@
       const char* ptr, Arena* arena, SerialArena* serial_arena,
       ParseContext* ctx, RepeatedPtrField<std::string>& field);
 
-  static void UnknownPackedEnum(MessageLite* msg, const TcParseTableBase* table,
-                                uint32_t tag, int32_t enum_value);
+  static void AddUnknownEnum(MessageLite* msg, const TcParseTableBase* table,
+                             uint32_t tag, int32_t enum_value);
+
+  static void WriteMapEntryAsUnknown(MessageLite* msg,
+                                     const TcParseTableBase* table,
+                                     uint32_t tag, NodeBase* node,
+                                     MapAuxInfo map_info);
+
+  static void InitializeMapNodeEntry(void* obj, MapTypeCard type_card,
+                                     UntypedMapBase& map,
+                                     const TcParseTableBase::FieldAux* aux);
+  static void DestroyMapNode(NodeBase* node, MapAuxInfo map_info,
+                             UntypedMapBase& map);
+  static const char* ParseOneMapEntry(
+      NodeBase* node, const char* ptr, ParseContext* ctx,
+      const TcParseTableBase::FieldAux* aux, const TcParseTableBase* table,
+      const TcParseTableBase::FieldEntry& entry);
 
   // Mini field lookup:
   static const TcParseTableBase::FieldEntry* FindFieldEntry(
@@ -724,264 +860,17 @@
   static const char* MpMessage(PROTOBUF_TC_PARAM_DECL);
   static const char* MpRepeatedMessage(PROTOBUF_TC_PARAM_DECL);
   static const char* MpFallback(PROTOBUF_TC_PARAM_DECL);
+  static const char* MpMap(PROTOBUF_TC_PARAM_DECL);
 };
 
-// Shift "byte" left by n * 7 bits, filling vacated bits with ones.
-template <int n>
-inline PROTOBUF_ALWAYS_INLINE int64_t shift_left_fill_with_ones(uint64_t byte,
-                                                                uint64_t ones) {
-  return static_cast<int64_t>((byte << (n * 7)) | (ones >> (64 - (n * 7))));
-}
-
-// Shift "byte" left by n * 7 bits, filling vacated bits with ones, and
-// put the new value in res.  Return whether the result was negative.
-template <int n>
-inline PROTOBUF_ALWAYS_INLINE bool shift_left_fill_with_ones_was_negative(
-    uint64_t byte, uint64_t ones, int64_t& res) {
-#if defined(__GCC_ASM_FLAG_OUTPUTS__) && defined(__x86_64__)
-  // For the first two rounds (ptr[1] and ptr[2]), micro benchmarks show a
-  // substantial improvement from capturing the sign from the condition code
-  // register on x86-64.
-  bool sign_bit;
-  asm("shldq %3, %2, %1"
-      : "=@ccs"(sign_bit), "+r"(byte)
-      : "r"(ones), "i"(n * 7));
-  res = static_cast<int64_t>(byte);
-  return sign_bit;
-#else
-  // Generic fallback:
-  res = shift_left_fill_with_ones<n>(byte, ones);
-  return res < 0;
-#endif
-}
-
-template <class VarintType>
-inline PROTOBUF_ALWAYS_INLINE std::pair<const char*, VarintType>
-ParseFallbackPair(const char* p, int64_t res1) {
-  constexpr bool kIs64BitVarint = std::is_same<VarintType, uint64_t>::value;
-  constexpr bool kIs32BitVarint = std::is_same<VarintType, uint32_t>::value;
-  static_assert(kIs64BitVarint || kIs32BitVarint,
-                "Only 32 or 64 bit varints are supported");
-  auto ptr = reinterpret_cast<const int8_t*>(p);
-
-  // The algorithm relies on sign extension for each byte to set all high bits
-  // when the varint continues. It also relies on asserting all of the lower
-  // bits for each successive byte read. This allows the result to be aggregated
-  // using a bitwise AND. For example:
-  //
-  //          8       1          64     57 ... 24     17  16      9  8       1
-  // ptr[0] = 1aaa aaaa ; res1 = 1111 1111 ... 1111 1111  1111 1111  1aaa aaaa
-  // ptr[1] = 1bbb bbbb ; res2 = 1111 1111 ... 1111 1111  11bb bbbb  b111 1111
-  // ptr[2] = 1ccc cccc ; res3 = 0000 0000 ... 000c cccc  cc11 1111  1111 1111
-  //                             ---------------------------------------------
-  //        res1 & res2 & res3 = 0000 0000 ... 000c cccc  ccbb bbbb  baaa aaaa
-  //
-  // On x86-64, a shld from a single register filled with enough 1s in the high
-  // bits can accomplish all this in one instruction. It so happens that res1
-  // has 57 high bits of ones, which is enough for the largest shift done.
-  //
-  // Just as importantly, by keeping results in res1, res2, and res3, we take
-  // advantage of the superscalar abilities of the CPU.
-  ABSL_DCHECK_EQ(res1 >> 7, -1);
-  uint64_t ones = res1;  // save the high 1 bits from res1 (input to SHLD)
-  int64_t res2, res3;    // accumulated result chunks
-
-  if (!shift_left_fill_with_ones_was_negative<1>(ptr[1], ones, res2))
-    goto done2;
-  if (!shift_left_fill_with_ones_was_negative<2>(ptr[2], ones, res3))
-    goto done3;
-
-  // For the remainder of the chunks, check the sign of the AND result.
-  res2 &= shift_left_fill_with_ones<3>(ptr[3], ones);
-  if (res2 >= 0) goto done4;
-  res1 &= shift_left_fill_with_ones<4>(ptr[4], ones);
-  if (res1 >= 0) goto done5;
-  if (kIs64BitVarint) {
-    res2 &= shift_left_fill_with_ones<5>(ptr[5], ones);
-    if (res2 >= 0) goto done6;
-    res3 &= shift_left_fill_with_ones<6>(ptr[6], ones);
-    if (res3 >= 0) goto done7;
-    res1 &= shift_left_fill_with_ones<7>(ptr[7], ones);
-    if (res1 >= 0) goto done8;
-    res3 &= shift_left_fill_with_ones<8>(ptr[8], ones);
-    if (res3 >= 0) goto done9;
-  } else if (kIs32BitVarint) {
-    if (PROTOBUF_PREDICT_TRUE(!(ptr[5] & 0x80))) goto done6;
-    if (PROTOBUF_PREDICT_TRUE(!(ptr[6] & 0x80))) goto done7;
-    if (PROTOBUF_PREDICT_TRUE(!(ptr[7] & 0x80))) goto done8;
-    if (PROTOBUF_PREDICT_TRUE(!(ptr[8] & 0x80))) goto done9;
-  }
-
-  // For valid 64bit varints, the 10th byte/ptr[9] should be exactly 1. In this
-  // case, the continuation bit of ptr[8] already set the top bit of res3
-  // correctly, so all we have to do is check that the expected case is true.
-  if (PROTOBUF_PREDICT_TRUE(kIs64BitVarint && ptr[9] == 1)) goto done10;
-
-  if (PROTOBUF_PREDICT_FALSE(ptr[9] & 0x80)) {
-    // If the continue bit is set, it is an unterminated varint.
-    return {nullptr, 0};
-  }
-
-  // A zero value of the first bit of the 10th byte represents an
-  // over-serialized varint. This case should not happen, but if does (say, due
-  // to a nonconforming serializer), deassert the continuation bit that came
-  // from ptr[8].
-  if (kIs64BitVarint && (ptr[9] & 1) == 0) {
-#if defined(__GCC_ASM_FLAG_OUTPUTS__) && defined(__x86_64__)
-    // Use a small instruction since this is an uncommon code path.
-    asm("btcq $63,%0" : "+r"(res3));
-#else
-    res3 ^= static_cast<uint64_t>(1) << 63;
-#endif
-  }
-  goto done10;
-
-done2:
-  return {p + 2, res1 & res2};
-done3:
-  return {p + 3, res1 & res2 & res3};
-done4:
-  return {p + 4, res1 & res2 & res3};
-done5:
-  return {p + 5, res1 & res2 & res3};
-done6:
-  return {p + 6, res1 & res2 & res3};
-done7:
-  return {p + 7, res1 & res2 & res3};
-done8:
-  return {p + 8, res1 & res2 & res3};
-done9:
-  return {p + 9, res1 & res2 & res3};
-done10:
-  return {p + 10, res1 & res2 & res3};
-}
-
-// Notes:
-// 1) if data_offset is negative, it's read from data.offset()
-// 2) if hasbit_idx is negative, it's read from data.hasbit_idx()
-template <int data_offset, int hasbit_idx>
-PROTOBUF_NOINLINE const char* TcParser::FastTV8S1(PROTOBUF_TC_PARAM_DECL) {
-  using TagType = uint8_t;
-
-  // Special case for a varint bool field with a tag of 1 byte:
-  // The coded_tag() field will actually contain the value too and we can check
-  // both at the same time.
-  auto coded_tag = data.coded_tag<uint16_t>();
-  if (PROTOBUF_PREDICT_TRUE(coded_tag == 0x0000 || coded_tag == 0x0100)) {
-    auto& field =
-        RefAt<bool>(msg, data_offset >= 0 ? data_offset : data.offset());
-    // Note: we use `data.data` because Clang generates suboptimal code when
-    // using coded_tag.
-    // In x86_64 this uses the CH register to read the second byte out of
-    // `data`.
-    uint8_t value = data.data >> 8;
-    // The assume allows using a mov instead of test+setne.
-    PROTOBUF_ASSUME(value <= 1);
-    field = static_cast<bool>(value);
-
-    ptr += sizeof(TagType) + 1;  // Consume the tag and the value.
-    if (hasbit_idx < 0) {
-      hasbits |= (uint64_t{1} << data.hasbit_idx());
-    } else {
-      if (hasbit_idx < 32) {
-        // `& 31` avoids a compiler warning when hasbit_idx is negative.
-        hasbits |= (uint64_t{1} << (hasbit_idx & 31));
-      } else {
-        static_assert(hasbit_idx == 63 || (hasbit_idx < 32),
-                      "hard-coded hasbit_idx should be 0-31, or the special"
-                      "value 63, which indicates the field has no has-bit.");
-        // TODO(jorg): investigate whether higher hasbit indices are worth
-        // supporting. Something like:
-        // auto& hasblock = TcParser::RefAt<uint32_t>(msg, hasbit_idx / 32 * 4);
-        // hasblock |= uint32_t{1} << (hasbit_idx % 32);
-      }
-    }
-
-    PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
-  }
-
-  // If it didn't match above either the tag is wrong, or the value is encoded
-  // non-canonically.
-  // Jump to MiniParse as wrong tag is the most probable reason.
-  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
-}
-
-template <typename FieldType, int data_offset, int hasbit_idx>
-PROTOBUF_NOINLINE const char* TcParser::FastTV64S1(PROTOBUF_TC_PARAM_DECL) {
-  using TagType = uint8_t;
-  // super-early success test...
-  if (PROTOBUF_PREDICT_TRUE(((data.data) & 0x80FF) == 0)) {
-    ptr += sizeof(TagType);  // Consume tag
-    if (hasbit_idx < 32) {
-      hasbits |= (uint64_t{1} << hasbit_idx);
-    }
-    uint8_t value = data.data >> 8;
-    RefAt<FieldType>(msg, data_offset) = value;
-    ptr += 1;
-    PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
-  }
-  if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
-    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
-  }
-  ptr += sizeof(TagType);  // Consume tag
-  if (hasbit_idx < 32) {
-    hasbits |= (uint64_t{1} << hasbit_idx);
-  }
-
-  auto tmp =
-      ParseFallbackPair<uint64_t>(ptr, static_cast<int8_t>(data.data >> 8));
-  data.data = 0;  // Indicate to the compiler that we don't need this anymore.
-  ptr = tmp.first;
-  if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) {
-    return Error(PROTOBUF_TC_PARAM_PASS);
-  }
-
-  RefAt<FieldType>(msg, data_offset) = static_cast<FieldType>(tmp.second);
-  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
-}
-
-template <typename FieldType, int data_offset, int hasbit_idx>
-PROTOBUF_NOINLINE const char* TcParser::FastTV32S1(PROTOBUF_TC_PARAM_DECL) {
-  using TagType = uint8_t;
-  // super-early success test...
-  if (PROTOBUF_PREDICT_TRUE(((data.data) & 0x80FF) == 0)) {
-    ptr += sizeof(TagType);  // Consume tag
-    if (hasbit_idx < 32) {
-      hasbits |= (uint64_t{1} << hasbit_idx);
-    }
-    uint8_t value = data.data >> 8;
-    RefAt<FieldType>(msg, data_offset) = value;
-    ptr += 1;
-    PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
-  }
-  if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
-    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
-  }
-  ptr += sizeof(TagType);  // Consume tag
-  if (hasbit_idx < 32) {
-    hasbits |= (uint64_t{1} << hasbit_idx);
-  }
-
-  auto tmp =
-      ParseFallbackPair<uint32_t>(ptr, static_cast<int8_t>(data.data >> 8));
-  data.data = 0;  // Indicate to the compiler that we don't need this anymore.
-  ptr = tmp.first;
-  if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) {
-    return Error(PROTOBUF_TC_PARAM_PASS);
-  }
-
-  RefAt<FieldType>(msg, data_offset) = static_cast<FieldType>(tmp.second);
-  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
-}
-
 // Dispatch to the designated parse function
 inline PROTOBUF_ALWAYS_INLINE const char* TcParser::TagDispatch(
-    PROTOBUF_TC_PARAM_DECL) {
+    PROTOBUF_TC_PARAM_NO_DATA_DECL) {
   const auto coded_tag = UnalignedLoad<uint16_t>(ptr);
   const size_t idx = coded_tag & table->fast_idx_mask;
   PROTOBUF_ASSUME((idx & 7) == 0);
   auto* fast_entry = table->fast_entry(idx >> 3);
-  data = fast_entry->bits;
+  TcFieldData data = fast_entry->bits;
   data.data ^= coded_tag;
   PROTOBUF_MUSTTAIL return fast_entry->target()(PROTOBUF_TC_PARAM_PASS);
 }
@@ -992,25 +881,23 @@
 // mode. Luckily the structure of the algorithm is such that it's always
 // possible to just return and use the enclosing parse loop as a trampoline.
 inline PROTOBUF_ALWAYS_INLINE const char* TcParser::ToTagDispatch(
-    PROTOBUF_TC_PARAM_DECL) {
+    PROTOBUF_TC_PARAM_NO_DATA_DECL) {
   constexpr bool always_return = !PROTOBUF_TAILCALL;
   if (always_return || !ctx->DataAvailable(ptr)) {
-    PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+    PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
-  PROTOBUF_MUSTTAIL return TagDispatch(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return TagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 inline PROTOBUF_ALWAYS_INLINE const char* TcParser::ToParseLoop(
-    PROTOBUF_TC_PARAM_DECL) {
-  (void)data;
+    PROTOBUF_TC_PARAM_NO_DATA_DECL) {
   (void)ctx;
   SyncHasbits(msg, hasbits, table);
   return ptr;
 }
 
 inline PROTOBUF_ALWAYS_INLINE const char* TcParser::Error(
-    PROTOBUF_TC_PARAM_DECL) {
-  (void)data;
+    PROTOBUF_TC_PARAM_NO_DATA_DECL) {
   (void)ctx;
   (void)ptr;
   SyncHasbits(msg, hasbits, table);
diff --git a/src/google/protobuf/generated_message_tctable_lite.cc b/src/google/protobuf/generated_message_tctable_lite.cc
index 86d067b..95869ff 100644
--- a/src/google/protobuf/generated_message_tctable_lite.cc
+++ b/src/google/protobuf/generated_message_tctable_lite.cc
@@ -37,6 +37,8 @@
 #include "google/protobuf/generated_message_tctable_decl.h"
 #include "google/protobuf/generated_message_tctable_impl.h"
 #include "google/protobuf/inlined_string_field.h"
+#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
+#include "google/protobuf/map.h"
 #include "google/protobuf/message_lite.h"
 #include "google/protobuf/parse_context.h"
 #include "google/protobuf/wire_format_lite.h"
@@ -98,7 +100,7 @@
     // TODO(b/64614992): remove this asm
     asm("" : "+r"(table));
 #endif
-    ptr = TagDispatch(msg, ptr, ctx, {}, table - 1, 0);
+    ptr = TagDispatch(msg, ptr, ctx, TcFieldData::DefaultInit(), table - 1, 0);
     if (ptr == nullptr) break;
     if (ctx->LastTag() != 1) break;  // Ended on terminating tag
   }
@@ -279,7 +281,7 @@
   ptr = ReadTagInlined(ptr, &tag);
   if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) {
     if (export_called_function) *test_out = {Error};
-    return Error(PROTOBUF_TC_PARAM_PASS);
+    PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
 
   auto* entry = FindFieldEntry(table, tag >> 3);
@@ -308,7 +310,7 @@
       &MpPackedFixed,     // FieldKind::kFkPackedFixed
       &MpString<false>,   // FieldKind::kFkString
       &MpMessage<false>,  // FieldKind::kFkMessage
-      &MpFallback,        // FieldKind::kFkMap
+      &MpMap,             // FieldKind::kFkMap
       &Error,             // kSplitMask | FieldKind::kFkNone
       &MpVarint<true>,    // kSplitMask | FieldKind::kFkVarint
       &Error,             // kSplitMask | FieldKind::kFkPackedVarint
@@ -351,8 +353,9 @@
   PROTOBUF_MUSTTAIL return parse_fn(PROTOBUF_TC_PARAM_PASS);
 }
 
-PROTOBUF_NOINLINE const char* TcParser::MiniParse(PROTOBUF_TC_PARAM_DECL) {
-  PROTOBUF_MUSTTAIL return MiniParse<false>(PROTOBUF_TC_PARAM_PASS);
+PROTOBUF_NOINLINE const char* TcParser::MiniParse(
+    PROTOBUF_TC_PARAM_NO_DATA_DECL) {
+  PROTOBUF_MUSTTAIL return MiniParse<false>(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 PROTOBUF_NOINLINE TcParser::TestMiniParseResult TcParser::TestMiniParse(
     PROTOBUF_TC_PARAM_DECL) {
@@ -362,18 +365,18 @@
   return result;
 }
 
-const char* TcParser::MpFallback(PROTOBUF_TC_PARAM_DECL) {
+PROTOBUF_NOINLINE const char* TcParser::MpFallback(PROTOBUF_TC_PARAM_DECL) {
   PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
 }
 
 template <typename TagType>
 const char* TcParser::FastEndGroupImpl(PROTOBUF_TC_PARAM_DECL) {
   if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
-    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
   ctx->SetLastTag(data.decoded_tag());
   ptr += sizeof(TagType);
-  PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 PROTOBUF_NOINLINE const char* TcParser::FastEndG1(PROTOBUF_TC_PARAM_DECL) {
@@ -403,7 +406,7 @@
 inline PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularParseMessageAuxImpl(
     PROTOBUF_TC_PARAM_DECL) {
   if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
-    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
   auto saved_tag = UnalignedLoad<TagType>(ptr);
   ptr += sizeof(TagType);
@@ -478,7 +481,7 @@
 inline PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedParseMessageAuxImpl(
     PROTOBUF_TC_PARAM_DECL) {
   if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
-    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
   const auto expected_tag = UnalignedLoad<TagType>(ptr);
   const auto aux = *table->field_aux(data.aux_idx());
@@ -502,14 +505,14 @@
       }
     }
     if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) {
-      PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_PASS);
+      PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
     }
     if (PROTOBUF_PREDICT_FALSE(!ctx->DataAvailable(ptr))) {
-      PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+      PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_NO_DATA_PASS);
     }
   } while (UnalignedLoad<TagType>(ptr) == expected_tag);
 
-  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 PROTOBUF_NOINLINE const char* TcParser::FastMdR1(PROTOBUF_TC_PARAM_DECL) {
@@ -560,13 +563,13 @@
 PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularFixed(
     PROTOBUF_TC_PARAM_DECL) {
   if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
-    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
   ptr += sizeof(TagType);  // Consume tag
   hasbits |= (uint64_t{1} << data.hasbit_idx());
   RefAt<LayoutType>(msg, data.offset()) = UnalignedLoad<LayoutType>(ptr);
   ptr += sizeof(LayoutType);
-  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 PROTOBUF_NOINLINE const char* TcParser::FastF32S1(PROTOBUF_TC_PARAM_DECL) {
@@ -598,7 +601,7 @@
     if (data.coded_tag<TagType>() == 0) {
       return PackedFixed<LayoutType, TagType>(PROTOBUF_TC_PARAM_PASS);
     } else {
-      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
     }
   }
   auto& field = RefAt<RepeatedField<LayoutType>>(msg, data.offset());
@@ -607,7 +610,7 @@
     field.Add(UnalignedLoad<LayoutType>(ptr + sizeof(TagType)));
     ptr += sizeof(TagType) + sizeof(LayoutType);
   } while (ctx->DataAvailable(ptr) && UnalignedLoad<TagType>(ptr) == tag);
-  return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 PROTOBUF_NOINLINE const char* TcParser::FastF32R1(PROTOBUF_TC_PARAM_DECL) {
@@ -643,7 +646,7 @@
     if (data.coded_tag<TagType>() == 0) {
       return RepeatedFixed<LayoutType, TagType>(PROTOBUF_TC_PARAM_PASS);
     } else {
-      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
     }
   }
   ptr += sizeof(TagType);
@@ -680,6 +683,136 @@
 
 namespace {
 
+// Shift "byte" left by n * 7 bits, filling vacated bits with ones.
+template <int n>
+inline PROTOBUF_ALWAYS_INLINE int64_t shift_left_fill_with_ones(uint64_t byte,
+                                                                uint64_t ones) {
+  return static_cast<int64_t>((byte << (n * 7)) | (ones >> (64 - (n * 7))));
+}
+
+// Shift "byte" left by n * 7 bits, filling vacated bits with ones, and
+// put the new value in res.  Return whether the result was negative.
+template <int n>
+inline PROTOBUF_ALWAYS_INLINE bool shift_left_fill_with_ones_was_negative(
+    uint64_t byte, uint64_t ones, int64_t& res) {
+#if defined(__GCC_ASM_FLAG_OUTPUTS__) && defined(__x86_64__)
+  // For the first two rounds (up to 2 varint bytes), micro benchmarks show a
+  // substantial improvement from capturing the sign from the condition code
+  // register on x86-64.
+  bool sign_bit;
+  asm("shldq %3, %2, %1"
+      : "=@ccs"(sign_bit), "+r"(byte)
+      : "r"(ones), "i"(n * 7));
+  res = static_cast<int64_t>(byte);
+  return sign_bit;
+#else
+  // Generic fallback:
+  res = shift_left_fill_with_ones<n>(byte, ones);
+  return res < 0;
+#endif
+}
+
+template <class VarintType>
+inline PROTOBUF_ALWAYS_INLINE std::pair<const char*, VarintType>
+ParseFallbackPair(const char* p, int64_t res1) {
+  constexpr bool kIs64BitVarint = std::is_same<VarintType, uint64_t>::value;
+  constexpr bool kIs32BitVarint = std::is_same<VarintType, uint32_t>::value;
+  static_assert(kIs64BitVarint || kIs32BitVarint,
+                "Only 32 or 64 bit varints are supported");
+  auto ptr = reinterpret_cast<const int8_t*>(p);
+
+  // The algorithm relies on sign extension for each byte to set all high bits
+  // when the varint continues. It also relies on asserting all of the lower
+  // bits for each successive byte read. This allows the result to be aggregated
+  // using a bitwise AND. For example:
+  //
+  //          8       1          64     57 ... 24     17  16      9  8       1
+  // ptr[0] = 1aaa aaaa ; res1 = 1111 1111 ... 1111 1111  1111 1111  1aaa aaaa
+  // ptr[1] = 1bbb bbbb ; res2 = 1111 1111 ... 1111 1111  11bb bbbb  b111 1111
+  // ptr[2] = 0ccc cccc ; res3 = 0000 0000 ... 000c cccc  cc11 1111  1111 1111
+  //                             ---------------------------------------------
+  //        res1 & res2 & res3 = 0000 0000 ... 000c cccc  ccbb bbbb  baaa aaaa
+  //
+  // On x86-64, a shld from a single register filled with enough 1s in the high
+  // bits can accomplish all this in one instruction. It so happens that res1
+  // has 57 high bits of ones, which is enough for the largest shift done.
+  //
+  // Just as importantly, by keeping results in res1, res2, and res3, we take
+  // advantage of the superscalar abilities of the CPU.
+  ABSL_DCHECK_EQ(res1 >> 7, -1);
+  uint64_t ones = res1;  // save the high 1 bits from res1 (input to SHLD)
+  int64_t res2, res3;    // accumulated result chunks
+
+  if (!shift_left_fill_with_ones_was_negative<1>(ptr[1], ones, res2))
+    goto done2;
+  if (!shift_left_fill_with_ones_was_negative<2>(ptr[2], ones, res3))
+    goto done3;
+
+  // For the remainder of the chunks, check the sign of the AND result.
+  res2 &= shift_left_fill_with_ones<3>(ptr[3], ones);
+  if (res2 >= 0) goto done4;
+  res1 &= shift_left_fill_with_ones<4>(ptr[4], ones);
+  if (res1 >= 0) goto done5;
+  if (kIs64BitVarint) {
+    res2 &= shift_left_fill_with_ones<5>(ptr[5], ones);
+    if (res2 >= 0) goto done6;
+    res3 &= shift_left_fill_with_ones<6>(ptr[6], ones);
+    if (res3 >= 0) goto done7;
+    res1 &= shift_left_fill_with_ones<7>(ptr[7], ones);
+    if (res1 >= 0) goto done8;
+    res3 &= shift_left_fill_with_ones<8>(ptr[8], ones);
+    if (res3 >= 0) goto done9;
+  } else if (kIs32BitVarint) {
+    if (PROTOBUF_PREDICT_TRUE(!(ptr[5] & 0x80))) goto done6;
+    if (PROTOBUF_PREDICT_TRUE(!(ptr[6] & 0x80))) goto done7;
+    if (PROTOBUF_PREDICT_TRUE(!(ptr[7] & 0x80))) goto done8;
+    if (PROTOBUF_PREDICT_TRUE(!(ptr[8] & 0x80))) goto done9;
+  }
+
+  // For valid 64bit varints, the 10th byte/ptr[9] should be exactly 1. In this
+  // case, the continuation bit of ptr[8] already set the top bit of res3
+  // correctly, so all we have to do is check that the expected case is true.
+  if (PROTOBUF_PREDICT_TRUE(kIs64BitVarint && ptr[9] == 1)) goto done10;
+
+  if (PROTOBUF_PREDICT_FALSE(ptr[9] & 0x80)) {
+    // If the continue bit is set, it is an unterminated varint.
+    return {nullptr, 0};
+  }
+
+  // A zero value of the first bit of the 10th byte represents an
+  // over-serialized varint. This case should not happen, but if does (say, due
+  // to a nonconforming serializer), deassert the continuation bit that came
+  // from ptr[8].
+  if (kIs64BitVarint && (ptr[9] & 1) == 0) {
+#if defined(__GCC_ASM_FLAG_OUTPUTS__) && defined(__x86_64__)
+    // Use a small instruction since this is an uncommon code path.
+    asm("btcq $63,%0" : "+r"(res3));
+#else
+    res3 ^= static_cast<uint64_t>(1) << 63;
+#endif
+  }
+  goto done10;
+
+done2:
+  return {p + 2, res1 & res2};
+done3:
+  return {p + 3, res1 & res2 & res3};
+done4:
+  return {p + 4, res1 & res2 & res3};
+done5:
+  return {p + 5, res1 & res2 & res3};
+done6:
+  return {p + 6, res1 & res2 & res3};
+done7:
+  return {p + 7, res1 & res2 & res3};
+done8:
+  return {p + 8, res1 & res2 & res3};
+done9:
+  return {p + 9, res1 & res2 & res3};
+done10:
+  return {p + 10, res1 & res2 & res3};
+}
+
 template <typename Type>
 inline PROTOBUF_ALWAYS_INLINE const char* ParseVarint(const char* p,
                                                       Type* value) {
@@ -792,7 +925,7 @@
 PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularVarint(
     PROTOBUF_TC_PARAM_DECL) {
   if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
-    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
   ptr += sizeof(TagType);  // Consume tag
   hasbits |= (uint64_t{1} << data.hasbit_idx());
@@ -807,7 +940,7 @@
 
   RefAt<FieldType>(msg, data.offset()) =
       ZigZagDecodeHelper<FieldType, zigzag>(static_cast<uint8_t>(*ptr++));
-  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 template <typename FieldType, typename TagType, bool zigzag>
@@ -842,31 +975,86 @@
   hasbits = spill.hasbits;
 
   if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) {
-    return Error(PROTOBUF_TC_PARAM_PASS);
+    PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
   RefAt<FieldType>(msg, data.offset()) =
       ZigZagDecodeHelper<FieldType, zigzag>(tmp);
-  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+}
+
+template <typename FieldType>
+PROTOBUF_ALWAYS_INLINE const char* TcParser::FastVarintS1(
+    PROTOBUF_TC_PARAM_DECL) {
+  using TagType = uint8_t;
+  // super-early success test...
+  if (PROTOBUF_PREDICT_TRUE(((data.data) & 0x80FF) == 0)) {
+    ptr += sizeof(TagType);  // Consume tag
+    hasbits |= (uint64_t{1} << data.hasbit_idx());
+    uint8_t value = data.data >> 8;
+    RefAt<FieldType>(msg, data.offset()) = value;
+    ptr += 1;
+    PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+  }
+  if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+  }
+  ptr += sizeof(TagType);  // Consume tag
+  hasbits |= (uint64_t{1} << data.hasbit_idx());
+
+  auto tmp =
+      ParseFallbackPair<FieldType>(ptr, static_cast<int8_t>(data.data >> 8));
+  ptr = tmp.first;
+  if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) {
+    PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+  }
+
+  RefAt<FieldType>(msg, data.offset()) = tmp.second;
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 PROTOBUF_NOINLINE const char* TcParser::FastV8S1(PROTOBUF_TC_PARAM_DECL) {
-  PROTOBUF_MUSTTAIL return FastTV8S1<-1, -1>(PROTOBUF_TC_PARAM_PASS);
+  using TagType = uint8_t;
+
+  // Special case for a varint bool field with a tag of 1 byte:
+  // The coded_tag() field will actually contain the value too and we can check
+  // both at the same time.
+  auto coded_tag = data.coded_tag<uint16_t>();
+  if (PROTOBUF_PREDICT_TRUE(coded_tag == 0x0000 || coded_tag == 0x0100)) {
+    auto& field = RefAt<bool>(msg, data.offset());
+    // Note: we use `data.data` because Clang generates suboptimal code when
+    // using coded_tag.
+    // In x86_64 this uses the CH register to read the second byte out of
+    // `data`.
+    uint8_t value = data.data >> 8;
+    // The assume allows using a mov instead of test+setne.
+    PROTOBUF_ASSUME(value <= 1);
+    field = static_cast<bool>(value);
+
+    ptr += sizeof(TagType) + 1;  // Consume the tag and the value.
+    hasbits |= (uint64_t{1} << data.hasbit_idx());
+
+    PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+  }
+
+  // If it didn't match above either the tag is wrong, or the value is encoded
+  // non-canonically.
+  // Jump to MiniParse as wrong tag is the most probable reason.
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
+
 PROTOBUF_NOINLINE const char* TcParser::FastV8S2(PROTOBUF_TC_PARAM_DECL) {
   PROTOBUF_MUSTTAIL return SingularVarint<bool, uint16_t>(
       PROTOBUF_TC_PARAM_PASS);
 }
 PROTOBUF_NOINLINE const char* TcParser::FastV32S1(PROTOBUF_TC_PARAM_DECL) {
-  PROTOBUF_MUSTTAIL return SingularVarint<uint32_t, uint8_t>(
-      PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return FastVarintS1<uint32_t>(PROTOBUF_TC_PARAM_PASS);
 }
 PROTOBUF_NOINLINE const char* TcParser::FastV32S2(PROTOBUF_TC_PARAM_DECL) {
   PROTOBUF_MUSTTAIL return SingularVarint<uint32_t, uint16_t>(
       PROTOBUF_TC_PARAM_PASS);
 }
 PROTOBUF_NOINLINE const char* TcParser::FastV64S1(PROTOBUF_TC_PARAM_DECL) {
-  PROTOBUF_MUSTTAIL return SingularVarint<uint64_t, uint8_t>(
-      PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return FastVarintS1<uint64_t>(PROTOBUF_TC_PARAM_PASS);
 }
 PROTOBUF_NOINLINE const char* TcParser::FastV64S2(PROTOBUF_TC_PARAM_DECL) {
   PROTOBUF_MUSTTAIL return SingularVarint<uint64_t, uint16_t>(
@@ -899,7 +1087,7 @@
     if (data.coded_tag<TagType>() == 0) {
       return PackedVarint<FieldType, TagType, zigzag>(PROTOBUF_TC_PARAM_PASS);
     } else {
-      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
     }
   }
   auto& field = RefAt<RepeatedField<FieldType>>(msg, data.offset());
@@ -909,14 +1097,14 @@
     FieldType tmp;
     ptr = ParseVarint(ptr, &tmp);
     if (ptr == nullptr) {
-      return Error(PROTOBUF_TC_PARAM_PASS);
+      PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
     }
     field.Add(ZigZagDecodeHelper<FieldType, zigzag>(tmp));
     if (!ctx->DataAvailable(ptr)) {
       break;
     }
   } while (UnalignedLoad<TagType>(ptr) == expected_tag);
-  return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 PROTOBUF_NOINLINE const char* TcParser::FastV8R1(PROTOBUF_TC_PARAM_DECL) {
@@ -969,7 +1157,7 @@
     if (data.coded_tag<TagType>() == 0) {
       return RepeatedVarint<FieldType, TagType, zigzag>(PROTOBUF_TC_PARAM_PASS);
     } else {
-      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
     }
   }
   ptr += sizeof(TagType);
@@ -1038,30 +1226,48 @@
 
 PROTOBUF_NOINLINE const char* TcParser::FastUnknownEnumFallback(
     PROTOBUF_TC_PARAM_DECL) {
-  // If we know we want to put this field directly into the unknown field set,
-  // then we can skip the call to MiniParse and directly call table->fallback.
-  // However, we first have to update `data` to contain the decoded tag.
+  // Skip MiniParse/fallback and insert the element directly into the unknown
+  // field set. We also normalize the value into an int32 as we do for known
+  // enum values.
   uint32_t tag;
   ptr = ReadTag(ptr, &tag);
   if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) {
-    return Error(PROTOBUF_TC_PARAM_PASS);
+    PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
-  data.data = tag;
-  PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+  uint64_t tmp;
+  ptr = ParseVarint(ptr, &tmp);
+  if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) {
+    PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+  }
+  AddUnknownEnum(msg, table, tag, static_cast<int32_t>(tmp));
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+}
+
+PROTOBUF_NOINLINE const char* TcParser::MpUnknownEnumFallback(
+    PROTOBUF_TC_PARAM_DECL) {
+  // Like FastUnknownEnumFallback, but with the Mp ABI.
+  uint32_t tag = data.tag();
+  uint64_t tmp;
+  ptr = ParseVarint(ptr, &tmp);
+  if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) {
+    PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+  }
+  AddUnknownEnum(msg, table, tag, static_cast<int32_t>(tmp));
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 template <typename TagType, uint16_t xform_val>
 PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularEnum(
     PROTOBUF_TC_PARAM_DECL) {
   if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
-    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
   const char* ptr2 = ptr;  // Save for unknown enum case
   ptr += sizeof(TagType);  // Consume tag
   uint64_t tmp;
   ptr = ParseVarint(ptr, &tmp);
   if (ptr == nullptr) {
-    return Error(PROTOBUF_TC_PARAM_PASS);
+    PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
   const TcParseTableBase::FieldAux aux = *table->field_aux(data.aux_idx());
   if (PROTOBUF_PREDICT_FALSE(
@@ -1071,7 +1277,7 @@
   }
   hasbits |= (uint64_t{1} << data.hasbit_idx());
   RefAt<int32_t>(msg, data.offset()) = tmp;
-  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 PROTOBUF_NOINLINE const char* TcParser::FastErS1(PROTOBUF_TC_PARAM_DECL) {
@@ -1099,7 +1305,7 @@
       PROTOBUF_MUSTTAIL return PackedEnum<TagType, xform_val>(
           PROTOBUF_TC_PARAM_PASS);
     } else {
-      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
     }
   }
   auto& field = RefAt<RepeatedField<int32_t>>(msg, data.offset());
@@ -1111,7 +1317,7 @@
     uint64_t tmp;
     ptr = ParseVarint(ptr, &tmp);
     if (ptr == nullptr) {
-      return Error(PROTOBUF_TC_PARAM_PASS);
+      PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
     }
     if (PROTOBUF_PREDICT_FALSE(
             !EnumIsValidAux(static_cast<int32_t>(tmp), xform_val, aux))) {
@@ -1125,7 +1331,7 @@
       break;
     }
   } while (UnalignedLoad<TagType>(ptr) == expected_tag);
-  return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 const TcParser::UnknownFieldOps& TcParser::GetUnknownFieldOps(
@@ -1139,9 +1345,10 @@
   return *reinterpret_cast<const UnknownFieldOps*>(ptr);
 }
 
-PROTOBUF_NOINLINE void TcParser::UnknownPackedEnum(
-    MessageLite* msg, const TcParseTableBase* table, uint32_t tag,
-    int32_t enum_value) {
+PROTOBUF_NOINLINE void TcParser::AddUnknownEnum(MessageLite* msg,
+                                                const TcParseTableBase* table,
+                                                uint32_t tag,
+                                                int32_t enum_value) {
   GetUnknownFieldOps(table).write_varint(msg, tag >> 3, enum_value);
 }
 
@@ -1153,7 +1360,7 @@
       PROTOBUF_MUSTTAIL return RepeatedEnum<TagType, xform_val>(
           PROTOBUF_TC_PARAM_PASS);
     } else {
-      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
     }
   }
   const auto saved_tag = UnalignedLoad<TagType>(ptr);
@@ -1165,7 +1372,7 @@
   const TcParseTableBase::FieldAux aux = *table->field_aux(data.aux_idx());
   return ctx->ReadPackedVarint(ptr, [=](int32_t value) {
     if (!EnumIsValidAux(value, xform_val, aux)) {
-      UnknownPackedEnum(msg, table, FastDecodeTag(saved_tag), value);
+      AddUnknownEnum(msg, table, FastDecodeTag(saved_tag), value);
     } else {
       field->Add(value);
     }
@@ -1210,18 +1417,18 @@
 PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularEnumSmallRange(
     PROTOBUF_TC_PARAM_DECL) {
   if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
-    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
 
   uint8_t v = ptr[sizeof(TagType)];
   if (PROTOBUF_PREDICT_FALSE(min > v || v > data.aux_idx())) {
-    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
 
   RefAt<int32_t>(msg, data.offset()) = v;
   ptr += sizeof(TagType) + 1;
   hasbits |= (uint64_t{1} << data.hasbit_idx());
-  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 PROTOBUF_NOINLINE const char* TcParser::FastEr0S1(PROTOBUF_TC_PARAM_DECL) {
@@ -1252,7 +1459,7 @@
       PROTOBUF_MUSTTAIL return PackedEnumSmallRange<TagType, min>(
           PROTOBUF_TC_PARAM_PASS);
     } else {
-      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
     }
   }
   auto& field = RefAt<RepeatedField<int32_t>>(msg, data.offset());
@@ -1261,14 +1468,14 @@
   do {
     uint8_t v = ptr[sizeof(TagType)];
     if (PROTOBUF_PREDICT_FALSE(min > v || v > max)) {
-      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
     }
     field.Add(static_cast<int32_t>(v));
     ptr += sizeof(TagType) + 1;
     if (PROTOBUF_PREDICT_FALSE(!ctx->DataAvailable(ptr))) break;
   } while (UnalignedLoad<TagType>(ptr) == expected_tag);
 
-  PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 PROTOBUF_NOINLINE const char* TcParser::FastEr0R1(PROTOBUF_TC_PARAM_DECL) {
@@ -1297,7 +1504,7 @@
       PROTOBUF_MUSTTAIL return RepeatedEnumSmallRange<TagType, min>(
           PROTOBUF_TC_PARAM_PASS);
     } else {
-      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+      PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
     }
   }
 
@@ -1312,7 +1519,7 @@
 
   return ctx->ReadPackedVarint(ptr, [=](int32_t v) {
     if (PROTOBUF_PREDICT_FALSE(min > v || v > max)) {
-      UnknownPackedEnum(msg, table, FastDecodeTag(saved_tag), v);
+      AddUnknownEnum(msg, table, FastDecodeTag(saved_tag), v);
     } else {
       field->Add(v);
     }
@@ -1387,7 +1594,7 @@
 PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularString(
     PROTOBUF_TC_PARAM_DECL) {
   if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
-    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
   auto saved_tag = UnalignedLoad<TagType>(ptr);
   ptr += sizeof(TagType);
@@ -1400,20 +1607,24 @@
   } else {
     ptr = ReadStringNoArena(msg, ptr, ctx, data.aux_idx(), table, field);
   }
-  if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
+  if (ptr == nullptr) {
+    PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+  }
   switch (utf8) {
     case kNoUtf8:
 #ifdef NDEBUG
     case kUtf8ValidateOnly:
 #endif
-      return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+      PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_NO_DATA_PASS);
     default:
       if (PROTOBUF_PREDICT_TRUE(IsValidUTF8(field))) {
-        return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+        PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_NO_DATA_PASS);
       }
       ReportFastUtf8Error(FastDecodeTag(saved_tag), table);
-      return utf8 == kUtf8 ? Error(PROTOBUF_TC_PARAM_PASS)
-                           : ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+      if (utf8 == kUtf8) {
+        PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+      }
+      PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
 }
 
@@ -1447,49 +1658,49 @@
 // Inlined string variants:
 
 const char* TcParser::FastBiS1(PROTOBUF_TC_PARAM_DECL) {
-  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 const char* TcParser::FastBiS2(PROTOBUF_TC_PARAM_DECL) {
-  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 const char* TcParser::FastSiS1(PROTOBUF_TC_PARAM_DECL) {
-  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 const char* TcParser::FastSiS2(PROTOBUF_TC_PARAM_DECL) {
-  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 const char* TcParser::FastUiS1(PROTOBUF_TC_PARAM_DECL) {
-  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 const char* TcParser::FastUiS2(PROTOBUF_TC_PARAM_DECL) {
-  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 // Corded string variants:
 const char* TcParser::FastBcS1(PROTOBUF_TC_PARAM_DECL) {
-  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 const char* TcParser::FastBcS2(PROTOBUF_TC_PARAM_DECL) {
-  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 const char* TcParser::FastScS1(PROTOBUF_TC_PARAM_DECL) {
-  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 const char* TcParser::FastScS2(PROTOBUF_TC_PARAM_DECL) {
-  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 const char* TcParser::FastUcS1(PROTOBUF_TC_PARAM_DECL) {
-  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 const char* TcParser::FastUcS2(PROTOBUF_TC_PARAM_DECL) {
-  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 template <typename TagType, typename FieldType, TcParser::Utf8Type utf8>
 PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedString(
     PROTOBUF_TC_PARAM_DECL) {
   if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
-    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
+    PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
   const auto expected_tag = UnalignedLoad<TagType>(ptr);
   auto& field = RefAt<FieldType>(msg, data.offset());
@@ -1522,7 +1733,7 @@
       ptr = ParseRepeatedStringOnce(ptr, arena, serial_arena, ctx, field);
 
       if (PROTOBUF_PREDICT_FALSE(ptr == nullptr || !validate_last_string())) {
-        return Error(PROTOBUF_TC_PARAM_PASS);
+        PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
       }
       if (!ctx->DataAvailable(ptr)) break;
     } while (UnalignedLoad<TagType>(ptr) == expected_tag);
@@ -1532,12 +1743,12 @@
       std::string* str = field.Add();
       ptr = InlineGreedyStringParser(str, ptr, ctx);
       if (PROTOBUF_PREDICT_FALSE(ptr == nullptr || !validate_last_string())) {
-        return Error(PROTOBUF_TC_PARAM_PASS);
+        PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
       }
       if (!ctx->DataAvailable(ptr)) break;
     } while (UnalignedLoad<TagType>(ptr) == expected_tag);
   }
-  return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 PROTOBUF_NOINLINE const char* TcParser::FastBR1(PROTOBUF_TC_PARAM_DECL) {
@@ -1712,7 +1923,7 @@
     RefAt<uint32_t>(base, entry.offset) = UnalignedLoad<uint32_t>(ptr);
     ptr += sizeof(uint32_t);
   }
-  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 PROTOBUF_NOINLINE const char* TcParser::MpRepeatedFixed(
@@ -1761,7 +1972,7 @@
     } while (next_tag == decoded_tag);
   }
 
-  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 PROTOBUF_NOINLINE const char* TcParser::MpPackedFixed(PROTOBUF_TC_PARAM_DECL) {
@@ -1790,9 +2001,9 @@
   }
 
   if (ptr == nullptr) {
-    return Error(PROTOBUF_TC_PARAM_PASS);
+    PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
-  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 template <bool is_split>
@@ -1817,7 +2028,9 @@
   const char* ptr2 = ptr;  // save for unknown enum case
   uint64_t tmp;
   ptr = ParseVarint(ptr, &tmp);
-  if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
+  if (ptr == nullptr) {
+    PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+  }
 
   // Transform and/or validate the value
   uint16_t rep = type_card & field_layout::kRepMask;
@@ -1829,7 +2042,7 @@
     if (is_validated_enum) {
       if (!EnumIsValidAux(tmp, xform_val, *table->field_aux(&entry))) {
         ptr = ptr2;
-        PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+        PROTOBUF_MUSTTAIL return MpUnknownEnumFallback(PROTOBUF_TC_PARAM_PASS);
       }
     } else if (is_zigzag) {
       tmp = WireFormatLite::ZigZagDecode32(static_cast<uint32_t>(tmp));
@@ -1854,7 +2067,7 @@
     RefAt<bool>(base, entry.offset) = static_cast<bool>(tmp);
   }
 
-  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 PROTOBUF_NOINLINE const char* TcParser::MpRepeatedVarint(
@@ -1884,11 +2097,15 @@
     do {
       uint64_t tmp;
       ptr = ParseVarint(ptr2, &tmp);
-      if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
+      if (ptr == nullptr) {
+        PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+      }
       field.Add(is_zigzag ? WireFormatLite::ZigZagDecode64(tmp) : tmp);
       if (!ctx->DataAvailable(ptr)) break;
       ptr2 = ReadTag(ptr, &next_tag);
-      if (ptr2 == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
+      if (ptr2 == nullptr) {
+        PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+      }
     } while (next_tag == decoded_tag);
   } else if (rep == field_layout::kRep32Bits) {
     auto& field = RefAt<RepeatedField<uint32_t>>(msg, entry.offset);
@@ -1897,11 +2114,14 @@
     do {
       uint64_t tmp;
       ptr = ParseVarint(ptr2, &tmp);
-      if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
+      if (ptr == nullptr) {
+        PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+      }
       if (is_validated_enum) {
         if (!EnumIsValidAux(tmp, xform_val, *table->field_aux(&entry))) {
           ptr = ptr2;
-          PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
+          PROTOBUF_MUSTTAIL return MpUnknownEnumFallback(
+              PROTOBUF_TC_PARAM_PASS);
         }
       } else if (is_zigzag) {
         tmp = WireFormatLite::ZigZagDecode32(tmp);
@@ -1909,7 +2129,9 @@
       field.Add(tmp);
       if (!ctx->DataAvailable(ptr)) break;
       ptr2 = ReadTag(ptr, &next_tag);
-      if (ptr2 == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
+      if (ptr2 == nullptr) {
+        PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+      }
     } while (next_tag == decoded_tag);
   } else {
     ABSL_DCHECK_EQ(rep, static_cast<uint16_t>(field_layout::kRep8Bits));
@@ -1919,15 +2141,19 @@
     do {
       uint64_t tmp;
       ptr = ParseVarint(ptr2, &tmp);
-      if (ptr == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
+      if (ptr == nullptr) {
+        PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+      }
       field.Add(static_cast<bool>(tmp));
       if (!ctx->DataAvailable(ptr)) break;
       ptr2 = ReadTag(ptr, &next_tag);
-      if (ptr2 == nullptr) return Error(PROTOBUF_TC_PARAM_PASS);
+      if (ptr2 == nullptr) {
+        PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+      }
     } while (next_tag == decoded_tag);
   }
 
-  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 PROTOBUF_NOINLINE const char* TcParser::MpPackedVarint(PROTOBUF_TC_PARAM_DECL) {
@@ -1959,7 +2185,7 @@
       const TcParseTableBase::FieldAux aux = *table->field_aux(entry.aux_idx);
       return ctx->ReadPackedVarint(ptr, [=](int32_t value) {
         if (!EnumIsValidAux(value, xform_val, aux)) {
-          UnknownPackedEnum(msg, table, data.tag(), value);
+          AddUnknownEnum(msg, table, data.tag(), value);
         } else {
           field->Add(value);
         }
@@ -1978,7 +2204,7 @@
         ptr, [field](uint64_t value) { field->Add(value); });
   }
 
-  return Error(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 bool TcParser::MpVerifyUtf8(absl::string_view wire_bytes,
@@ -2018,10 +2244,6 @@
   }
   const uint16_t xform_val = type_card & field_layout::kTvMask;
   const uint16_t rep = type_card & field_layout::kRepMask;
-  if (rep == field_layout::kRepIString) {
-    // TODO(b/198211897): support InilnedStringField.
-    PROTOBUF_MUSTTAIL return table->fallback(PROTOBUF_TC_PARAM_PASS);
-  }
 
   // Mark the field as present:
   const bool is_oneof = card == field_layout::kFcOneof;
@@ -2051,14 +2273,15 @@
     }
 
     case field_layout::kRepIString: {
+      ABSL_DCHECK(false);
       break;
     }
   }
 
   if (ptr == nullptr || !is_valid) {
-    return Error(PROTOBUF_TC_PARAM_PASS);
+    PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
   }
-  return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 PROTOBUF_ALWAYS_INLINE const char* TcParser::ParseRepeatedStringOnce(
@@ -2105,7 +2328,7 @@
           if (PROTOBUF_PREDICT_FALSE(ptr == nullptr ||
                                      !MpVerifyUtf8(field[field.size() - 1],
                                                    table, entry, xform_val))) {
-            return Error(PROTOBUF_TC_PARAM_PASS);
+            PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
           }
           if (!ctx->DataAvailable(ptr)) break;
           ptr2 = ReadTag(ptr, &next_tag);
@@ -2118,7 +2341,7 @@
           if (PROTOBUF_PREDICT_FALSE(
                   ptr == nullptr ||
                   !MpVerifyUtf8(*str, table, entry, xform_val))) {
-            return Error(PROTOBUF_TC_PARAM_PASS);
+            PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
           }
           if (!ctx->DataAvailable(ptr)) break;
           ptr2 = ReadTag(ptr, &next_tag);
@@ -2135,7 +2358,7 @@
 #endif
   }
 
-  return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
+  PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_NO_DATA_PASS);
 }
 
 template <bool is_split>
@@ -2271,6 +2494,332 @@
   }
 }
 
+static void SerializeMapKey(const NodeBase* node, MapTypeCard type_card,
+                            io::CodedOutputStream& coded_output) {
+  switch (type_card.wiretype()) {
+    case WireFormatLite::WIRETYPE_VARINT:
+      switch (type_card.cpp_type()) {
+        case MapTypeCard::kBool:
+          WireFormatLite::WriteBool(
+              1, static_cast<const KeyNode<bool>*>(node)->key(), &coded_output);
+          break;
+        case MapTypeCard::k32:
+          if (type_card.is_zigzag()) {
+            WireFormatLite::WriteSInt32(
+                1, static_cast<const KeyNode<uint32_t>*>(node)->key(),
+                &coded_output);
+          } else {
+            WireFormatLite::WriteInt32(
+                1, static_cast<const KeyNode<uint32_t>*>(node)->key(),
+                &coded_output);
+          }
+          break;
+        case MapTypeCard::k64:
+          if (type_card.is_zigzag()) {
+            WireFormatLite::WriteSInt64(
+                1, static_cast<const KeyNode<uint64_t>*>(node)->key(),
+                &coded_output);
+          } else {
+            WireFormatLite::WriteInt64(
+                1, static_cast<const KeyNode<uint64_t>*>(node)->key(),
+                &coded_output);
+          }
+          break;
+        default:
+          PROTOBUF_ASSUME(false);
+      }
+      break;
+    case WireFormatLite::WIRETYPE_FIXED32:
+      WireFormatLite::WriteFixed32(
+          1, static_cast<const KeyNode<uint32_t>*>(node)->key(), &coded_output);
+      break;
+    case WireFormatLite::WIRETYPE_FIXED64:
+      WireFormatLite::WriteFixed64(
+          1, static_cast<const KeyNode<uint64_t>*>(node)->key(), &coded_output);
+      break;
+    case WireFormatLite::WIRETYPE_LENGTH_DELIMITED:
+      // We should never have a message here. They can only be values maps.
+      ABSL_DCHECK_EQ(+type_card.cpp_type(), +MapTypeCard::kString);
+      WireFormatLite::WriteString(
+          1, static_cast<const KeyNode<std::string>*>(node)->key(),
+          &coded_output);
+      break;
+    default:
+      PROTOBUF_ASSUME(false);
+  }
+}
+
+void TcParser::WriteMapEntryAsUnknown(MessageLite* msg,
+                                      const TcParseTableBase* table,
+                                      uint32_t tag, NodeBase* node,
+                                      MapAuxInfo map_info) {
+  std::string serialized;
+  {
+    io::StringOutputStream string_output(&serialized);
+    io::CodedOutputStream coded_output(&string_output);
+    SerializeMapKey(node, map_info.key_type_card, coded_output);
+    // The mapped_type is always an enum here.
+    ABSL_DCHECK(map_info.value_is_validated_enum);
+    WireFormatLite::WriteInt32(2,
+                               *reinterpret_cast<int32_t*>(
+                                   node->GetVoidValue(map_info.node_size_info)),
+                               &coded_output);
+  }
+  GetUnknownFieldOps(table).write_length_delimited(msg, tag >> 3, serialized);
+}
+
+PROTOBUF_ALWAYS_INLINE inline void TcParser::InitializeMapNodeEntry(
+    void* obj, MapTypeCard type_card, UntypedMapBase& map,
+    const TcParseTableBase::FieldAux* aux) {
+  switch (type_card.cpp_type()) {
+    case MapTypeCard::kBool:
+      memset(obj, 0, sizeof(bool));
+      break;
+    case MapTypeCard::k32:
+      memset(obj, 0, sizeof(uint32_t));
+      break;
+    case MapTypeCard::k64:
+      memset(obj, 0, sizeof(uint64_t));
+      break;
+    case MapTypeCard::kString:
+      Arena::CreateInArenaStorage(reinterpret_cast<std::string*>(obj),
+                                  map.arena());
+      break;
+    case MapTypeCard::kMessage:
+      aux[1].create_in_arena(map.arena(), reinterpret_cast<MessageLite*>(obj));
+      break;
+    default:
+      PROTOBUF_ASSUME(false);
+  }
+}
+
+PROTOBUF_NOINLINE void TcParser::DestroyMapNode(NodeBase* node,
+                                                MapAuxInfo map_info,
+                                                UntypedMapBase& map) {
+  if (map_info.key_type_card.cpp_type() == MapTypeCard::kString) {
+    static_cast<std::string*>(node->GetVoidKey())->~basic_string();
+  }
+  if (map_info.value_type_card.cpp_type() == MapTypeCard::kString) {
+    static_cast<std::string*>(node->GetVoidValue(map_info.node_size_info))
+        ->~basic_string();
+  } else if (map_info.value_type_card.cpp_type() == MapTypeCard::kMessage) {
+    static_cast<MessageLite*>(node->GetVoidValue(map_info.node_size_info))
+        ->~MessageLite();
+  }
+  map.DeallocNode(node, map_info.node_size_info);
+}
+
+template <typename T>
+const char* ReadFixed(void* obj, const char* ptr) {
+  auto v = UnalignedLoad<T>(ptr);
+  ptr += sizeof(v);
+  memcpy(obj, &v, sizeof(v));
+  return ptr;
+}
+
+const char* TcParser::ParseOneMapEntry(
+    NodeBase* node, const char* ptr, ParseContext* ctx,
+    const TcParseTableBase::FieldAux* aux, const TcParseTableBase* table,
+    const TcParseTableBase::FieldEntry& entry) {
+  using WFL = WireFormatLite;
+
+  const auto map_info = aux[0].map_info;
+  const uint8_t key_tag = WFL::MakeTag(1, map_info.key_type_card.wiretype());
+  const uint8_t value_tag =
+      WFL::MakeTag(2, map_info.value_type_card.wiretype());
+
+  while (!ctx->Done(&ptr)) {
+    uint32_t inner_tag = ptr[0];
+
+    if (PROTOBUF_PREDICT_FALSE(inner_tag != key_tag &&
+                               inner_tag != value_tag)) {
+      // Do a full parse and check again in case the tag has non-canonical
+      // encoding.
+      ptr = ReadTag(ptr, &inner_tag);
+      if (PROTOBUF_PREDICT_FALSE(inner_tag != key_tag &&
+                                 inner_tag != value_tag)) {
+        if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) return nullptr;
+
+        if (inner_tag == 0 || (inner_tag & 7) == WFL::WIRETYPE_END_GROUP) {
+          ctx->SetLastTag(inner_tag);
+          break;
+        }
+
+        ptr = UnknownFieldParse(inner_tag, nullptr, ptr, ctx);
+        if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) return nullptr;
+        continue;
+      }
+    } else {
+      ++ptr;
+    }
+
+    MapTypeCard type_card;
+    void* obj;
+    if (inner_tag == key_tag) {
+      type_card = map_info.key_type_card;
+      obj = node->GetVoidKey();
+    } else {
+      type_card = map_info.value_type_card;
+      obj = node->GetVoidValue(map_info.node_size_info);
+    }
+
+    switch (type_card.wiretype()) {
+      case WFL::WIRETYPE_VARINT:
+        uint64_t tmp;
+        ptr = ParseVarint(ptr, &tmp);
+        if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) return nullptr;
+        switch (type_card.cpp_type()) {
+          case MapTypeCard::kBool:
+            *reinterpret_cast<bool*>(obj) = static_cast<bool>(tmp);
+            continue;
+          case MapTypeCard::k32: {
+            uint32_t v = static_cast<uint32_t>(tmp);
+            if (type_card.is_zigzag()) v = WFL::ZigZagDecode32(v);
+            memcpy(obj, &v, sizeof(v));
+            continue;
+          }
+          case MapTypeCard::k64:
+            if (type_card.is_zigzag()) tmp = WFL::ZigZagDecode64(tmp);
+            memcpy(obj, &tmp, sizeof(tmp));
+            continue;
+          default:
+            PROTOBUF_ASSUME(false);
+        }
+      case WFL::WIRETYPE_FIXED32:
+        ptr = ReadFixed<uint32_t>(obj, ptr);
+        continue;
+      case WFL::WIRETYPE_FIXED64:
+        ptr = ReadFixed<uint64_t>(obj, ptr);
+        continue;
+      case WFL::WIRETYPE_LENGTH_DELIMITED:
+        if (type_card.cpp_type() == MapTypeCard::kString) {
+          const int size = ReadSize(&ptr);
+          if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) return nullptr;
+          std::string* str = reinterpret_cast<std::string*>(obj);
+          ptr = ctx->ReadString(ptr, size, str);
+          if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) return nullptr;
+          bool do_utf8_check = map_info.fail_on_utf8_failure;
+#ifndef NDEBUG
+          do_utf8_check |= map_info.log_debug_utf8_failure;
+#endif
+          if (type_card.is_utf8() && do_utf8_check &&
+              !utf8_range::IsStructurallyValid(*str)) {
+            PrintUTF8ErrorLog(MessageName(table), FieldName(table, &entry),
+                              "parsing", false);
+            if (map_info.fail_on_utf8_failure) {
+              return nullptr;
+            }
+          }
+          continue;
+        } else {
+          ABSL_DCHECK_EQ(+type_card.cpp_type(), +MapTypeCard::kMessage);
+          ABSL_DCHECK_EQ(inner_tag, value_tag);
+          ptr = ctx->ParseMessage(reinterpret_cast<MessageLite*>(obj), ptr);
+          if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) return nullptr;
+          continue;
+        }
+      default:
+        PROTOBUF_ASSUME(false);
+    }
+  }
+  return ptr;
+}
+
+PROTOBUF_NOINLINE const char* TcParser::MpMap(PROTOBUF_TC_PARAM_DECL) {
+  const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
+  // `aux[0]` points into a MapAuxInfo.
+  // If we have a message mapped_type aux[1] points into a `create_in_arena`.
+  // If we have a validated enum mapped_type aux[1] point into a
+  // `enum_validator`.
+  const auto* aux = table->field_aux(&entry);
+  const auto map_info = aux[0].map_info;
+
+  if (PROTOBUF_PREDICT_FALSE(!map_info.is_supported ||
+                             (data.tag() & 7) !=
+                                 WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) {
+    PROTOBUF_MUSTTAIL return MpFallback(PROTOBUF_TC_PARAM_PASS);
+  }
+
+  // When using LITE, the offset points directly into the Map<> object.
+  // Otherwise, it points into a MapField and we must synchronize with
+  // reflection. It is done by calling the MutableMap() virtual function on the
+  // field's base class.
+  UntypedMapBase& map =
+      map_info.use_lite
+          ? RefAt<UntypedMapBase>(msg, entry.offset)
+          : *RefAt<MapFieldBaseForParse>(msg, entry.offset).MutableMap();
+
+  const uint32_t saved_tag = data.tag();
+
+  while (true) {
+    NodeBase* node = map.AllocNode(map_info.node_size_info);
+
+    InitializeMapNodeEntry(node->GetVoidKey(), map_info.key_type_card, map,
+                           aux);
+    InitializeMapNodeEntry(node->GetVoidValue(map_info.node_size_info),
+                           map_info.value_type_card, map, aux);
+
+    ptr = ctx->ParseLengthDelimitedInlined(ptr, [&](const char* ptr) {
+      return ParseOneMapEntry(node, ptr, ctx, aux, table, entry);
+    });
+
+    if (PROTOBUF_PREDICT_TRUE(ptr != nullptr)) {
+      if (PROTOBUF_PREDICT_FALSE(
+              map_info.value_is_validated_enum &&
+              !aux[1].enum_validator(*static_cast<int32_t*>(
+                  node->GetVoidValue(map_info.node_size_info))))) {
+        WriteMapEntryAsUnknown(msg, table, saved_tag, node, map_info);
+      } else {
+        // Done parsing the node, try to insert it.
+        // If it overwrites something we get old node back to destroy it.
+        switch (map_info.key_type_card.cpp_type()) {
+          case MapTypeCard::kBool:
+            node = static_cast<KeyMapBase<bool>&>(map).InsertOrReplaceNode(
+                static_cast<KeyMapBase<bool>::KeyNode*>(node));
+            break;
+          case MapTypeCard::k32:
+            node = static_cast<KeyMapBase<uint32_t>&>(map).InsertOrReplaceNode(
+                static_cast<KeyMapBase<uint32_t>::KeyNode*>(node));
+            break;
+          case MapTypeCard::k64:
+            node = static_cast<KeyMapBase<uint64_t>&>(map).InsertOrReplaceNode(
+                static_cast<KeyMapBase<uint64_t>::KeyNode*>(node));
+            break;
+          case MapTypeCard::kString:
+            node =
+                static_cast<KeyMapBase<std::string>&>(map).InsertOrReplaceNode(
+                    static_cast<KeyMapBase<std::string>::KeyNode*>(node));
+            break;
+          default:
+            PROTOBUF_ASSUME(false);
+        }
+      }
+    }
+
+    // Destroy the node if we have it.
+    // It could be because we failed to parse, or because insertion returned
+    // an overwritten node.
+    if (PROTOBUF_PREDICT_FALSE(node != nullptr && map.arena() == nullptr)) {
+      DestroyMapNode(node, map_info, map);
+    }
+
+    if (PROTOBUF_PREDICT_FALSE(ptr == nullptr)) {
+      PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+    }
+
+    if (PROTOBUF_PREDICT_FALSE(!ctx->DataAvailable(ptr))) {
+      PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+    }
+
+    uint32_t next_tag;
+    const char* ptr2 = ReadTagInlined(ptr, &next_tag);
+    if (next_tag != saved_tag) break;
+    ptr = ptr2;
+  }
+
+  PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS);
+}
+
 }  // namespace internal
 }  // namespace protobuf
 }  // namespace google
diff --git a/src/google/protobuf/generated_message_tctable_lite_test.cc b/src/google/protobuf/generated_message_tctable_lite_test.cc
index e242d83..12aeced 100644
--- a/src/google/protobuf/generated_message_tctable_lite_test.cc
+++ b/src/google/protobuf/generated_message_tctable_lite_test.cc
@@ -94,7 +94,7 @@
   const TcParseTable<0, 1, 0, 0, 2> parse_table = {
       {
           kHasBitsOffset,  //
-          0, 0, 0,         // no _extensions_
+          0,               // no _extensions_
           1, 0,            // max_field_number, fast_idx_mask
           offsetof(decltype(parse_table), field_lookup_table),
           0xFFFFFFFF - 1,  // skipmap
@@ -125,7 +125,7 @@
   // clang-format on
   uint8_t serialize_buffer[64];
 
-  for (int size : {8, 32, 64, -8, -32, -64}) {
+  for (int size : {8, 32, 64}) {
     SCOPED_TRACE(size);
     auto next_i = [](uint64_t i) {
       // if i + 1 is a power of two, return that.
@@ -192,21 +192,12 @@
           case 8:
             fn = &TcParser::FastV8S1;
             break;
-          case -8:
-            fn = &TcParser::FastTV8S1<kFieldOffset, kHasBitIndex>;
-            break;
           case 32:
             fn = &TcParser::FastV32S1;
             break;
-          case -32:
-            fn = &TcParser::FastTV32S1<uint32_t, kFieldOffset, kHasBitIndex>;
-            break;
           case 64:
             fn = &TcParser::FastV64S1;
             break;
-          case -64:
-            fn = &TcParser::FastTV64S1<uint64_t, kFieldOffset, kHasBitIndex>;
-            break;
         }
         fallback_ptr_received = absl::nullopt;
         fallback_hasbits_received = absl::nullopt;
@@ -215,7 +206,6 @@
                      Xor2SerializedBytes(parse_table.fast_entries[0].bits, ptr),
                      &parse_table.header, /*hasbits=*/0);
         switch (size) {
-          case -8:
           case 8: {
             if (end_ptr == nullptr) {
               // If end_ptr is nullptr, that means the FastParser gave up and
@@ -240,7 +230,6 @@
             EXPECT_EQ(actual_field, static_cast<decltype(actual_field)>(i))  //
                 << " hex: " << absl::StrCat(absl::Hex(actual_field));
           }; break;
-          case -32:
           case 32: {
             ASSERT_TRUE(end_ptr);
             ASSERT_EQ(end_ptr - ptr, serialized.size());
@@ -249,7 +238,6 @@
             EXPECT_EQ(actual_field, static_cast<decltype(actual_field)>(i))  //
                 << " hex: " << absl::StrCat(absl::Hex(actual_field));
           }; break;
-          case -64:
           case 64: {
             ASSERT_EQ(end_ptr - ptr, serialized.size());
 
@@ -297,9 +285,9 @@
   TcParseTable<0, 3, 0, 0, 2> table = {
       // header:
       {
-          0, 0, 0, 0,  // has_bits_offset, extensions
-          0,           // max_field_number
-          0,           // fast_idx_mask,
+          0, 0,  // has_bits_offset, extensions
+          0,     // max_field_number
+          0,     // fast_idx_mask,
           offsetof(decltype(table), field_lookup_table),
           0xFFFFFFFF - 7,  // 7 = fields 1, 2, and 3.
           offsetof(decltype(table), field_names),
@@ -364,9 +352,9 @@
   TcParseTable<0, 5, 0, 0, 8> table = {
       // header:
       {
-          0, 0, 0, 0,  // has_bits_offset, extensions
-          111,         // max_field_number
-          0,           // fast_idx_mask,
+          0, 0,  // has_bits_offset, extensions
+          111,   // max_field_number
+          0,     // fast_idx_mask,
           offsetof(decltype(table), field_lookup_table),
           0xFFFFFFFF - (1 << 1) - (1 << 2)   // fields 2, 3
                      - (1 << 3) - (1 << 4),  // fields 4, 5
@@ -405,9 +393,9 @@
   TcParseTable<0, 6, 0, 0, 8> table = {
       // header:
       {
-          0, 0, 0, 0,  // has_bits_offset, extensions
-          111,         // max_field_number
-          0,           // fast_idx_mask,
+          0, 0,  // has_bits_offset, extensions
+          111,   // max_field_number
+          0,     // fast_idx_mask,
           offsetof(decltype(table), field_lookup_table),
           0xFFFFFFFF - (1<<0) - (1<<2) - (1<<3) - (1<<4) - (1<<6),  // 1,3-5,7
           offsetof(decltype(table), field_entries),
@@ -451,9 +439,9 @@
   TcParseTable<0, 10, 0, 0, 8> table = {
       // header:
       {
-          0, 0, 0, 0,  // has_bits_offset, extensions
-          70,          // max_field_number
-          0,           // fast_idx_mask,
+          0, 0,  // has_bits_offset, extensions
+          70,    // max_field_number
+          0,     // fast_idx_mask,
           offsetof(decltype(table), field_lookup_table),
           0xFFFFFFFF - (1<<0) - (1<<2) - (1<<3) - (1<<4)   // 1, 3, 4, 5, 6
                      - (1<<5) - (1<<7) - (1<<8) - (1<<10)  // 8, 9, 11, 12
@@ -497,9 +485,9 @@
   TcParseTable<0, 3, 0, 15, 2> table = {
       // header:
       {
-          0, 0, 0, 0,  // has_bits_offset, extensions
-          3,           // max_field_number
-          0,           // fast_idx_mask,
+          0, 0,  // has_bits_offset, extensions
+          3,     // max_field_number
+          0,     // fast_idx_mask,
           offsetof(decltype(table), field_lookup_table),
           0xFFFFFFFF - (1<<0) - (1<<1) - (1<<2),  // fields 1, 2, 3
           offsetof(decltype(table), field_entries),
@@ -547,9 +535,9 @@
   TableType table = {
       // header:
       {
-          0, 0, 0, 0,  // has_bits_offset, extensions
-          0,           // max_field_number
-          0,           // fast_idx_mask,
+          0, 0,  // has_bits_offset, extensions
+          0,     // max_field_number
+          0,     // fast_idx_mask,
           offsetof(decltype(table), field_lookup_table),
           0xFFFFFFFF,       // no fields
           offsetof(decltype(table), field_names),  // no field_entries
@@ -598,7 +586,7 @@
 const TcParseTable<5, 134, 5, 2176, 55> test_all_types_table = {
     // header:
     {
-        0, 0, 0, 0,  // has_bits_offset, extensions
+        0, 0,  // has_bits_offset, extensions
         418, 248,    // max_field_number, fast_idx_mask
         offsetof(decltype(test_all_types_table), field_lookup_table),
         977895424,  // skipmap for fields 1-15,18-19,21-22,24-25,27,31-32
diff --git a/src/google/protobuf/lazy_field_heavy.cc b/src/google/protobuf/lazy_field_heavy.cc
new file mode 100644
index 0000000..02d4d2e
--- /dev/null
+++ b/src/google/protobuf/lazy_field_heavy.cc
@@ -0,0 +1,105 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "google/protobuf/lazy_field.h"
+#include "google/protobuf/message.h"
+
+namespace google {
+namespace protobuf {
+namespace internal {
+namespace {
+class ByFactory {
+ public:
+  explicit ByFactory(const Descriptor* type, MessageFactory* factory)
+      : type_(type), factory_(factory) {}
+
+  Message* New(Arena* arena) const {
+    return factory_->GetPrototype(type_)->New(arena);
+  }
+
+  const Message& Default() const { return *factory_->GetPrototype(type_); }
+
+ private:
+  const Descriptor* type_;
+  MessageFactory* factory_;
+};
+
+}  // namespace
+
+const Message& LazyField::GetDynamic(const Descriptor* type,
+                                     MessageFactory* factory,
+                                     Arena* arena) const {
+  return DownCast<const Message&>(GetGeneric(ByFactory(type, factory), arena));
+}
+
+Message* LazyField::MutableDynamic(const Descriptor* type,
+                                   MessageFactory* factory, Arena* arena) {
+  return DownCast<Message*>(
+      MutableGeneric(ByFactory(type, factory), arena, nullptr));
+}
+
+Message* LazyField::ReleaseDynamic(const Descriptor* type,
+                                   MessageFactory* factory, Arena* arena) {
+  return DownCast<Message*>(ReleaseGeneric(ByFactory(type, factory), arena));
+}
+
+Message* LazyField::UnsafeArenaReleaseDynamic(const Descriptor* type,
+                                              MessageFactory* factory,
+                                              Arena* arena) {
+  return DownCast<Message*>(
+      UnsafeArenaReleaseGeneric(ByFactory(type, factory), arena));
+}
+
+size_t LazyField::SpaceUsedExcludingSelfLong() const {
+  // absl::Cord::EstimatedMemoryUsage counts itself that should be excluded
+  // because sizeof(Cord) is already counted in self.
+  size_t total_size = unparsed_.EstimatedMemoryUsage() - sizeof(absl::Cord);
+  switch (GetLogicalState()) {
+    case LogicalState::kClearExposed:
+    case LogicalState::kNoParseRequired:
+    case LogicalState::kDirty:
+    case LogicalState::kParseError: {
+      const auto* message = raw_.load(std::memory_order_relaxed).message();
+      total_size += DownCast<const Message*>(message)->SpaceUsedLong();
+    } break;
+    case LogicalState::kClear:
+    case LogicalState::kParseRequired:
+      // We may have a `Message*` here, but we cannot safely access it
+      // because, a racing SharedInit could delete it out from under us.
+      // Other states in this structure are already passed kSharedInit and are
+      // thus safe.
+      break;  // Nothing to add.
+  }
+  return total_size;
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
diff --git a/src/google/protobuf/map.h b/src/google/protobuf/map.h
index e3bc9ec..99369d3 100644
--- a/src/google/protobuf/map.h
+++ b/src/google/protobuf/map.h
@@ -90,6 +90,8 @@
           WireFormatLite::FieldType value_wire_type>
 class MapField;
 
+struct MapTestPeer;
+
 template <typename Key, typename T>
 class TypeDefinedMapFieldBase;
 
@@ -291,11 +293,30 @@
   using key_arg = K;
 };
 
+enum class MapNodeSizeInfoT : uint32_t;
+inline uint16_t SizeFromInfo(MapNodeSizeInfoT node_size_info) {
+  return static_cast<uint16_t>(static_cast<uint32_t>(node_size_info) >> 16);
+}
+inline uint16_t ValueOffsetFromInfo(MapNodeSizeInfoT node_size_info) {
+  return static_cast<uint16_t>(static_cast<uint32_t>(node_size_info) >> 0);
+}
+constexpr MapNodeSizeInfoT MakeNodeInfo(uint16_t size, uint16_t value_offset) {
+  return static_cast<MapNodeSizeInfoT>((static_cast<uint32_t>(size) << 16) |
+                                       value_offset);
+}
+
 struct NodeBase {
   // Align the node to allow KeyNode to predict the location of the key.
   // This way sizeof(NodeBase) contains any possible padding it was going to
   // have between NodeBase and the key.
   alignas(kMaxMessageAlignment) NodeBase* next;
+
+  void* GetVoidKey() { return this + 1; }
+  const void* GetVoidKey() const { return this + 1; }
+
+  void* GetVoidValue(MapNodeSizeInfoT size_info) {
+    return reinterpret_cast<char*>(this) + ValueOffsetFromInfo(size_info);
+  }
 };
 
 inline NodeBase* EraseFromLinkedList(NodeBase* item, NodeBase* head) {
@@ -474,6 +495,7 @@
 
  protected:
   friend class TcParser;
+  friend struct MapTestPeer;
 
   struct NodeAndBucket {
     NodeBase* node;
@@ -538,11 +560,19 @@
   using AllocFor = absl::allocator_traits<Allocator>::template rebind_alloc<T>;
 
   // Alignment of the nodes is the same as alignment of NodeBase.
+  NodeBase* AllocNode(MapNodeSizeInfoT size_info) {
+    return AllocNode(SizeFromInfo(size_info));
+  }
+
   NodeBase* AllocNode(size_t node_size) {
     PROTOBUF_ASSUME(node_size % sizeof(NodeBase) == 0);
     return AllocFor<NodeBase>(alloc_).allocate(node_size / sizeof(NodeBase));
   }
 
+  void DeallocNode(NodeBase* node, MapNodeSizeInfoT size_info) {
+    DeallocNode(node, SizeFromInfo(size_info));
+  }
+
   void DeallocNode(NodeBase* node, size_t node_size) {
     PROTOBUF_ASSUME(node_size % sizeof(NodeBase) == 0);
     AllocFor<NodeBase>(alloc_).deallocate(node, node_size / sizeof(NodeBase));
@@ -595,6 +625,17 @@
   Allocator alloc_;
 };
 
+// Base class used by TcParser to extract the map object from a map field.
+// We keep it here to avoid a dependency into map_field.h from the main TcParser
+// code, since that would bring in Message too.
+class MapFieldBaseForParse {
+ public:
+  virtual UntypedMapBase* MutableMap() = 0;
+
+ protected:
+  ~MapFieldBaseForParse() = default;
+};
+
 // The value might be of different signedness, so use memcpy to extract it.
 template <typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>
 T ReadKey(const void* ptr) {
@@ -608,6 +649,12 @@
   return *reinterpret_cast<const T*>(ptr);
 }
 
+template <typename Key>
+struct KeyNode : NodeBase {
+  static constexpr size_t kOffset = sizeof(NodeBase);
+  decltype(auto) key() const { return ReadKey<Key>(GetVoidKey()); }
+};
+
 // KeyMapBase is a chaining hash map with the additional feature that some
 // buckets can be converted to use an ordered container.  This ensures O(lg n)
 // bounds on find, insert, and erase, while avoiding the overheads of ordered
@@ -644,12 +691,7 @@
   using UntypedMapBase::UntypedMapBase;
 
  protected:
-  struct KeyNode : NodeBase {
-    static constexpr size_t kOffset = sizeof(NodeBase);
-    decltype(auto) key() const {
-      return ReadKey<Key>(reinterpret_cast<const char*>(this) + kOffset);
-    }
-  };
+  using KeyNode = internal::KeyNode<Key>;
 
   // Trees. The payload type is a copy of Key, so that we can query the tree
   // with Keys that are not in any particular data structure.
@@ -728,6 +770,9 @@
   hasher hash_function() const { return {}; }
 
  protected:
+  friend class TcParser;
+  friend struct MapTestPeer;
+
   PROTOBUF_NOINLINE void erase_no_destroy(size_type b, KeyNode* node) {
     TreeIterator tree_it;
     const bool is_list = revalidate_if_necessary(b, node, &tree_it);
@@ -785,6 +830,25 @@
     return {nullptr, b};
   }
 
+  // Insert the given node.
+  // If the key is a duplicate, it inserts the new node and returns the old one.
+  // Gives ownership to the caller.
+  // If the key is unique, it returns `nullptr`.
+  KeyNode* InsertOrReplaceNode(KeyNode* node) {
+    KeyNode* to_erase = nullptr;
+    auto p = this->FindHelper(node->key());
+    if (p.node != nullptr) {
+      erase_no_destroy(p.bucket, static_cast<KeyNode*>(p.node));
+      to_erase = static_cast<KeyNode*>(p.node);
+    } else if (ResizeIfLoadIsOutOfRange(num_elements_ + 1)) {
+      p = FindHelper(node->key());
+    }
+    const size_type b = p.bucket;  // bucket number
+    InsertUnique(b, node);
+    ++num_elements_;
+    return to_erase;
+  }
+
   // Insert the given Node in bucket b.  If that would make bucket b too big,
   // and bucket b is not a tree, create a tree for buckets b.
   // Requires count(*KeyPtrFromNodePtr(node)) == 0 and that b is the correct
@@ -1417,6 +1481,10 @@
 
   // Linked-list nodes, as one would expect for a chaining hash table.
   struct Node : Base::KeyNode {
+    static constexpr internal::MapNodeSizeInfoT size_info() {
+      return internal::MakeNodeInfo(sizeof(Node),
+                                    PROTOBUF_FIELD_OFFSET(Node, kv.second));
+    }
     value_type kv;
   };
 
@@ -1543,6 +1611,8 @@
             internal::WireFormatLite::FieldType key_wire_type,
             internal::WireFormatLite::FieldType value_wire_type>
   friend class internal::MapFieldLite;
+  friend class internal::TcParser;
+  friend struct internal::MapTestPeer;
 };
 
 namespace internal {
diff --git a/src/google/protobuf/map_field.h b/src/google/protobuf/map_field.h
index 4529b0e..a6f5f90 100644
--- a/src/google/protobuf/map_field.h
+++ b/src/google/protobuf/map_field.h
@@ -336,7 +336,7 @@
 // This class provides access to map field using reflection, which is the same
 // as those provided for RepeatedPtrField<Message>. It is used for internal
 // reflection implementation only. Users should never use this directly.
-class PROTOBUF_EXPORT MapFieldBase {
+class PROTOBUF_EXPORT MapFieldBase : public MapFieldBaseForParse {
  public:
   MapFieldBase()
       : arena_(nullptr), repeated_field_(nullptr), state_(STATE_MODIFIED_MAP) {}
@@ -523,7 +523,9 @@
   bool EqualIterator(const MapIterator& a, const MapIterator& b) const override;
 
   virtual const Map<Key, T>& GetMap() const = 0;
-  virtual Map<Key, T>* MutableMap() = 0;
+  // This overrides the base's method to specialize the signature via
+  // covariance, but we do not yet provide an implementation here, so `= 0`.
+  Map<Key, T>* MutableMap() override = 0;
 
  protected:
   typename Map<Key, T>::const_iterator& InternalGetIterator(
@@ -543,19 +545,19 @@
 // internal generated message implementation only. Users should never use this
 // directly.
 template <typename Derived, typename Key, typename T,
-          WireFormatLite::FieldType kKeyFieldType,
-          WireFormatLite::FieldType kValueFieldType>
+          WireFormatLite::FieldType kKeyFieldType_,
+          WireFormatLite::FieldType kValueFieldType_>
 class MapField : public TypeDefinedMapFieldBase<Key, T> {
   // Provide utilities to parse/serialize key/value.  Provide utilities to
   // manipulate internal stored type.
-  typedef MapTypeHandler<kKeyFieldType, Key> KeyTypeHandler;
-  typedef MapTypeHandler<kValueFieldType, T> ValueTypeHandler;
+  typedef MapTypeHandler<kKeyFieldType_, Key> KeyTypeHandler;
+  typedef MapTypeHandler<kValueFieldType_, T> ValueTypeHandler;
 
   // Define message type for internal repeated field.
   typedef Derived EntryType;
 
   // Define abbreviation for parent MapFieldLite
-  typedef MapFieldLite<Derived, Key, T, kKeyFieldType, kValueFieldType>
+  typedef MapFieldLite<Derived, Key, T, kKeyFieldType_, kValueFieldType_>
       MapFieldLiteType;
 
   // Enum needs to be handled differently from other types because it has
@@ -567,6 +569,8 @@
 
  public:
   typedef Map<Key, T> MapType;
+  static constexpr WireFormatLite::FieldType kKeyFieldType = kKeyFieldType_;
+  static constexpr WireFormatLite::FieldType kValueFieldType = kValueFieldType_;
 
   MapField() : impl_() {}
   MapField(const MapField&) = delete;
diff --git a/src/google/protobuf/map_field_lite.h b/src/google/protobuf/map_field_lite.h
index dd41c18..9ecbf8b 100644
--- a/src/google/protobuf/map_field_lite.h
+++ b/src/google/protobuf/map_field_lite.h
@@ -67,6 +67,8 @@
 
  public:
   typedef Map<Key, T> MapType;
+  static constexpr WireFormatLite::FieldType kKeyFieldType = key_wire_type;
+  static constexpr WireFormatLite::FieldType kValueFieldType = value_wire_type;
 
   constexpr MapFieldLite() : map_() {}
   explicit MapFieldLite(Arena* arena) : map_(arena) {}
diff --git a/src/google/protobuf/map_field_test.cc b/src/google/protobuf/map_field_test.cc
index f988d6b..f7e5b32 100644
--- a/src/google/protobuf/map_field_test.cc
+++ b/src/google/protobuf/map_field_test.cc
@@ -76,6 +76,7 @@
   void SetRepeatedDirty() {
     state_.store(STATE_MODIFIED_REPEATED, std::memory_order_relaxed);
   }
+  UntypedMapBase* MutableMap() override { return nullptr; }
   bool ContainsMapKey(const MapKey& map_key) const override { return false; }
   bool InsertOrLookupMapValue(const MapKey& map_key,
                               MapValueRef* val) override {
diff --git a/src/google/protobuf/map_proto2_unittest.proto b/src/google/protobuf/map_proto2_unittest.proto
index 9cda195..ade970d 100644
--- a/src/google/protobuf/map_proto2_unittest.proto
+++ b/src/google/protobuf/map_proto2_unittest.proto
@@ -55,11 +55,39 @@
 message TestEnumMap {
   map<int32, Proto2MapEnum> known_map_field = 101;
   map<int32, Proto2MapEnum> unknown_map_field = 102;
+
+  // Other maps with all key types to test the unknown entry serialization
+  map<int64, Proto2MapEnum> unknown_map_field_int64 = 200;
+  map<uint64, Proto2MapEnum> unknown_map_field_uint64 = 201;
+  map<int32, Proto2MapEnum> unknown_map_field_int32 = 202;
+  map<uint32, Proto2MapEnum> unknown_map_field_uint32 = 203;
+  map<fixed32, Proto2MapEnum> unknown_map_field_fixed32 = 204;
+  map<fixed64, Proto2MapEnum> unknown_map_field_fixed64 = 205;
+  map<bool, Proto2MapEnum> unknown_map_field_bool = 206;
+  map<string, Proto2MapEnum> unknown_map_field_string = 207;
+  map<sint32, Proto2MapEnum> unknown_map_field_sint32 = 208;
+  map<sint64, Proto2MapEnum> unknown_map_field_sint64 = 209;
+  map<sfixed32, Proto2MapEnum> unknown_map_field_sfixed32 = 210;
+  map<sfixed64, Proto2MapEnum> unknown_map_field_sfixed64 = 211;
 }
 
 message TestEnumMapPlusExtra {
   map<int32, Proto2MapEnumPlusExtra> known_map_field = 101;
   map<int32, Proto2MapEnumPlusExtra> unknown_map_field = 102;
+
+  // Other maps with all key types to test the unknown entry serialization
+  map<int64, Proto2MapEnumPlusExtra> unknown_map_field_int64 = 200;
+  map<uint64, Proto2MapEnumPlusExtra> unknown_map_field_uint64 = 201;
+  map<int32, Proto2MapEnumPlusExtra> unknown_map_field_int32 = 202;
+  map<uint32, Proto2MapEnumPlusExtra> unknown_map_field_uint32 = 203;
+  map<fixed32, Proto2MapEnumPlusExtra> unknown_map_field_fixed32 = 204;
+  map<fixed64, Proto2MapEnumPlusExtra> unknown_map_field_fixed64 = 205;
+  map<bool, Proto2MapEnumPlusExtra> unknown_map_field_bool = 206;
+  map<string, Proto2MapEnumPlusExtra> unknown_map_field_string = 207;
+  map<sint32, Proto2MapEnumPlusExtra> unknown_map_field_sint32 = 208;
+  map<sint64, Proto2MapEnumPlusExtra> unknown_map_field_sint64 = 209;
+  map<sfixed32, Proto2MapEnumPlusExtra> unknown_map_field_sfixed32 = 210;
+  map<sfixed64, Proto2MapEnumPlusExtra> unknown_map_field_sfixed64 = 211;
 }
 
 message TestImportEnumMap {
@@ -90,3 +118,8 @@
 message TestSubmessageMaps {
   optional TestMaps m = 1;
 }
+
+message TestProto2BytesMap {
+  map<int32, bytes> map_bytes = 1;
+  map<int32, string> map_string = 2;
+}
diff --git a/src/google/protobuf/map_proto3_unittest.proto b/src/google/protobuf/map_proto3_unittest.proto
new file mode 100644
index 0000000..cea8ef6
--- /dev/null
+++ b/src/google/protobuf/map_proto3_unittest.proto
@@ -0,0 +1,43 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+// This file contains definitions that have different behavior in proto3.
+
+// We don't put this in a package within proto2 because we need to make sure
+// that the generated code doesn't depend on being in the proto2 namespace.
+// In map_test_util.h we do "using namespace unittest = protobuf_unittest".
+package proto3_unittest;
+
+message TestProto3BytesMap {
+  map<int32, bytes> map_bytes = 1;
+  map<int32, string> map_string = 2;
+}
diff --git a/src/google/protobuf/map_test.inc b/src/google/protobuf/map_test.inc
index 76c5ce8..8cd3cc2 100644
--- a/src/google/protobuf/map_test.inc
+++ b/src/google/protobuf/map_test.inc
@@ -49,6 +49,7 @@
 #include "google/protobuf/testing/googletest.h"
 #include <gtest/gtest.h>
 #include "absl/base/casts.h"
+#include "absl/cleanup/cleanup.h"
 #include "absl/container/btree_set.h"
 #include "absl/container/flat_hash_map.h"
 #include "absl/container/flat_hash_set.h"
@@ -64,6 +65,8 @@
 #include "google/protobuf/io/zero_copy_stream_impl.h"
 #include "google/protobuf/map.h"
 #include "google/protobuf/map_field_inl.h"
+#include "google/protobuf/map_proto2_unittest.pb.h"
+#include "google/protobuf/map_proto3_unittest.pb.h"
 #include "google/protobuf/message.h"
 #include "google/protobuf/reflection.h"
 #include "google/protobuf/reflection_ops.h"
@@ -179,6 +182,28 @@
 template <>
 struct is_internal_map_value_type<CountedInstance> : std::true_type {};
 
+struct MapTestPeer {
+  template <typename T>
+  static typename T::KeyMapBase& GetKeyMapBase(T& value) {
+    return value;
+  }
+
+  template <typename T>
+  static bool InsertOrReplaceNode(T& map, typename T::key_type key,
+                                  typename T::mapped_type value) {
+    using Node = typename T::Node;
+    auto* node = static_cast<Node*>(map.AllocNode(sizeof(Node)));
+    ::new (static_cast<void*>(&node->kv)) typename T::value_type{key, value};
+    node = static_cast<Node*>(GetKeyMapBase(map).InsertOrReplaceNode(node));
+    if (node) {
+      node->~Node();
+      GetKeyMapBase(map).DeallocNode(node, sizeof(Node));
+      return false;
+    }
+    return true;
+  }
+};
+
 namespace {
 using internal::DownCast;
 
@@ -2738,6 +2763,42 @@
   EXPECT_EQ(UNITTEST::PROTO2_MAP_ENUM_FOO, to.known_map_field().at(0));
 }
 
+TEST(GeneratedMapFieldTest, Proto2UnknownEnumAllKeyTypesWork) {
+#define PROTOBUF_INTERNAL_TEST_MAP_KEY_TYPE(Type, Value)                      \
+  {                                                                           \
+    UNITTEST::TestEnumMapPlusExtra from;                                      \
+    auto value = Value;                                                       \
+    ABSL_LOG(INFO) << "Testing " << #Type;                                    \
+    (*from.mutable_unknown_map_field_##Type())[Value] =                       \
+        UNITTEST::E_PROTO2_MAP_ENUM_EXTRA;                                    \
+    UNITTEST::TestEnumMap to;                                                 \
+    ASSERT_TRUE(to.ParseFromString(from.SerializeAsString()));                \
+    EXPECT_EQ(0, to.unknown_map_field_##Type().size())                        \
+        << testing::PrintToString(to.unknown_map_field_##Type());             \
+    const UnknownFieldSet& unknown_field_set =                                \
+        to.GetReflection()->GetUnknownFields(to);                             \
+    EXPECT_EQ(1, unknown_field_set.field_count());                            \
+    from.Clear();                                                             \
+    EXPECT_EQ(from.unknown_map_field_##Type().size(), 0);                     \
+    EXPECT_TRUE(from.ParseFromString(to.SerializeAsString()));                \
+    EXPECT_THAT(from.unknown_map_field_##Type(),                              \
+                ElementsAre(Pair(value, UNITTEST::E_PROTO2_MAP_ENUM_EXTRA))); \
+  }
+
+  PROTOBUF_INTERNAL_TEST_MAP_KEY_TYPE(int64, 17);
+  PROTOBUF_INTERNAL_TEST_MAP_KEY_TYPE(uint64, 17);
+  PROTOBUF_INTERNAL_TEST_MAP_KEY_TYPE(int32, 17);
+  PROTOBUF_INTERNAL_TEST_MAP_KEY_TYPE(uint32, 17);
+  PROTOBUF_INTERNAL_TEST_MAP_KEY_TYPE(fixed32, 17);
+  PROTOBUF_INTERNAL_TEST_MAP_KEY_TYPE(fixed64, 17);
+  PROTOBUF_INTERNAL_TEST_MAP_KEY_TYPE(bool, true);
+  PROTOBUF_INTERNAL_TEST_MAP_KEY_TYPE(string, "17");
+  PROTOBUF_INTERNAL_TEST_MAP_KEY_TYPE(sint32, 17);
+  PROTOBUF_INTERNAL_TEST_MAP_KEY_TYPE(sint64, 17);
+  PROTOBUF_INTERNAL_TEST_MAP_KEY_TYPE(sfixed32, 17);
+  PROTOBUF_INTERNAL_TEST_MAP_KEY_TYPE(sfixed64, 17);
+}
+
 TEST(GeneratedMapFieldTest, StandardWireFormat) {
   UNITTEST::TestMap message;
   std::string data = "\x0A\x04\x08\x01\x10\x01";
@@ -2910,6 +2971,41 @@
   EXPECT_EQ(3, message.map_int32_int32().at(2));
 }
 
+TEST(GeneratedMapFieldTest, ToplevelTagNotLengthPrefixed) {
+  UNITTEST::TestMap message;
+
+  // The toplevel matches a field number, but does not match the wire type.
+  std::string data = "\x08\x81\x04";
+  EXPECT_TRUE(message.ParseFromString(data));
+  EXPECT_EQ(0, message.map_int32_int32().size());
+
+  const UnknownFieldSet& unknown_field_set =
+      message.GetReflection()->GetUnknownFields(message);
+  ASSERT_EQ(1, unknown_field_set.field_count());
+  auto& field = unknown_field_set.field(0);
+  ASSERT_EQ(field.TYPE_VARINT, field.type());
+  EXPECT_EQ(((0x1 << 0) | (0x04 << 7)), field.varint());
+}
+
+TEST(GeneratedMapFieldTest, InnerTagsInLongForm) {
+  UNITTEST::TestMap message;
+
+  // First, control
+  absl::string_view data("\012\004\010\007\020\005", 6);
+  ASSERT_TRUE(message.ParseFromString(data));
+  EXPECT_THAT(message.map_int32_int32(), ElementsAre(Pair(7, 5)));
+
+  // Now we make the key tag long form
+  data = absl::string_view("\012\005\210\000\007\020\005", 7);
+  ASSERT_TRUE(message.ParseFromString(data));
+  EXPECT_THAT(message.map_int32_int32(), ElementsAre(Pair(7, 5)));
+
+  // Now we make the value tag long form
+  data = absl::string_view("\012\005\010\007\220\000\005", 7);
+  ASSERT_TRUE(message.ParseFromString(data));
+  EXPECT_THAT(message.map_int32_int32(), ElementsAre(Pair(7, 5)));
+}
+
 TEST(GeneratedMapFieldTest, CorruptedWireFormat) {
   UNITTEST::TestMap message;
 
@@ -3365,7 +3461,7 @@
 
   message->SerializeToString(&data);
   TestMap to;
-  to.ParseFromString(data);
+  ASSERT_TRUE(to.ParseFromString(data));
   EXPECT_EQ(128, to.map_int32_foreign_message().at(0).c());
 }
 
@@ -4134,6 +4230,63 @@
   EXPECT_EQ(nested_msg43_ptr, &moved_to_map[43].optional_nested_message());
 }
 
+TEST(KeyMapBaseTest, InsertOrReplaceNodeWorks) {
+  using M = Map<int32_t, std::string>;
+  M map;
+  EXPECT_TRUE(MapTestPeer::InsertOrReplaceNode(map, 10, "Foo"));
+  EXPECT_EQ(map.size(), 1);
+  EXPECT_EQ(map[10], "Foo");
+  EXPECT_FALSE(MapTestPeer::InsertOrReplaceNode(map, 10, "Bar"));
+  EXPECT_EQ(map.size(), 1);
+  EXPECT_EQ(map[10], "Bar");
+  map[100] = "BAD";
+  EXPECT_EQ(map.size(), 2);
+  EXPECT_EQ(map[10], "Bar");
+  EXPECT_EQ(map[100], "BAD");
+  EXPECT_FALSE(MapTestPeer::InsertOrReplaceNode(map, 100, "GOOD"));
+  EXPECT_EQ(map.size(), 2);
+  EXPECT_EQ(map[10], "Bar");
+  EXPECT_EQ(map[100], "GOOD");
+}
+
+TEST(NonUtf8Test, StringValuePassesInProto2) {
+  protobuf_unittest::TestProto2BytesMap message;
+  (*message.mutable_map_string())[1] = "\xFF";
+
+  auto serialized = message.SerializeAsString();
+  // Parsing passes, but a failure is logged in debug mode.
+  ASSERT_TRUE(message.ParseFromString(serialized));
+  EXPECT_EQ((*message.mutable_map_string())[1], "\xFF");
+}
+
+TEST(NonUtf8Test, BytesValuePassesInProto2) {
+  protobuf_unittest::TestProto2BytesMap message;
+  (*message.mutable_map_bytes())[1] = "\xFF";
+
+  auto serialized = message.SerializeAsString();
+  ASSERT_TRUE(message.ParseFromString(serialized));
+  EXPECT_EQ((*message.mutable_map_bytes())[1], "\xFF");
+}
+
+TEST(NonUtf8Test, StringValueFailsInProto3) {
+  proto3_unittest::TestProto3BytesMap message;
+  (*message.mutable_map_string())[1] = "\xFF";
+
+  auto serialized = message.SerializeAsString();
+  // It will fail, and log an error.
+  ASSERT_FALSE(message.ParseFromString(serialized));
+}
+
+
+TEST(NonUtf8Test, BytesValuePassesInProto3) {
+  proto3_unittest::TestProto3BytesMap message;
+  (*message.mutable_map_bytes())[1] = "\xFF";
+
+  auto serialized = message.SerializeAsString();
+  ASSERT_TRUE(message.ParseFromString(serialized));
+  EXPECT_EQ((*message.mutable_map_bytes())[1], "\xFF");
+}
+
 }  // namespace
 }  // namespace internal
 }  // namespace protobuf
diff --git a/src/google/protobuf/message.h b/src/google/protobuf/message.h
index ffd639e..645dc3d 100644
--- a/src/google/protobuf/message.h
+++ b/src/google/protobuf/message.h
@@ -118,6 +118,7 @@
 #include "google/protobuf/stubs/common.h"
 #include "google/protobuf/arena.h"
 #include "google/protobuf/port.h"
+#include "absl/base/attributes.h"
 #include "absl/base/call_once.h"
 #include "absl/base/casts.h"
 #include "absl/functional/function_ref.h"
@@ -630,6 +631,7 @@
                                         std::string* scratch) const;
 
 
+
   // Singular field mutators -----------------------------------------
   // These mutate the value of a non-repeated field.
 
@@ -960,6 +962,7 @@
   //       reflection->SetEnumValue(message, field, new_value);
   //     }
   //   }
+  ABSL_DEPRECATED("Use EnumDescriptor::is_closed instead.")
   bool SupportsUnknownEnumValues() const;
 
   // Returns the MessageFactory associated with this message.  This can be
diff --git a/src/google/protobuf/message_unittest.inc b/src/google/protobuf/message_unittest.inc
index 204da69..914e3e9 100644
--- a/src/google/protobuf/message_unittest.inc
+++ b/src/google/protobuf/message_unittest.inc
@@ -61,12 +61,14 @@
 #include "absl/strings/substitute.h"
 #include "google/protobuf/arena.h"
 #include "google/protobuf/descriptor.h"
+#include "google/protobuf/dynamic_message.h"
 #include "google/protobuf/generated_message_reflection.h"
-#include "google/protobuf/message.h"
+#include "google/protobuf/generated_message_tctable_impl.h"
 #include "google/protobuf/io/coded_stream.h"
 #include "google/protobuf/io/io_win32.h"
 #include "google/protobuf/io/zero_copy_stream.h"
 #include "google/protobuf/io/zero_copy_stream_impl.h"
+#include "google/protobuf/message.h"
 #include "google/protobuf/test_util2.h"
 
 
@@ -324,6 +326,20 @@
   EXPECT_NE(parsed.lazy_child().child().payload().optional_int32(), -1);
 }
 
+TEST(MESSAGE_TEST_NAME, ParseFailNonCanonicalZeroTag) {
+  const char encoded[] = {"\n\x3\x80\0\0"};
+  UNITTEST::NestedTestAllTypes parsed;
+  EXPECT_FALSE(parsed.ParsePartialFromString(
+      absl::string_view{encoded, sizeof(encoded) - 1}));
+}
+
+TEST(MESSAGE_TEST_NAME, ParseFailNonCanonicalZeroField) {
+  const char encoded[] = {"\012\x6\205\0\0\0\0\0"};
+  UNITTEST::NestedTestAllTypes parsed;
+  EXPECT_FALSE(parsed.ParsePartialFromString(
+      absl::string_view{encoded, sizeof(encoded) - 1}));
+}
+
 TEST(MESSAGE_TEST_NAME, NestedExplicitLazyExceedRecursionLimit) {
   UNITTEST::NestedTestAllTypes original, parsed;
   // Build proto with recursion depth of 5, with nested annotated LazyField.
@@ -458,6 +474,24 @@
   EXPECT_FALSE(parsed.ParseFromString(data));
 }
 
+TEST(MESSAGE_TEST_NAME, ParseFailsIfRepeatedGroupFieldMalformed) {
+  UNITTEST::TestMutualRecursionA original, parsed;
+  original.mutable_bb()
+      ->mutable_a()
+      ->add_subgroupr()
+      ->mutable_payload()
+      ->set_optional_int64(-1);
+
+  std::string data;
+  ASSERT_TRUE(original.SerializeToString(&data));
+  // Should parse correctly.
+  ASSERT_TRUE(parsed.ParseFromString(data));
+  // Overwriting the last byte of varint (-1) to 0xFF results in malformed wire.
+  data[data.size() - 2] = 0xFF;
+
+  EXPECT_FALSE(parsed.ParseFromString(data));
+}
+
 TEST(MESSAGE_TEST_NAME, UninitializedAndMalformed) {
   UNITTEST::TestRequiredForeign o, p1, p2;
   o.mutable_optional_message()->set_a(-1);
@@ -508,6 +542,8 @@
 TEST(MESSAGE_TEST_NAME, AllSetMethodsOnStringField) {
   UNITTEST::TestAllTypes msg;
 
+  msg.set_optional_string(absl::string_view("Abcdef"));
+  EXPECT_EQ(msg.optional_string(), "Abcdef");
 
   msg.set_optional_string("Asciiz");
   EXPECT_EQ(msg.optional_string(), "Asciiz");
@@ -528,6 +564,38 @@
   EXPECT_EQ(msg.optional_string(), "std::string value 3");
 }
 
+
+TEST(MESSAGE_TEST_NAME, AllAddMethodsOnRepeatedStringField) {
+  UNITTEST::TestAllTypes msg;
+
+  msg.add_repeated_string(absl::string_view("Abcdef"));
+  EXPECT_EQ(msg.repeated_string(0), "Abcdef");
+  msg.clear_repeated_string();
+
+  msg.add_repeated_string("Asciiz");
+  EXPECT_EQ(msg.repeated_string(0), "Asciiz");
+  msg.clear_repeated_string();
+
+  msg.add_repeated_string("Length delimited", 6);
+  EXPECT_EQ(msg.repeated_string(0), "Length");
+  msg.clear_repeated_string();
+
+  std::string value = "std::string value 1";
+  msg.add_repeated_string(value);
+  EXPECT_EQ(msg.repeated_string(0), "std::string value 1");
+  msg.clear_repeated_string();
+
+  value = "std::string value 2";
+  msg.add_repeated_string(std::cref(value));
+  EXPECT_EQ(msg.repeated_string(0), "std::string value 2");
+  msg.clear_repeated_string();
+
+  value = "std::string value 3";
+  msg.add_repeated_string(std::move(value));
+  EXPECT_EQ(msg.repeated_string(0), "std::string value 3");
+  msg.clear_repeated_string();
+}
+
 TEST(MESSAGE_TEST_NAME, SuccessAfterParsingFailure) {
   UNITTEST::NestedTestAllTypes o, p, q;
   constexpr int kDepth = 5;
@@ -1166,6 +1234,31 @@
             std::signbit(out_message.optional_double()));
 }
 
+TEST(MESSAGE_TEST_NAME,
+     RegressionTestForParseMessageReadingUninitializedLimit) {
+  UNITTEST::TestAllTypes in_message;
+  in_message.mutable_optional_nested_message();
+  std::string serialized = in_message.SerializeAsString();
+  // We expect this to have 3 bytes: two for the tag, and one for the zero size.
+  // Break the size by making it overlong.
+  ASSERT_EQ(serialized.size(), 3);
+  serialized.back() = '\200';
+  serialized += std::string(10, '\200');
+  EXPECT_FALSE(in_message.ParseFromString(serialized));
+}
+
+TEST(MESSAGE_TEST_NAME,
+     RegressionTestForParseMessageWithSizeBeyondInputFailsToPopLimit) {
+  UNITTEST::TestAllTypes in_message;
+  in_message.mutable_optional_nested_message();
+  std::string serialized = in_message.SerializeAsString();
+  // We expect this to have 3 bytes: two for the tag, and one for the zero size.
+  // Make the size a valid varint, but it overflows in the input.
+  ASSERT_EQ(serialized.size(), 3);
+  serialized.back() = 10;
+  EXPECT_FALSE(in_message.ParseFromString(serialized));
+}
+
 const uint8_t* SkipTag(const uint8_t* buf) {
   while (*buf & 0x80) ++buf;
   ++buf;
@@ -1225,18 +1318,62 @@
   }
 }
 
+std::string EncodeInt32Value(int number, int32_t value,
+                             int non_canonical_bytes) {
+  uint8_t buf[100];
+  uint8_t* p = buf;
+
+  p = internal::WireFormatLite::WriteInt32ToArray(number, value, p);
+  p = AddNonCanonicalBytes(SkipTag(buf), p, non_canonical_bytes);
+  return std::string(buf, p);
+}
+
+std::string EncodeInt64Value(int number, int64_t value, int non_canonical_bytes,
+                             bool use_packed = false) {
+  uint8_t buf[100];
+  uint8_t* p = buf;
+
+  if (use_packed) {
+    p = internal::WireFormatLite::WriteInt64NoTagToArray(value, p);
+    p = AddNonCanonicalBytes(buf, p, non_canonical_bytes);
+
+    std::string payload(buf, p);
+    p = buf;
+    p = internal::WireFormatLite::WriteStringToArray(number, payload, p);
+    return std::string(buf, p);
+
+  } else {
+    p = internal::WireFormatLite::WriteInt64ToArray(number, value, p);
+    p = AddNonCanonicalBytes(SkipTag(buf), p, non_canonical_bytes);
+    return std::string(buf, p);
+  }
+}
+
 std::string EncodeOtherField() {
   UNITTEST::EnumParseTester obj;
   obj.set_other_field(1);
   return obj.SerializeAsString();
 }
 
+template <typename T>
+static std::vector<const FieldDescriptor*> GetFields() {
+  auto* descriptor = T::descriptor();
+  std::vector<const FieldDescriptor*> fields;
+  for (int i = 0; i < descriptor->field_count(); ++i) {
+    fields.push_back(descriptor->field(i));
+  }
+  for (int i = 0; i < descriptor->extension_count(); ++i) {
+    fields.push_back(descriptor->extension(i));
+  }
+  return fields;
+}
+
 TEST(MESSAGE_TEST_NAME, TestEnumParsers) {
   UNITTEST::EnumParseTester obj;
 
   const auto other_field = EncodeOtherField();
 
-  // Encode a boolean field for many different cases and verify that it can be
+  // Encode an enum field for many different cases and verify that it can be
   // parsed as expected.
   // There are:
   //  - optional/repeated/packed fields
@@ -1246,6 +1383,9 @@
   //  - label combinations to trigger different parsers: sequential, small
   //  sequential, non-validated.
 
+  const std::vector<const FieldDescriptor*> fields =
+      GetFields<UNITTEST::EnumParseTester>();
+
   constexpr int kInvalidValue = 0x900913;
   auto* ref = obj.GetReflection();
   auto* descriptor = obj.descriptor();
@@ -1262,8 +1402,7 @@
             continue;
           }
           SCOPED_TRACE(add_garbage_bits);
-          for (int i = 0; i < descriptor->field_count(); ++i) {
-            const auto* field = descriptor->field(i);
+          for (auto field : fields) {
             if (field->name() == "other_field") continue;
             if (!field->is_repeated() && use_packed) continue;
             SCOPED_TRACE(field->full_name());
@@ -1336,6 +1475,52 @@
   }
 }
 
+TEST(MESSAGE_TEST_NAME, TestEnumParserForUnknownEnumValue) {
+  DynamicMessageFactory factory;
+  std::unique_ptr<Message> dynamic(
+      factory.GetPrototype(UNITTEST::EnumParseTester::descriptor())->New());
+
+  UNITTEST::EnumParseTester non_dynamic;
+
+  // For unknown enum values, for consistency we must include the
+  // int32_t enum value in the unknown field set, which might not be exactly the
+  // same as the input.
+  auto* descriptor = non_dynamic.descriptor();
+
+  const std::vector<const FieldDescriptor*> fields =
+      GetFields<UNITTEST::EnumParseTester>();
+
+  for (bool use_dynamic : {false, true}) {
+    SCOPED_TRACE(use_dynamic);
+    for (auto field : fields) {
+      if (field->name() == "other_field") continue;
+      SCOPED_TRACE(field->full_name());
+      for (bool use_packed : {false, true}) {
+        SCOPED_TRACE(use_packed);
+        if (!field->is_repeated() && use_packed) continue;
+
+        // -2 is an invalid enum value on all the tests here.
+        // We will encode -2 as a positive int64 that is equivalent to
+        // int32_t{-2} when truncated.
+        constexpr int64_t minus_2_non_canonical =
+            static_cast<int64_t>(static_cast<uint32_t>(int32_t{-2}));
+        static_assert(minus_2_non_canonical != -2, "");
+        std::string encoded = EncodeInt64Value(
+            field->number(), minus_2_non_canonical, 0, use_packed);
+
+        auto& obj = use_dynamic ? *dynamic : non_dynamic;
+        ASSERT_TRUE(obj.ParseFromString(encoded));
+
+        auto& unknown = obj.GetReflection()->GetUnknownFields(obj);
+        ASSERT_EQ(unknown.field_count(), 1);
+        EXPECT_EQ(unknown.field(0).number(), field->number());
+        EXPECT_EQ(unknown.field(0).type(), unknown.field(0).TYPE_VARINT);
+        EXPECT_EQ(unknown.field(0).varint(), int64_t{-2});
+      }
+    }
+  }
+}
+
 std::string EncodeBoolValue(int number, bool value, int non_canonical_bytes) {
   uint8_t buf[100];
   uint8_t* p = buf;
@@ -1358,6 +1543,9 @@
   //  - canonical and non-canonical encodings of the varint
   //  - last vs not last field
 
+  const std::vector<const FieldDescriptor*> fields =
+      GetFields<UNITTEST::BoolParseTester>();
+
   auto* ref = obj.GetReflection();
   auto* descriptor = obj.descriptor();
   for (bool use_tail_field : {false, true}) {
@@ -1371,8 +1559,7 @@
           continue;
         }
         SCOPED_TRACE(add_garbage_bits);
-        for (int i = 0; i < descriptor->field_count(); ++i) {
-          const auto* field = descriptor->field(i);
+        for (auto field : fields) {
           if (field->name() == "other_field") continue;
           SCOPED_TRACE(field->full_name());
           for (bool value : {false, true}) {
@@ -1407,16 +1594,6 @@
   }
 }
 
-std::string EncodeInt32Value(int number, int32_t value,
-                             int non_canonical_bytes) {
-  uint8_t buf[100];
-  uint8_t* p = buf;
-
-  p = internal::WireFormatLite::WriteInt32ToArray(number, value, p);
-  p = AddNonCanonicalBytes(SkipTag(buf), p, non_canonical_bytes);
-  return std::string(buf, p);
-}
-
 TEST(MESSAGE_TEST_NAME, TestInt32Parsers) {
   UNITTEST::Int32ParseTester obj;
 
@@ -1430,6 +1607,9 @@
   //  - canonical and non-canonical encodings of the varint
   //  - last vs not last field
 
+  const std::vector<const FieldDescriptor*> fields =
+      GetFields<UNITTEST::Int32ParseTester>();
+
   auto* ref = obj.GetReflection();
   auto* descriptor = obj.descriptor();
   for (bool use_tail_field : {false, true}) {
@@ -1443,8 +1623,7 @@
           continue;
         }
         SCOPED_TRACE(add_garbage_bits);
-        for (int i = 0; i < descriptor->field_count(); ++i) {
-          const auto* field = descriptor->field(i);
+        for (auto field : fields) {
           if (field->name() == "other_field") continue;
           SCOPED_TRACE(field->full_name());
           for (int32_t value : {1, 0, -1, (std::numeric_limits<int32_t>::min)(),
@@ -1480,16 +1659,6 @@
   }
 }
 
-std::string EncodeInt64Value(int number, int64_t value,
-                             int non_canonical_bytes) {
-  uint8_t buf[100];
-  uint8_t* p = buf;
-
-  p = internal::WireFormatLite::WriteInt64ToArray(number, value, p);
-  p = AddNonCanonicalBytes(SkipTag(buf), p, non_canonical_bytes);
-  return std::string(buf, p);
-}
-
 TEST(MESSAGE_TEST_NAME, TestInt64Parsers) {
   UNITTEST::Int64ParseTester obj;
 
@@ -1503,6 +1672,9 @@
   //  - canonical and non-canonical encodings of the varint
   //  - last vs not last field
 
+  const std::vector<const FieldDescriptor*> fields =
+      GetFields<UNITTEST::Int64ParseTester>();
+
   auto* ref = obj.GetReflection();
   auto* descriptor = obj.descriptor();
   for (bool use_tail_field : {false, true}) {
@@ -1516,8 +1688,7 @@
           continue;
         }
         SCOPED_TRACE(add_garbage_bits);
-        for (int i = 0; i < descriptor->field_count(); ++i) {
-          const auto* field = descriptor->field(i);
+        for (auto field : fields) {
           if (field->name() == "other_field") continue;
           SCOPED_TRACE(field->full_name());
           for (int64_t value : {int64_t{1}, int64_t{0}, int64_t{-1},
@@ -1600,6 +1771,60 @@
   size_t break_pos_;
 };
 
+template <typename T>
+static const internal::TcParseTableBase* GetTableIfAvailable(...) {
+  return nullptr;
+}
+
+template <typename T>
+static const internal::TcParseTableBase* GetTableIfAvailable(
+    decltype(internal::TcParser::GetTable<T>())) {
+  return internal::TcParser::GetTable<T>();
+}
+
+TEST(MESSAGE_TEST_NAME, TestRegressionInlinedStringAuxIdxMismatchOnFastParser) {
+  using Proto = UNITTEST::InlinedStringIdxRegressionProto;
+
+  auto* table = GetTableIfAvailable<Proto>(nullptr);
+  // Only test when TDP is on, and we have these fields inlined.
+  if (table != nullptr &&
+      table->fast_entry(1)->target() == internal::TcParser::FastSiS1) {
+    // optional string str1 = 1;
+    EXPECT_EQ(table->fast_entry(1)->bits.aux_idx(), 1);
+    // optional InlinedStringIdxRegressionProto sub = 2;
+    EXPECT_EQ(table->fast_entry(2)->bits.aux_idx(), 2);
+    // optional string str2 = 3;
+    // The aux_idx points to the inlined_string_idx and not the actual aux_idx.
+    EXPECT_EQ(table->fast_entry(3)->bits.aux_idx(), 2);
+    // optional string str3 = 4;
+    // The aux_idx points to the inlined_string_idx and not the actual aux_idx.
+    EXPECT_EQ(table->fast_entry(0)->bits.aux_idx(), 3);
+  }
+
+  std::string encoded;
+  {
+    Proto proto;
+    // We use strings longer than SSO.
+    proto.set_str1(std::string(100, 'a'));
+    proto.set_str2(std::string(100, 'a'));
+    proto.set_str3(std::string(100, 'a'));
+    encoded = proto.SerializeAsString();
+  }
+  Arena arena;
+  auto* proto = Arena::CreateMessage<Proto>(&arena);
+  // We don't alter donation here, so it works even if the idx are bad.
+  ASSERT_TRUE(proto->ParseFromString(encoded));
+  // Now we alter donation bits. str2's bit (#2) will be off, but its aux_idx
+  // (#3) will point to a donated string.
+  proto = Arena::CreateMessage<Proto>(&arena);
+  proto->mutable_str1();
+  proto->mutable_str2();
+  proto->mutable_str3();
+  // With the bug, this breaks the cleanup list, causing UB on arena
+  // destruction.
+  ASSERT_TRUE(proto->ParseFromString(encoded));
+}
+
 TEST(MESSAGE_TEST_NAME, TestRepeatedStringParsers) {
   google::protobuf::Arena arena;
 
@@ -1609,6 +1834,9 @@
 
   const auto* const descriptor = UNITTEST::StringParseTester::descriptor();
 
+  const std::vector<const FieldDescriptor*> fields =
+      GetFields<UNITTEST::StringParseTester>();
+
   static const size_t sso_capacity = std::string().capacity();
   if (sso_capacity == 0) GTEST_SKIP();
   // SSO, !SSO, and off-by-one just in case
@@ -1616,8 +1844,7 @@
        {sso_capacity - 1, sso_capacity, sso_capacity + 1, sso_capacity + 2}) {
     SCOPED_TRACE(size);
     const std::string value = sample.substr(0, size);
-    for (int i = 0; i < descriptor->field_count(); ++i) {
-      const auto* field = descriptor->field(i);
+    for (auto field : fields) {
       SCOPED_TRACE(field->full_name());
       const auto encoded = EncodeStringValue(field->number(), sample) +
                            EncodeStringValue(field->number(), value);
diff --git a/src/google/protobuf/parse_context.cc b/src/google/protobuf/parse_context.cc
index 11d2181..b96e7ac 100644
--- a/src/google/protobuf/parse_context.cc
+++ b/src/google/protobuf/parse_context.cc
@@ -319,26 +319,19 @@
 }
 
 const char* ParseContext::ReadSizeAndPushLimitAndDepth(const char* ptr,
-                                                       int* old_limit) {
-  int size = ReadSize(&ptr);
-  if (PROTOBUF_PREDICT_FALSE(!ptr) || depth_ <= 0) {
-    *old_limit = 0;  // Make sure this isn't uninitialized even on error return
-    return nullptr;
-  }
-  *old_limit = PushLimit(ptr, size);
-  --depth_;
-  return ptr;
+                                                       LimitToken* old_limit) {
+  return ReadSizeAndPushLimitAndDepthInlined(ptr, old_limit);
 }
 
 const char* ParseContext::ParseMessage(MessageLite* msg, const char* ptr) {
-  int old;
+  LimitToken old;
   ptr = ReadSizeAndPushLimitAndDepth(ptr, &old);
   if (ptr == nullptr) return ptr;
   auto old_depth = depth_;
   ptr = msg->_InternalParse(ptr, this);
   if (ptr != nullptr) ABSL_DCHECK_EQ(old_depth, depth_);
   depth_++;
-  if (!PopLimit(old)) return nullptr;
+  if (!PopLimit(std::move(old))) return nullptr;
   return ptr;
 }
 
diff --git a/src/google/protobuf/parse_context.h b/src/google/protobuf/parse_context.h
index 4b82927..a949eaf 100644
--- a/src/google/protobuf/parse_context.h
+++ b/src/google/protobuf/parse_context.h
@@ -36,11 +36,13 @@
 #include <string>
 #include <type_traits>
 
+#include "absl/base/config.h"
 #include "absl/log/absl_check.h"
 #include "absl/log/absl_log.h"
 #include "absl/strings/cord.h"
 #include "absl/strings/internal/resize_uninitialized.h"
 #include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
 #include "google/protobuf/arena.h"
 #include "google/protobuf/arenastring.h"
 #include "google/protobuf/endian.h"
@@ -131,8 +133,53 @@
     if (count > 0) StreamBackUp(count);
   }
 
+#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || defined(ABSL_HAVE_MEMORY_SANITIZER)
+  // In sanitizer mode we use an optional<int> to guarantee that:
+  //  - We do not read an uninitialized token.
+  //  - Every non-empty token is moved from and consumed.
+  class LimitToken {
+   public:
+    LimitToken() = default;
+    explicit LimitToken(int token) : token_(token) {}
+    LimitToken(LimitToken&& other) { *this = std::move(other); }
+    LimitToken& operator=(LimitToken&& other) {
+      token_ = std::exchange(other.token_, absl::nullopt);
+      return *this;
+    }
+
+    ~LimitToken() { ABSL_CHECK(!token_.has_value()); }
+
+    LimitToken(const LimitToken&) = delete;
+    LimitToken& operator=(const LimitToken&) = delete;
+
+    int token() && {
+      ABSL_CHECK(token_.has_value());
+      return *std::exchange(token_, absl::nullopt);
+    }
+
+   private:
+    absl::optional<int> token_;
+  };
+#else
+  class LimitToken {
+   public:
+    LimitToken() = default;
+    explicit LimitToken(int token) : token_(token) {}
+    LimitToken(LimitToken&&) = default;
+    LimitToken& operator=(LimitToken&&) = default;
+
+    LimitToken(const LimitToken&) = delete;
+    LimitToken& operator=(const LimitToken&) = delete;
+
+    int token() const { return token_; }
+
+   private:
+    int token_;
+  };
+#endif
+
   // If return value is negative it's an error
-  PROTOBUF_NODISCARD int PushLimit(const char* ptr, int limit) {
+  PROTOBUF_NODISCARD LimitToken PushLimit(const char* ptr, int limit) {
     ABSL_DCHECK(limit >= 0 && limit <= INT_MAX - kSlopBytes);
     // This add is safe due to the invariant above, because
     // ptr - buffer_end_ <= kSlopBytes.
@@ -140,12 +187,14 @@
     limit_end_ = buffer_end_ + (std::min)(0, limit);
     auto old_limit = limit_;
     limit_ = limit;
-    return old_limit - limit;
+    return LimitToken(old_limit - limit);
   }
 
-  PROTOBUF_NODISCARD bool PopLimit(int delta) {
+  PROTOBUF_NODISCARD bool PopLimit(LimitToken delta) {
+    // We must update the limit first before the early return. Otherwise, we can
+    // end up with an invalid limit and it can lead to integer overflows.
+    limit_ = limit_ + std::move(delta).token();
     if (PROTOBUF_PREDICT_FALSE(!EndedAtLimit())) return false;
-    limit_ = limit_ + delta;
     // TODO(gerbens) We could remove this line and hoist the code to
     // DoneFallback. Study the perf/bin-size effects.
     limit_end_ = buffer_end_ + (std::min)(0, limit_);
@@ -467,16 +516,24 @@
                                     bool>::type = true>
   PROTOBUF_NODISCARD const char* ParseMessage(T* msg, const char* ptr);
 
+  // Read the length prefix, push the new limit, call the func(ptr), and then
+  // pop the limit. Useful for situations that don't value an actual message,
+  // like map entries.
+  template <typename Func>
+  PROTOBUF_NODISCARD const char* ParseLengthDelimitedInlined(const char*,
+                                                             const Func& func);
+
   template <typename TcParser, typename Table>
   PROTOBUF_NODISCARD PROTOBUF_ALWAYS_INLINE const char* ParseMessage(
       MessageLite* msg, const char* ptr, const Table* table) {
-    int old;
+    LimitToken old;
     ptr = ReadSizeAndPushLimitAndDepthInlined(ptr, &old);
+    if (ptr == nullptr) return ptr;
     auto old_depth = depth_;
-    ptr = ptr ? TcParser::ParseLoop(msg, ptr, this, table) : nullptr;
+    ptr = TcParser::ParseLoop(msg, ptr, this, table);
     if (ptr != nullptr) ABSL_DCHECK_EQ(old_depth, depth_);
     depth_++;
-    if (!PopLimit(old)) return nullptr;
+    if (!PopLimit(std::move(old))) return nullptr;
     return ptr;
   }
 
@@ -518,20 +575,20 @@
 
  private:
   // Out-of-line routine to save space in ParseContext::ParseMessage<T>
-  //   int old;
+  //   LimitToken old;
   //   ptr = ReadSizeAndPushLimitAndDepth(ptr, &old)
   // is equivalent to:
   //   int size = ReadSize(&ptr);
   //   if (!ptr) return nullptr;
-  //   int old = PushLimit(ptr, size);
+  //   LimitToken old = PushLimit(ptr, size);
   //   if (--depth_ < 0) return nullptr;
-  PROTOBUF_NODISCARD const char* ReadSizeAndPushLimitAndDepth(const char* ptr,
-                                                              int* old_limit);
+  PROTOBUF_NODISCARD const char* ReadSizeAndPushLimitAndDepth(
+      const char* ptr, LimitToken* old_limit);
 
   // As above, but fully inlined for the cases where we care about performance
   // more than size. eg TcParser.
   PROTOBUF_NODISCARD PROTOBUF_ALWAYS_INLINE const char*
-  ReadSizeAndPushLimitAndDepthInlined(const char* ptr, int* old_limit);
+  ReadSizeAndPushLimitAndDepthInlined(const char* ptr, LimitToken* old_limit);
 
   // The context keeps an internal stack to keep track of the recursive
   // part of the parse state.
@@ -598,6 +655,11 @@
   memcpy(&res, &tmp, sizeof(T));
   return res;
 }
+template <typename T, typename Void,
+          typename = std::enable_if_t<std::is_same<Void, void>::value>>
+T UnalignedLoad(const Void* p) {
+  return UnalignedLoad<T>(reinterpret_cast<const char*>(p));
+}
 
 PROTOBUF_EXPORT
 std::pair<const char*, uint32_t> VarintParseSlow32(const char* p, uint32_t res);
@@ -883,27 +945,37 @@
                           !std::is_base_of<MessageLite, T>::value, bool>::type>
 PROTOBUF_NODISCARD const char* ParseContext::ParseMessage(T* msg,
                                                           const char* ptr) {
-  int old;
+  LimitToken old;
   ptr = ReadSizeAndPushLimitAndDepth(ptr, &old);
   if (ptr == nullptr) return ptr;
   auto old_depth = depth_;
   ptr = msg->_InternalParse(ptr, this);
   if (ptr != nullptr) ABSL_DCHECK_EQ(old_depth, depth_);
   depth_++;
-  if (!PopLimit(old)) return nullptr;
+  if (!PopLimit(std::move(old))) return nullptr;
+  return ptr;
+}
+
+template <typename Func>
+PROTOBUF_NODISCARD PROTOBUF_ALWAYS_INLINE const char*
+ParseContext::ParseLengthDelimitedInlined(const char* ptr, const Func& func) {
+  LimitToken old;
+  ptr = ReadSizeAndPushLimitAndDepthInlined(ptr, &old);
+  if (ptr == nullptr) return ptr;
+  PROTOBUF_ALWAYS_INLINE_CALL ptr = func(ptr);
+  depth_++;
+  if (!PopLimit(std::move(old))) return nullptr;
   return ptr;
 }
 
 inline const char* ParseContext::ReadSizeAndPushLimitAndDepthInlined(
-    const char* ptr, int* old_limit) {
+    const char* ptr, LimitToken* old_limit) {
   int size = ReadSize(&ptr);
-  if (PROTOBUF_PREDICT_FALSE(!ptr)) {
-    // Make sure this isn't uninitialized even on error return
-    *old_limit = 0;
+  if (PROTOBUF_PREDICT_FALSE(!ptr) || depth_ <= 0) {
     return nullptr;
   }
   *old_limit = PushLimit(ptr, size);
-  if (--depth_ < 0) return nullptr;
+  --depth_;
   return ptr;
 }
 
@@ -1122,7 +1194,7 @@
                                                 InternalMetadata* metadata,
                                                 int field_num) {
   return ctx->ReadPackedVarint(
-      ptr, [object, is_valid, metadata, field_num](uint64_t val) {
+      ptr, [object, is_valid, metadata, field_num](int32_t val) {
         if (is_valid(val)) {
           static_cast<RepeatedField<int>*>(object)->Add(val);
         } else {
@@ -1137,7 +1209,7 @@
     bool (*is_valid)(const void*, int), const void* data,
     InternalMetadata* metadata, int field_num) {
   return ctx->ReadPackedVarint(
-      ptr, [object, is_valid, data, metadata, field_num](uint64_t val) {
+      ptr, [object, is_valid, data, metadata, field_num](int32_t val) {
         if (is_valid(data, val)) {
           static_cast<RepeatedField<int>*>(object)->Add(val);
         } else {
diff --git a/src/google/protobuf/port_def.inc b/src/google/protobuf/port_def.inc
index def5de4..d9bf569 100644
--- a/src/google/protobuf/port_def.inc
+++ b/src/google/protobuf/port_def.inc
@@ -213,6 +213,10 @@
 // Owner: shaod@, gberg@
 #define PROTOBUF_FUTURE_DESCRIPTOR_EXTENSION_DECL 1
 
+// Enable cord handling.
+// Owner: mvels@, mkruskal@
+#define PROTOBUF_FUTURE_OPENSOURCE_CORD 1
+
 #endif
 
 #ifdef PROTOBUF_VERSION
@@ -223,12 +227,12 @@
 #ifdef PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC
 #error PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC was previously defined
 #endif
-#define PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC 4022000
+#define PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC 3021000
 
 #ifdef PROTOBUF_MIN_PROTOC_VERSION
 #error PROTOBUF_MIN_PROTOC_VERSION was previously defined
 #endif
-#define PROTOBUF_MIN_PROTOC_VERSION 4022000
+#define PROTOBUF_MIN_PROTOC_VERSION 3021000
 
 #ifdef PROTOBUF_VERSION_SUFFIX
 #error PROTOBUF_VERSION_SUFFIX was previously defined
@@ -261,6 +265,21 @@
 # define PROTOBUF_ALWAYS_INLINE
 #endif
 
+#ifdef PROTOBUF_ALWAYS_INLINE_CALL
+#error PROTOBUF_ALWAYS_INLINE_CALL was previously defined
+#endif
+// For functions we want to force inline from the caller, instead of in the
+// declaration of the callee.
+// This is useful for lambdas where it is not easy to specify ALWAYS_INLINE.
+// Use like:
+//   PROTOBUF_ALWAYS_INLINE_CALL res = SomeFunc(args...);
+#if defined(__clang__) && !defined(PROTOBUF_NO_INLINE_CALL) && \
+    __has_cpp_attribute(clang::always_inline)
+#define PROTOBUF_ALWAYS_INLINE_CALL [[clang::always_inline]]
+#else
+#define PROTOBUF_ALWAYS_INLINE_CALL
+#endif
+
 #ifdef PROTOBUF_NDEBUG_INLINE
 #error PROTOBUF_NDEBUG_INLINE was previously defined
 #endif
@@ -386,16 +405,6 @@
 # define PROTOBUF_DEPRECATED_MSG(msg)
 #endif
 
-#if defined(PROTOBUF_DEPRECATED_ENUM)
-#error PROTOBUF_DEPRECATED_ENUM was previously defined
-#endif
-#if defined(__clang__) || defined(__GNUC__)
-// https://gcc.gnu.org/gcc-6/changes.html
-# define PROTOBUF_DEPRECATED_ENUM __attribute__((deprecated))
-#else
-# define PROTOBUF_DEPRECATED_ENUM
-#endif
-
 #if defined(__clang__)
 #define PROTOBUF_IGNORE_DEPRECATION_START                     \
   _Pragma("clang diagnostic push")                                  \
@@ -433,7 +442,7 @@
 
 // The minimum library version which works with the current version of the
 // headers.
-#define GOOGLE_PROTOBUF_MIN_LIBRARY_VERSION 4022000
+#define GOOGLE_PROTOBUF_MIN_LIBRARY_VERSION 3021000
 
 #ifdef PROTOBUF_RTTI
 #error PROTOBUF_RTTI was previously defined
@@ -856,6 +865,19 @@
 // PROTOBUF_TC_PARAM_PASS passes values to match PROTOBUF_TC_PARAM_DECL.
 #define PROTOBUF_TC_PARAM_PASS msg, ptr, ctx, data, table, hasbits
 
+// PROTOBUF_TC_PARAM_NO_DATA_DECL and PROTOBUF_TC_PARAM_NO_DATA_PASS provide the
+// exact same ABI as above, except that they don't name or pass the `data`
+// argument. Specific functions such as `Error() and `ToTagDispatch()` don't
+// use the `data` argument. By not passing `data` down the call stack, we free
+// up the register holding that value, which may matter in highly optimized
+// functions such as varint parsing.
+#define PROTOBUF_TC_PARAM_NO_DATA_DECL                                        \
+  ::google::protobuf::MessageLite *msg, const char *ptr,                                \
+      ::google::protobuf::internal::ParseContext *ctx, ::google::protobuf::internal::TcFieldData, \
+      const ::google::protobuf::internal::TcParseTableBase *table, uint64_t hasbits
+#define PROTOBUF_TC_PARAM_NO_DATA_PASS \
+  msg, ptr, ctx, ::google::protobuf::internal::TcFieldData::DefaultInit(), table, hasbits
+
 #ifdef PROTOBUF_UNUSED
 #error PROTOBUF_UNUSED was previously defined
 #endif
@@ -1014,6 +1036,13 @@
 //   int index = ...
 //   int value = vec[index];
 #pragma GCC diagnostic ignored "-Wsign-conversion"
+#if __GNUC__ == 12 && __GNUC_MINOR__ < 4
+// Wrong warning emitted when assigning a single char c-string to a std::string
+// in c++20 mode and optimization on.
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105329
+// Planned to be fixed by 12.3 but widen window to 12.4.
+#pragma GCC diagnostic ignored "-Wrestrict"
+#endif
 #endif  // __GNUC__
 
 // Silence some MSVC warnings in all our code.
diff --git a/src/google/protobuf/port_undef.inc b/src/google/protobuf/port_undef.inc
index a0a5f64..2be01b1 100644
--- a/src/google/protobuf/port_undef.inc
+++ b/src/google/protobuf/port_undef.inc
@@ -49,6 +49,7 @@
 #undef PROTOBUF_NAMESPACE
 #undef PROTOBUF_NAMESPACE_ID
 #undef PROTOBUF_ALWAYS_INLINE
+#undef PROTOBUF_ALWAYS_INLINE_CALL
 #undef PROTOBUF_NDEBUG_INLINE
 #undef PROTOBUF_MUSTTAIL
 #undef PROTOBUF_TAILCALL
@@ -56,7 +57,6 @@
 #undef PROTOBUF_NOINLINE
 #undef PROTOBUF_SECTION_VARIABLE
 #undef PROTOBUF_DEPRECATED
-#undef PROTOBUF_DEPRECATED_ENUM
 #undef PROTOBUF_DEPRECATED_MSG
 #undef PROTOBUF_IGNORE_DEPRECATION_START
 #undef PROTOBUF_IGNORE_DEPRECATION_STOP
diff --git a/src/google/protobuf/reflection_mode.cc b/src/google/protobuf/reflection_mode.cc
new file mode 100644
index 0000000..7611c7b
--- /dev/null
+++ b/src/google/protobuf/reflection_mode.cc
@@ -0,0 +1,57 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "google/protobuf/reflection_mode.h"
+
+// Must be included last.
+#include "google/protobuf/port_def.inc"
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+#if !defined(PROTOBUF_NO_THREADLOCAL)
+
+#if defined(PROTOBUF_USE_DLLS)
+ReflectionMode& ScopedReflectionMode::reflection_mode() {
+  static PROTOBUF_THREAD_LOCAL ReflectionMode reflection_mode =
+      ReflectionMode::kDefault;
+  return reflection_mode;
+}
+#else
+PROTOBUF_CONSTINIT PROTOBUF_THREAD_LOCAL ReflectionMode
+    ScopedReflectionMode::reflection_mode_ = ReflectionMode::kDefault;
+#endif
+
+#endif
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
diff --git a/src/google/protobuf/reflection_mode.h b/src/google/protobuf/reflection_mode.h
new file mode 100644
index 0000000..e9ff340
--- /dev/null
+++ b/src/google/protobuf/reflection_mode.h
@@ -0,0 +1,165 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This header provides support for a per thread 'reflection mode'.
+//
+// Some protocol buffer optimizations use interceptors to determine which
+// fields are effectively used in the application. These optimizations are
+// disabled if certain reflection calls are intercepted as the assumption is
+// then that any field data can be accessed.
+//
+// The 'reflection mode' defined in this header is intended to be used by
+// logic such as ad-hoc profilers to indicate that any scoped reflection usage
+// is not originating from, or affecting application code. This reflection mode
+// can then be used by such interceptors to ignore any reflection calls not
+// affecting the application behavior.
+
+#ifndef GOOGLE_PROTOBUF_REFLECTION_MODE_H__
+#define GOOGLE_PROTOBUF_REFLECTION_MODE_H__
+
+#include <cstddef>
+
+// Must be included last.
+#include "google/protobuf/port_def.inc"
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+// The ReflectionModes are ordered in observability levels:
+// kDefault: Lowest level. All reflection calls are observable.
+// kDebugString: Middle level. Only reflection calls in Message::DebugString are
+//               observable.
+// kDiagnostics: Highest level. No reflection calls are observable.
+enum class ReflectionMode {
+  kDefault,
+  kDebugString,
+  kDiagnostics,
+};
+
+// Returns the current ReflectionMode of protobuf for the current thread. This
+// reflection mode can be used by interceptors to ignore any reflection calls
+// not affecting the application behavior.
+// Always returns `kDefault' if the current platform does not support thread
+// local data.
+ReflectionMode GetReflectionMode();
+
+// Scoping class to set the specific ReflectionMode for a given scope.
+class PROTOBUF_EXPORT ScopedReflectionMode final {
+ public:
+  // Sets the current reflection mode, which will be restored at destruction.
+  // The reflection mode can only be 'elevated' in observability levels.
+  // For instance, if the current mode is `kDiagnostics` then scope will remain
+  // unchanged regardless of `mode`.
+  explicit ScopedReflectionMode(ReflectionMode mode);
+
+  // Restores the previous reflection mode.
+  ~ScopedReflectionMode();
+
+  // Returns the scoped ReflectionMode for the current thread.
+  // See `GetReflectionMode()` for more information on purpose and usage.
+  static ReflectionMode current_reflection_mode();
+
+  // ScopedReflectionMode is only intended to be used as a locally scoped
+  // instance to set a reflection mode for the code scoped by this instance.
+  ScopedReflectionMode(const ScopedReflectionMode&) = delete;
+  ScopedReflectionMode& operator=(const ScopedReflectionMode&) = delete;
+
+ private:
+#if !defined(PROTOBUF_NO_THREADLOCAL)
+  const ReflectionMode previous_mode_;
+#if defined(PROTOBUF_USE_DLLS)
+  static ReflectionMode& reflection_mode();
+#else
+  PROTOBUF_CONSTINIT static PROTOBUF_THREAD_LOCAL ReflectionMode
+      reflection_mode_;
+#endif  // PROTOBUF_USE_DLLS
+#endif  // !PROTOBUF_NO_THREADLOCAL
+};
+
+#if !defined(PROTOBUF_NO_THREADLOCAL)
+
+#if defined(PROTOBUF_USE_DLLS)
+
+inline ScopedReflectionMode::ScopedReflectionMode(ReflectionMode mode)
+    : previous_mode_(reflection_mode()) {
+  if (mode > reflection_mode()) {
+    reflection_mode() = mode;
+  }
+}
+
+inline ScopedReflectionMode::~ScopedReflectionMode() {
+  reflection_mode() = previous_mode_;
+}
+
+inline ReflectionMode ScopedReflectionMode::current_reflection_mode() {
+  return reflection_mode();
+}
+
+#else
+
+inline ScopedReflectionMode::ScopedReflectionMode(ReflectionMode mode)
+    : previous_mode_(reflection_mode_) {
+  if (mode > reflection_mode_) {
+    reflection_mode_ = mode;
+  }
+}
+
+inline ScopedReflectionMode::~ScopedReflectionMode() {
+  reflection_mode_ = previous_mode_;
+}
+
+inline ReflectionMode ScopedReflectionMode::current_reflection_mode() {
+  return reflection_mode_;
+}
+
+#endif  // PROTOBUF_USE_DLLS
+
+#else
+
+inline ScopedReflectionMode::ScopedReflectionMode(ReflectionMode mode) {}
+inline ScopedReflectionMode::~ScopedReflectionMode() {}
+inline ReflectionMode ScopedReflectionMode::current_reflection_mode() {
+  return ReflectionMode::kDefault;
+}
+
+#endif  // !PROTOBUF_NO_THREADLOCAL
+
+inline ReflectionMode GetReflectionMode() {
+  return ScopedReflectionMode::current_reflection_mode();
+}
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
+
+#include "google/protobuf/port_undef.inc"
+
+#endif  // GOOGLE_PROTOBUF_REFLECTION_MODE_H__
diff --git a/src/google/protobuf/reflection_mode_test.cc b/src/google/protobuf/reflection_mode_test.cc
new file mode 100644
index 0000000..9320888
--- /dev/null
+++ b/src/google/protobuf/reflection_mode_test.cc
@@ -0,0 +1,115 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include "google/protobuf/reflection_mode.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+#ifndef PROTOBUF_NO_THREADLOCAL
+
+TEST(ReflectionModeTest, SimpleScopedReflection) {
+  ASSERT_EQ(ScopedReflectionMode::current_reflection_mode(),
+            ReflectionMode::kDefault);
+  ScopedReflectionMode scope(ReflectionMode::kDiagnostics);
+  EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
+            ReflectionMode::kDiagnostics);
+}
+
+TEST(ReflectionModeTest, CleanNestedScopedReflection) {
+  ASSERT_EQ(ScopedReflectionMode::current_reflection_mode(),
+            ReflectionMode::kDefault);
+  {
+    ScopedReflectionMode scope1(ReflectionMode::kDebugString);
+    EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
+              ReflectionMode::kDebugString);
+    {
+      ScopedReflectionMode scope2(ReflectionMode::kDiagnostics);
+      EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
+                ReflectionMode::kDiagnostics);
+    }
+    EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
+              ReflectionMode::kDebugString);
+  }
+  EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
+            ReflectionMode::kDefault);
+}
+
+TEST(ReflectionModeTest, UglyNestedScopedReflection) {
+  ASSERT_EQ(ScopedReflectionMode::current_reflection_mode(),
+            ReflectionMode::kDefault);
+  ScopedReflectionMode scope1(ReflectionMode::kDebugString);
+  EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
+            ReflectionMode::kDebugString);
+  ScopedReflectionMode scope2(ReflectionMode::kDiagnostics);
+  EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
+            ReflectionMode::kDiagnostics);
+}
+
+TEST(ReflectionModeTest, DebugStringModeDoesNotReplaceDiagnosticsMode) {
+  ASSERT_EQ(ScopedReflectionMode::current_reflection_mode(),
+            ReflectionMode::kDefault);
+  ScopedReflectionMode scope1(ReflectionMode::kDiagnostics);
+  {
+    ScopedReflectionMode scope2(ReflectionMode::kDebugString);
+    EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
+              ReflectionMode::kDiagnostics);
+  }
+}
+
+#else
+
+TEST(ReflectionModeTest, AlwaysReturnDefaultWhenNoThreadLocal) {
+  ASSERT_EQ(ScopedReflectionMode::current_reflection_mode(),
+            ReflectionMode::kDefault);
+  {
+    ScopedReflectionMode scope1(ReflectionMode::kDebugString);
+    EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
+              ReflectionMode::kDefault);
+    {
+      ScopedReflectionMode scope2(ReflectionMode::kDiagnostics);
+      EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
+                ReflectionMode::kDefault);
+    }
+    EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
+              ReflectionMode::kDefault);
+  }
+  EXPECT_EQ(ScopedReflectionMode::current_reflection_mode(),
+            ReflectionMode::kDefault);
+}
+
+#endif
+
+}  // namespace internal
+}  // namespace protobuf
+}  // namespace google
diff --git a/src/google/protobuf/repeated_field_unittest.cc b/src/google/protobuf/repeated_field_unittest.cc
index 248f01e..f3b4f23 100644
--- a/src/google/protobuf/repeated_field_unittest.cc
+++ b/src/google/protobuf/repeated_field_unittest.cc
@@ -201,34 +201,24 @@
   CheckAllocationSizes<RepeatedField<uint64_t>>(false);
 }
 
-template <typename Rep>
-void CheckNaturalGrowthOnArenasReuseBlocks(bool is_ptr) {
+TEST(RepeatedField, NaturalGrowthOnArenasReuseBlocks) {
   Arena arena;
-  std::vector<Rep*> values;
-  using T = typename Rep::value_type;
+  std::vector<RepeatedField<int>*> values;
 
   static constexpr int kNumFields = 100;
   static constexpr int kNumElems = 1000;
   for (int i = 0; i < kNumFields; ++i) {
-    values.push_back(Arena::CreateMessage<Rep>(&arena));
+    values.push_back(Arena::CreateMessage<RepeatedField<int>>(&arena));
     auto& field = *values.back();
     for (int j = 0; j < kNumElems; ++j) {
-      field.Add(T{});
+      field.Add(j);
     }
   }
 
-  size_t used_bytes_if_reusing =
-      values.size() * values[0]->Capacity() * (is_ptr ? sizeof(T*) : sizeof(T));
-  // Use a 2% slack for other overhead.
-  // If we were not reusing the blocks, the actual value would be ~2x the
-  // expected.
-  EXPECT_THAT(
-      arena.SpaceUsed() - (is_ptr ? sizeof(T) * kNumElems * kNumFields : 0),
-      AllOf(Ge(used_bytes_if_reusing), Le(1.02 * used_bytes_if_reusing)));
-}
-
-TEST(RepeatedField, NaturalGrowthOnArenasReuseBlocks) {
-  CheckNaturalGrowthOnArenasReuseBlocks<RepeatedField<int>>(false);
+  size_t expected = values.size() * values[0]->Capacity() * sizeof(int);
+  // Use a 2% slack for other overhead. If we were not reusing the blocks, the
+  // actual value would be ~2x the expected.
+  EXPECT_THAT(arena.SpaceUsed(), AllOf(Ge(expected), Le(1.02 * expected)));
 }
 
 // Test swapping between various types of RepeatedFields.
@@ -1358,7 +1348,27 @@
 }
 
 TEST(RepeatedPtrField, NaturalGrowthOnArenasReuseBlocks) {
-  CheckNaturalGrowthOnArenasReuseBlocks<RepeatedPtrField<std::string>>(true);
+  using Rep = RepeatedPtrField<std::string>;
+  Arena arena;
+  std::vector<Rep*> values;
+
+  static constexpr int kNumFields = 100;
+  static constexpr int kNumElems = 1000;
+  for (int i = 0; i < kNumFields; ++i) {
+    values.push_back(Arena::CreateMessage<Rep>(&arena));
+    auto& field = *values.back();
+    for (int j = 0; j < kNumElems; ++j) {
+      field.Add("");
+    }
+  }
+
+  size_t expected =
+      values.size() * values[0]->Capacity() * sizeof(std::string*) +
+      sizeof(std::string) * kNumElems * kNumFields;
+  // Use a 2% slack for other overhead.
+  // If we were not reusing the blocks, the actual value would be ~2x the
+  // expected.
+  EXPECT_THAT(arena.SpaceUsed(), AllOf(Ge(expected), Le(1.02 * expected)));
 }
 
 TEST(RepeatedPtrField, AddAndAssignRanges) {
diff --git a/src/google/protobuf/serial_arena.h b/src/google/protobuf/serial_arena.h
index 95767f6..8ac5cdd 100644
--- a/src/google/protobuf/serial_arena.h
+++ b/src/google/protobuf/serial_arena.h
@@ -333,6 +333,19 @@
 
   static size_t FreeStringBlocks(StringBlock* string_block, size_t unused);
 
+  // Adds 'used` to space_used_ in relaxed atomic order.
+  void AddSpaceUsed(size_t space_used) {
+    space_used_.store(space_used_.load(std::memory_order_relaxed) + space_used,
+                      std::memory_order_relaxed);
+  }
+
+  // Adds 'allocated` to space_allocated_ in relaxed atomic order.
+  void AddSpaceAllocated(size_t space_allocated) {
+    space_allocated_.store(
+        space_allocated_.load(std::memory_order_relaxed) + space_allocated,
+        std::memory_order_relaxed);
+  }
+
   // Members are declared here to track sizeof(SerialArena) and hotness
   // centrally. They are (roughly) laid out in descending order of hotness.
 
diff --git a/src/google/protobuf/source_context.pb.h b/src/google/protobuf/source_context.pb.h
index ee2b4ff..c16e410 100644
--- a/src/google/protobuf/source_context.pb.h
+++ b/src/google/protobuf/source_context.pb.h
@@ -9,7 +9,7 @@
 #include <type_traits>
 
 #include "google/protobuf/port_def.inc"
-#if PROTOBUF_VERSION < 4022000
+#if PROTOBUF_VERSION < 3021000
 #error "This file was generated by a newer version of protoc which is"
 #error "incompatible with your Protocol Buffer headers. Please update"
 #error "your headers."
@@ -96,6 +96,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
diff --git a/src/google/protobuf/string_block.h b/src/google/protobuf/string_block.h
index fd7812b..b105d57 100644
--- a/src/google/protobuf/string_block.h
+++ b/src/google/protobuf/string_block.h
@@ -62,11 +62,21 @@
   StringBlock(const StringBlock&) = delete;
   StringBlock& operator=(const StringBlock&) = delete;
 
+  // Returns the size of the next string block based on the size information
+  // stored in `block`. `block` may be null in which case the size of the
+  // initial string block is returned.
+  static size_t NextSize(StringBlock* block);
+
   // Allocates a new StringBlock pointing to `next`, which can be null.
   // The size of the returned block depends on the allocated size of `next`.
   static StringBlock* New(StringBlock* next);
 
-  // Deletes `block`. `block` must not be null.
+  // Allocates a new string block `in place`. `n` must be the value returned
+  // from a previous call to `StringBlock::NextSize(next)`
+  static StringBlock* Emplace(void* p, size_t n, StringBlock* next);
+
+  // Deletes `block` if `block` is heap allocated. `block` must not be null.
+  // Returns the allocated size of `block`, or 0 if the block was emplaced.
   static size_t Delete(StringBlock* block);
 
   StringBlock* next() const;
@@ -86,6 +96,9 @@
   // Returns the total allocation size of this instance.
   size_t allocated_size() const { return allocated_size_; }
 
+  // Returns true if this block is heap allocated, false if emplaced.
+  bool heap_allocated() const { return heap_allocated_; }
+
   // Returns the effective size available for allocation string instances.
   // This value is guaranteed to be a multiple of sizeof(std::string), and
   // guaranteed to never be zero.
@@ -97,21 +110,44 @@
 
   ~StringBlock() = default;
 
-  explicit StringBlock(StringBlock* next, uint32_t size,
+  explicit StringBlock(StringBlock* next, bool heap_allocated, uint32_t size,
                        uint32_t next_size) noexcept
-      : next_(next), allocated_size_(size), next_size_(next_size) {}
+      : next_(next),
+        heap_allocated_(heap_allocated),
+        allocated_size_(size),
+        next_size_(next_size) {}
 
   static constexpr uint32_t min_size() { return size_t{256}; }
   static constexpr uint32_t max_size() { return size_t{8192}; }
 
+  // Returns `size` rounded down such that we can fit a perfect number
+  // of std::string instances inside a StringBlock of that size.
+  static constexpr uint32_t RoundedSize(uint32_t size);
+
   // Returns the size of the next block.
   size_t next_size() const { return next_size_; }
 
   StringBlock* const next_;
-  const uint32_t allocated_size_;
+  const bool heap_allocated_ : 1;
+  const uint32_t allocated_size_ : 31;
   const uint32_t next_size_;
 };
 
+constexpr uint32_t StringBlock::RoundedSize(uint32_t size) {
+  return size - (size - sizeof(StringBlock)) % sizeof(std::string);
+}
+
+inline size_t StringBlock::NextSize(StringBlock* block) {
+  return block ? block->next_size() : min_size();
+}
+
+inline StringBlock* StringBlock::Emplace(void* p, size_t n, StringBlock* next) {
+  ABSL_DCHECK_EQ(n, NextSize(next));
+  uint32_t doubled = static_cast<uint32_t>(n) * 2;
+  uint32_t next_size = next ? std::min(doubled, max_size()) : min_size();
+  return new (p) StringBlock(next, false, RoundedSize(n), next_size);
+}
+
 inline StringBlock* StringBlock::New(StringBlock* next) {
   // Compute required size, rounding down to a multiple of sizeof(std:string)
   // so that we can optimize the allocation path. I.e., we incur a (constant
@@ -122,13 +158,14 @@
     size = next->next_size_;
     next_size = std::min(size * 2, max_size());
   }
-  size -= (size - sizeof(StringBlock)) % sizeof(std::string);
+  size = RoundedSize(size);
   void* p = ::operator new(size);
-  return new (p) StringBlock(next, size, next_size);
+  return new (p) StringBlock(next, true, size, next_size);
 }
 
 inline size_t StringBlock::Delete(StringBlock* block) {
   ABSL_DCHECK(block != nullptr);
+  if (!block->heap_allocated_) return size_t{0};
   size_t size = block->allocated_size();
   internal::SizedDelete(block, size);
   return size;
diff --git a/src/google/protobuf/string_block_test.cc b/src/google/protobuf/string_block_test.cc
index ff1c71f..3c4562d 100644
--- a/src/google/protobuf/string_block_test.cc
+++ b/src/google/protobuf/string_block_test.cc
@@ -33,7 +33,9 @@
 #include "google/protobuf/string_block.h"
 
 #include <cstddef>
+#include <memory>
 #include <string>
+#include <vector>
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -58,10 +60,12 @@
   return EffectiveSizeFor(size) + sizeof(StringBlock);
 }
 
-TEST(StringBlockTest, OneNewBlock) {
+TEST(StringBlockTest, HeapAllocateOneBlock) {
   StringBlock* block = StringBlock::New(nullptr);
+
   ASSERT_THAT(block, Ne(nullptr));
   EXPECT_THAT(block->next(), Eq(nullptr));
+  ASSERT_TRUE(block->heap_allocated());
   EXPECT_THAT(block->allocated_size(), Eq(AllocatedSizeFor(256)));
   EXPECT_THAT(block->effective_size(), Eq(EffectiveSizeFor(256)));
   EXPECT_THAT(block->begin(), Eq(block->AtOffset(0)));
@@ -70,7 +74,26 @@
   EXPECT_THAT(StringBlock::Delete(block), Eq(AllocatedSizeFor(256)));
 }
 
-TEST(StringBlockTest, NewBlocks) {
+TEST(StringBlockTest, EmplaceOneBlock) {
+  // NextSize() returns unrounded 'min_size()' on first call.
+  size_t size = StringBlock::NextSize(nullptr);
+  EXPECT_THAT(size, Eq(256));
+
+  auto buffer = std::make_unique<char[]>(size);
+  StringBlock* block = StringBlock::Emplace(buffer.get(), size, nullptr);
+
+  ASSERT_THAT(block, Ne(nullptr));
+  EXPECT_THAT(block->next(), Eq(nullptr));
+  ASSERT_FALSE(block->heap_allocated());
+  EXPECT_THAT(block->allocated_size(), Eq(AllocatedSizeFor(256)));
+  EXPECT_THAT(block->effective_size(), Eq(EffectiveSizeFor(256)));
+  EXPECT_THAT(block->begin(), Eq(block->AtOffset(0)));
+  EXPECT_THAT(block->end(), Eq(block->AtOffset(block->effective_size())));
+
+  EXPECT_THAT(StringBlock::Delete(block), Eq(0));
+}
+
+TEST(StringBlockTest, HeapAllocateMultipleBlocks) {
   // Note: first two blocks are 256
   StringBlock* previous = StringBlock::New(nullptr);
 
@@ -78,6 +101,7 @@
     StringBlock* block = StringBlock::New(previous);
     ASSERT_THAT(block, Ne(nullptr));
     ASSERT_THAT(block->next(), Eq(previous));
+    ASSERT_TRUE(block->heap_allocated());
     ASSERT_THAT(block->allocated_size(), Eq(AllocatedSizeFor(size)));
     ASSERT_THAT(block->effective_size(), Eq(EffectiveSizeFor(size)));
     ASSERT_THAT(block->begin(), Eq(block->AtOffset(0)));
@@ -88,7 +112,8 @@
   // Capped at 8K from here on
   StringBlock* block = StringBlock::New(previous);
   ASSERT_THAT(block, Ne(nullptr));
-  EXPECT_THAT(block->next(), Eq(previous));
+  ASSERT_THAT(block->next(), Eq(previous));
+  ASSERT_TRUE(block->heap_allocated());
   ASSERT_THAT(block->allocated_size(), Eq(AllocatedSizeFor(8192)));
   ASSERT_THAT(block->effective_size(), Eq(EffectiveSizeFor(8192)));
   ASSERT_THAT(block->begin(), Eq(block->AtOffset(0)));
@@ -102,6 +127,48 @@
   }
 }
 
+TEST(StringBlockTest, EmplaceMultipleBlocks) {
+  std::vector<std::unique_ptr<char[]>> buffers;
+
+  // Convenience lambda to allocate a buffer and invoke Emplace on it.
+  auto EmplaceBlock = [&](StringBlock* previous) {
+    size_t size = StringBlock::NextSize(previous);
+    buffers.push_back(std::make_unique<char[]>(size));
+    return StringBlock::Emplace(buffers.back().get(), size, previous);
+  };
+
+  // Note: first two blocks are 256
+  StringBlock* previous = EmplaceBlock(nullptr);
+
+  for (int size = 256; size <= 8192; size *= 2) {
+    StringBlock* block = EmplaceBlock(previous);
+    ASSERT_THAT(block, Ne(nullptr));
+    ASSERT_THAT(block->next(), Eq(previous));
+    ASSERT_FALSE(block->heap_allocated());
+    ASSERT_THAT(block->allocated_size(), Eq(AllocatedSizeFor(size)));
+    ASSERT_THAT(block->effective_size(), Eq(EffectiveSizeFor(size)));
+    ASSERT_THAT(block->begin(), Eq(block->AtOffset(0)));
+    ASSERT_THAT(block->end(), Eq(block->AtOffset(block->effective_size())));
+    previous = block;
+  }
+
+  // Capped at 8K from here on
+  StringBlock* block = EmplaceBlock(previous);
+  ASSERT_THAT(block, Ne(nullptr));
+  EXPECT_THAT(block->next(), Eq(previous));
+  ASSERT_FALSE(block->heap_allocated());
+  ASSERT_THAT(block->allocated_size(), Eq(AllocatedSizeFor(8192)));
+  ASSERT_THAT(block->effective_size(), Eq(EffectiveSizeFor(8192)));
+  ASSERT_THAT(block->begin(), Eq(block->AtOffset(0)));
+  ASSERT_THAT(block->end(), Eq(block->AtOffset(block->effective_size())));
+
+  while (block) {
+    StringBlock* next = block->next();
+    EXPECT_THAT(StringBlock::Delete(block), Eq(0));
+    block = next;
+  }
+}
+
 }  // namespace
 }  // namespace internal
 }  // namespace protobuf
diff --git a/src/google/protobuf/struct.pb.cc b/src/google/protobuf/struct.pb.cc
index a5fa0b8..b1f1f4a 100644
--- a/src/google/protobuf/struct.pb.cc
+++ b/src/google/protobuf/struct.pb.cc
@@ -618,7 +618,7 @@
       // .google.protobuf.NullValue null_value = 1;
       case 1:
         if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 8)) {
-          ::uint32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          ::int32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
           CHK_(ptr);
           _internal_set_null_value(static_cast<::PROTOBUF_NAMESPACE_ID::NullValue>(val));
         } else {
@@ -701,49 +701,46 @@
   ::uint32_t cached_has_bits = 0;
   (void) cached_has_bits;
 
-  // .google.protobuf.NullValue null_value = 1;
-  if (kind_case() == kNullValue) {
-    target = stream->EnsureSpace(target);
-    target = ::_pbi::WireFormatLite::WriteEnumToArray(
-        1, this->_internal_null_value(), target);
+  switch (kind_case()) {
+    case kNullValue: {
+      target = stream->EnsureSpace(target);
+      target = ::_pbi::WireFormatLite::WriteEnumToArray(
+          1, this->_internal_null_value(), target);
+      break;
+    }
+    case kNumberValue: {
+      target = stream->EnsureSpace(target);
+      target = ::_pbi::WireFormatLite::WriteDoubleToArray(
+          2, this->_internal_number_value(), target);
+      break;
+    }
+    case kStringValue: {
+      const std::string& _s = this->_internal_string_value();
+      ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+          _s.data(), static_cast<int>(_s.length()), ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, "google.protobuf.Value.string_value");
+      target = stream->WriteStringMaybeAliased(3, _s, target);
+      break;
+    }
+    case kBoolValue: {
+      target = stream->EnsureSpace(target);
+      target = ::_pbi::WireFormatLite::WriteBoolToArray(
+          4, this->_internal_bool_value(), target);
+      break;
+    }
+    case kStructValue: {
+      target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(5, _Internal::struct_value(this),
+          _Internal::struct_value(this).GetCachedSize(), target, stream);
+      break;
+    }
+    case kListValue: {
+      target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+        InternalWriteMessage(6, _Internal::list_value(this),
+          _Internal::list_value(this).GetCachedSize(), target, stream);
+      break;
+    }
+    default: ;
   }
-
-  // double number_value = 2;
-  if (kind_case() == kNumberValue) {
-    target = stream->EnsureSpace(target);
-    target = ::_pbi::WireFormatLite::WriteDoubleToArray(
-        2, this->_internal_number_value(), target);
-  }
-
-  // string string_value = 3;
-  if (kind_case() == kStringValue) {
-    const std::string& _s = this->_internal_string_value();
-    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
-        _s.data(), static_cast<int>(_s.length()), ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, "google.protobuf.Value.string_value");
-    target = stream->WriteStringMaybeAliased(3, _s, target);
-  }
-
-  // bool bool_value = 4;
-  if (kind_case() == kBoolValue) {
-    target = stream->EnsureSpace(target);
-    target = ::_pbi::WireFormatLite::WriteBoolToArray(
-        4, this->_internal_bool_value(), target);
-  }
-
-  // .google.protobuf.Struct struct_value = 5;
-  if (kind_case() == kStructValue) {
-    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
-      InternalWriteMessage(5, _Internal::struct_value(this),
-        _Internal::struct_value(this).GetCachedSize(), target, stream);
-  }
-
-  // .google.protobuf.ListValue list_value = 6;
-  if (kind_case() == kListValue) {
-    target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
-      InternalWriteMessage(6, _Internal::list_value(this),
-        _Internal::list_value(this).GetCachedSize(), target, stream);
-  }
-
   if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
     target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
         _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
diff --git a/src/google/protobuf/struct.pb.h b/src/google/protobuf/struct.pb.h
index 6494421..7863324 100644
--- a/src/google/protobuf/struct.pb.h
+++ b/src/google/protobuf/struct.pb.h
@@ -9,7 +9,7 @@
 #include <type_traits>
 
 #include "google/protobuf/port_def.inc"
-#if PROTOBUF_VERSION < 4022000
+#if PROTOBUF_VERSION < 3021000
 #error "This file was generated by a newer version of protoc which is"
 #error "incompatible with your Protocol Buffer headers. Please update"
 #error "your headers."
@@ -171,6 +171,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
@@ -332,6 +339,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
@@ -596,6 +610,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
diff --git a/src/google/protobuf/struct.proto b/src/google/protobuf/struct.proto
index c4ea645..1bf0c1a 100644
--- a/src/google/protobuf/struct.proto
+++ b/src/google/protobuf/struct.proto
@@ -80,7 +80,7 @@
 // `NullValue` is a singleton enumeration to represent the null value for the
 // `Value` type union.
 //
-//  The JSON representation for `NullValue` is JSON `null`.
+// The JSON representation for `NullValue` is JSON `null`.
 enum NullValue {
   // Null value.
   NULL_VALUE = 0;
diff --git a/src/google/protobuf/stubs/common.h b/src/google/protobuf/stubs/common.h
index 80426dc..3255944 100644
--- a/src/google/protobuf/stubs/common.h
+++ b/src/google/protobuf/stubs/common.h
@@ -74,15 +74,15 @@
 // The minimum header version which works with the current version of
 // the library.  This constant should only be used by protoc's C++ code
 // generator.
-static const int kMinHeaderVersionForLibrary = 4022000;
+static const int kMinHeaderVersionForLibrary = 3021000;
 
 // The minimum protoc version which works with the current version of the
 // headers.
-#define GOOGLE_PROTOBUF_MIN_PROTOC_VERSION 4022000
+#define GOOGLE_PROTOBUF_MIN_PROTOC_VERSION 3021000
 
 // The minimum header version which works with the current version of
 // protoc.  This constant should only be used in VerifyVersion().
-static const int kMinHeaderVersionForProtoc = 4022000;
+static const int kMinHeaderVersionForProtoc = 3021000;
 
 // Verifies that the headers and libraries are compatible.  Use the macro
 // below to call this.
diff --git a/src/google/protobuf/test_messages_proto2.proto b/src/google/protobuf/test_messages_proto2.proto
index 1cc7c86..088bfa7 100644
--- a/src/google/protobuf/test_messages_proto2.proto
+++ b/src/google/protobuf/test_messages_proto2.proto
@@ -301,3 +301,97 @@
   optional string concept = 2;
   repeated string requires = 3;
 }
+
+message TestAllRequiredTypesProto2 {
+  message NestedMessage {
+    required int32 a = 1;
+    required TestAllRequiredTypesProto2 corecursive = 2;
+    optional TestAllRequiredTypesProto2 optional_corecursive = 3;
+  }
+
+  enum NestedEnum {
+    FOO = 0;
+    BAR = 1;
+    BAZ = 2;
+    NEG = -1;  // Intentionally negative.
+  }
+
+  // Singular
+  required int32 required_int32 = 1;
+  required int64 required_int64 = 2;
+  required uint32 required_uint32 = 3;
+  required uint64 required_uint64 = 4;
+  required sint32 required_sint32 = 5;
+  required sint64 required_sint64 = 6;
+  required fixed32 required_fixed32 = 7;
+  required fixed64 required_fixed64 = 8;
+  required sfixed32 required_sfixed32 = 9;
+  required sfixed64 required_sfixed64 = 10;
+  required float required_float = 11;
+  required double required_double = 12;
+  required bool required_bool = 13;
+  required string required_string = 14;
+  required bytes required_bytes = 15;
+
+  required NestedMessage required_nested_message = 18;
+  required ForeignMessageProto2 required_foreign_message = 19;
+
+  required NestedEnum required_nested_enum = 21;
+  required ForeignEnumProto2 required_foreign_enum = 22;
+
+  required string required_string_piece = 24 [ctype = STRING_PIECE];
+  required string required_cord = 25 [ctype = CORD];
+
+  required TestAllRequiredTypesProto2 recursive_message = 27;
+  optional TestAllRequiredTypesProto2 optional_recursive_message = 28;
+
+  // extensions
+  extensions 120 to 200;
+
+  // groups
+  required group Data = 201 {
+    required int32 group_int32 = 202;
+    required uint32 group_uint32 = 203;
+  }
+
+  // default values
+  required int32 default_int32 = 241 [default = -123456789];
+  required int64 default_int64 = 242 [default = -9123456789123456789];
+  required uint32 default_uint32 = 243 [default = 2123456789];
+  required uint64 default_uint64 = 244 [default = 10123456789123456789];
+  required sint32 default_sint32 = 245 [default = -123456789];
+  required sint64 default_sint64 = 246 [default = -9123456789123456789];
+  required fixed32 default_fixed32 = 247 [default = 2123456789];
+  required fixed64 default_fixed64 = 248 [default = 10123456789123456789];
+  required sfixed32 default_sfixed32 = 249 [default = -123456789];
+  required sfixed64 default_sfixed64 = 250 [default = -9123456789123456789];
+  required float default_float = 251 [default = 9e9];
+  required double default_double = 252 [default = 7e22];
+  required bool default_bool = 253 [default = true];
+  required string default_string = 254 [default = "Rosebud"];
+  required bytes default_bytes = 255 [default = "joshua"];
+
+  // Reserved for unknown fields test.
+  reserved 1000 to 9999;
+
+  // message_set test case.
+  message MessageSetCorrect {
+    option message_set_wire_format = true;
+
+    extensions 4 to max;
+  }
+
+  message MessageSetCorrectExtension1 {
+    extend MessageSetCorrect {
+      optional MessageSetCorrectExtension1 message_set_extension = 1547769;
+    }
+    required string str = 25;
+  }
+
+  message MessageSetCorrectExtension2 {
+    extend MessageSetCorrect {
+      optional MessageSetCorrectExtension2 message_set_extension = 4135312;
+    }
+    required int32 i = 9;
+  }
+}
diff --git a/src/google/protobuf/text_format.cc b/src/google/protobuf/text_format.cc
index 57ff14f..de994f4 100644
--- a/src/google/protobuf/text_format.cc
+++ b/src/google/protobuf/text_format.cc
@@ -64,6 +64,7 @@
 #include "google/protobuf/io/zero_copy_stream_impl.h"
 #include "google/protobuf/map_field.h"
 #include "google/protobuf/message.h"
+#include "google/protobuf/reflection_mode.h"
 #include "google/protobuf/repeated_field.h"
 #include "google/protobuf/unknown_field_set.h"
 #include "google/protobuf/wire_format_lite.h"
@@ -74,6 +75,9 @@
 namespace google {
 namespace protobuf {
 
+using internal::ReflectionMode;
+using internal::ScopedReflectionMode;
+
 namespace {
 
 inline bool IsHexNumber(const std::string& str) {
@@ -103,6 +107,8 @@
 }  // namespace internal
 
 std::string Message::DebugString() const {
+  // Indicate all scoped reflection calls are from DebugString function.
+  ScopedReflectionMode scope(ReflectionMode::kDebugString);
   std::string debug_string;
 
   TextFormat::Printer printer;
@@ -122,6 +128,8 @@
 }
 
 std::string Message::ShortDebugString() const {
+  // Indicate all scoped reflection calls are from DebugString function.
+  ScopedReflectionMode scope(ReflectionMode::kDebugString);
   std::string debug_string;
 
   TextFormat::Printer printer;
@@ -146,6 +154,8 @@
 }
 
 std::string Message::Utf8DebugString() const {
+  // Indicate all scoped reflection calls are from DebugString function.
+  ScopedReflectionMode scope(ReflectionMode::kDebugString);
   std::string debug_string;
 
   TextFormat::Printer printer;
@@ -171,6 +181,9 @@
 
 void PerformAbslStringify(const Message& message,
                           absl::FunctionRef<void(absl::string_view)> append) {
+  // Indicate all scoped reflection calls are from DebugString function.
+  ScopedReflectionMode scope(ReflectionMode::kDebugString);
+
   // TODO(b/249835002): consider using the single line version for short
   TextFormat::Printer printer;
   printer.SetExpandAny(true);
@@ -905,7 +918,7 @@
 
         if (enum_value == nullptr) {
           if (int_value != kint64max &&
-              reflection->SupportsUnknownEnumValues()) {
+              !field->legacy_enum_field_treated_as_closed()) {
             SET_FIELD(EnumValue, int_value);
             return true;
           } else if (!allow_unknown_enum_) {
@@ -2686,18 +2699,23 @@
   return Parser().ParseFieldValueFromString(input, field, message);
 }
 
+template <typename... T>
+PROTOBUF_NOINLINE void TextFormat::OutOfLinePrintString(
+    BaseTextGenerator* generator, const T&... values) {
+  generator->PrintString(absl::StrCat(values...));
+}
+
 void TextFormat::Printer::PrintUnknownFields(
     const UnknownFieldSet& unknown_fields, BaseTextGenerator* generator,
     int recursion_budget) const {
   for (int i = 0; i < unknown_fields.field_count(); i++) {
     const UnknownField& field = unknown_fields.field(i);
-    std::string field_number = absl::StrCat(field.number());
 
     switch (field.type()) {
       case UnknownField::TYPE_VARINT:
-        generator->PrintString(field_number);
+        OutOfLinePrintString(generator, field.number());
         generator->PrintMaybeWithMarker(MarkerToken(), ": ");
-        generator->PrintString(absl::StrCat(field.varint()));
+        OutOfLinePrintString(generator, field.varint());
         if (single_line_mode_) {
           generator->PrintLiteral(" ");
         } else {
@@ -2705,10 +2723,10 @@
         }
         break;
       case UnknownField::TYPE_FIXED32: {
-        generator->PrintString(field_number);
+        OutOfLinePrintString(generator, field.number());
         generator->PrintMaybeWithMarker(MarkerToken(), ": ", "0x");
-        generator->PrintString(
-            absl::StrCat(absl::Hex(field.fixed32(), absl::kZeroPad8)));
+        OutOfLinePrintString(generator,
+                             absl::Hex(field.fixed32(), absl::kZeroPad8));
         if (single_line_mode_) {
           generator->PrintLiteral(" ");
         } else {
@@ -2717,10 +2735,10 @@
         break;
       }
       case UnknownField::TYPE_FIXED64: {
-        generator->PrintString(field_number);
+        OutOfLinePrintString(generator, field.number());
         generator->PrintMaybeWithMarker(MarkerToken(), ": ", "0x");
-        generator->PrintString(
-            absl::StrCat(absl::Hex(field.fixed64(), absl::kZeroPad16)));
+        OutOfLinePrintString(generator,
+                             absl::Hex(field.fixed64(), absl::kZeroPad16));
         if (single_line_mode_) {
           generator->PrintLiteral(" ");
         } else {
@@ -2729,7 +2747,7 @@
         break;
       }
       case UnknownField::TYPE_LENGTH_DELIMITED: {
-        generator->PrintString(field_number);
+        OutOfLinePrintString(generator, field.number());
         const std::string& value = field.length_delimited();
         // We create a CodedInputStream so that we can adhere to our recursion
         // budget when we attempt to parse the data. UnknownFieldSet parsing is
@@ -2770,7 +2788,7 @@
         break;
       }
       case UnknownField::TYPE_GROUP:
-        generator->PrintString(field_number);
+        OutOfLinePrintString(generator, field.number());
         if (single_line_mode_) {
           generator->PrintMaybeWithMarker(MarkerToken(), " ", "{ ");
         } else {
diff --git a/src/google/protobuf/text_format.h b/src/google/protobuf/text_format.h
index 122c8a4..74f954b 100644
--- a/src/google/protobuf/text_format.h
+++ b/src/google/protobuf/text_format.h
@@ -141,7 +141,7 @@
     // Print text to the output stream.
     virtual void Print(const char* text, size_t size) = 0;
 
-    void PrintString(const std::string& str) { Print(str.data(), str.size()); }
+    void PrintString(absl::string_view str) { Print(str.data(), str.size()); }
 
     template <size_t n>
     void PrintLiteral(const char (&text)[n]) {
@@ -724,6 +724,11 @@
                                     ParseLocationRange location);
   static inline ParseInfoTree* CreateNested(ParseInfoTree* info_tree,
                                             const FieldDescriptor* field);
+  // To reduce stack frame bloat we use an out-of-line function to print
+  // strings. This avoid local std::string temporaries.
+  template <typename... T>
+  static void OutOfLinePrintString(BaseTextGenerator* generator,
+                                   const T&... values);
 };
 
 
diff --git a/src/google/protobuf/text_format_unittest.cc b/src/google/protobuf/text_format_unittest.cc
index f171855..7920acc 100644
--- a/src/google/protobuf/text_format_unittest.cc
+++ b/src/google/protobuf/text_format_unittest.cc
@@ -297,6 +297,22 @@
             message.DebugString());
 }
 
+TEST_F(TextFormatTest, PrintUnknownFieldsDeepestStackWorks) {
+  // Test printing of unknown fields in a message.
+
+  unittest::TestEmptyMessage message;
+  UnknownFieldSet* unknown_fields = message.mutable_unknown_fields();
+
+  for (int i = 0; i < 200; ++i) {
+    unknown_fields = unknown_fields->AddGroup(1);
+  }
+
+  unknown_fields->AddVarint(2, 100);
+
+  std::string str;
+  EXPECT_TRUE(TextFormat::PrintToString(message, &str));
+}
+
 TEST_F(TextFormatTest, PrintUnknownFieldsHidden) {
   // Test printing of unknown fields in a message when suppressed.
 
@@ -2350,6 +2366,7 @@
             std::signbit(out_message.optional_double()));
 }
 
+
 }  // namespace text_format_unittest
 }  // namespace protobuf
 }  // namespace google
diff --git a/src/google/protobuf/timestamp.pb.h b/src/google/protobuf/timestamp.pb.h
index 86ffd2a..bb9082b 100644
--- a/src/google/protobuf/timestamp.pb.h
+++ b/src/google/protobuf/timestamp.pb.h
@@ -9,7 +9,7 @@
 #include <type_traits>
 
 #include "google/protobuf/port_def.inc"
-#if PROTOBUF_VERSION < 4022000
+#if PROTOBUF_VERSION < 3021000
 #error "This file was generated by a newer version of protoc which is"
 #error "incompatible with your Protocol Buffer headers. Please update"
 #error "your headers."
@@ -96,6 +96,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
diff --git a/src/google/protobuf/type.pb.cc b/src/google/protobuf/type.pb.cc
index c608689..15a9d80 100644
--- a/src/google/protobuf/type.pb.cc
+++ b/src/google/protobuf/type.pb.cc
@@ -28,6 +28,10 @@
     &::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized {}
   }
 
+  , /*decltype(_impl_.edition_)*/ {
+    &::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized {}
+  }
+
   , /*decltype(_impl_.source_context_)*/nullptr
   , /*decltype(_impl_.syntax_)*/ 0
 
@@ -90,6 +94,10 @@
     &::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized {}
   }
 
+  , /*decltype(_impl_.edition_)*/ {
+    &::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized {}
+  }
+
   , /*decltype(_impl_.source_context_)*/nullptr
   , /*decltype(_impl_.syntax_)*/ 0
 
@@ -163,6 +171,7 @@
     PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Type, _impl_.options_),
     PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Type, _impl_.source_context_),
     PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Type, _impl_.syntax_),
+    PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Type, _impl_.edition_),
     ~0u,  // no _has_bits_
     PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Field, _internal_metadata_),
     ~0u,  // no _extensions_
@@ -194,6 +203,7 @@
     PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Enum, _impl_.options_),
     PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Enum, _impl_.source_context_),
     PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Enum, _impl_.syntax_),
+    PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Enum, _impl_.edition_),
     ~0u,  // no _has_bits_
     PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::EnumValue, _internal_metadata_),
     ~0u,  // no _extensions_
@@ -220,10 +230,10 @@
 static const ::_pbi::MigrationSchema
     schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
         { 0, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Type)},
-        { 14, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Field)},
-        { 32, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Enum)},
-        { 45, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::EnumValue)},
-        { 56, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Option)},
+        { 15, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Field)},
+        { 33, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Enum)},
+        { 47, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::EnumValue)},
+        { 58, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Option)},
 };
 
 static const ::_pb::Message* const file_default_instances[] = {
@@ -236,44 +246,46 @@
 const char descriptor_table_protodef_google_2fprotobuf_2ftype_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
     "\n\032google/protobuf/type.proto\022\017google.pro"
     "tobuf\032\031google/protobuf/any.proto\032$google"
-    "/protobuf/source_context.proto\"\327\001\n\004Type\022"
+    "/protobuf/source_context.proto\"\350\001\n\004Type\022"
     "\014\n\004name\030\001 \001(\t\022&\n\006fields\030\002 \003(\0132\026.google.p"
     "rotobuf.Field\022\016\n\006oneofs\030\003 \003(\t\022(\n\007options"
     "\030\004 \003(\0132\027.google.protobuf.Option\0226\n\016sourc"
     "e_context\030\005 \001(\0132\036.google.protobuf.Source"
     "Context\022\'\n\006syntax\030\006 \001(\0162\027.google.protobu"
-    "f.Syntax\"\325\005\n\005Field\022)\n\004kind\030\001 \001(\0162\033.googl"
-    "e.protobuf.Field.Kind\0227\n\013cardinality\030\002 \001"
-    "(\0162\".google.protobuf.Field.Cardinality\022\016"
-    "\n\006number\030\003 \001(\005\022\014\n\004name\030\004 \001(\t\022\020\n\010type_url"
-    "\030\006 \001(\t\022\023\n\013oneof_index\030\007 \001(\005\022\016\n\006packed\030\010 "
-    "\001(\010\022(\n\007options\030\t \003(\0132\027.google.protobuf.O"
-    "ption\022\021\n\tjson_name\030\n \001(\t\022\025\n\rdefault_valu"
-    "e\030\013 \001(\t\"\310\002\n\004Kind\022\020\n\014TYPE_UNKNOWN\020\000\022\017\n\013TY"
-    "PE_DOUBLE\020\001\022\016\n\nTYPE_FLOAT\020\002\022\016\n\nTYPE_INT6"
-    "4\020\003\022\017\n\013TYPE_UINT64\020\004\022\016\n\nTYPE_INT32\020\005\022\020\n\014"
-    "TYPE_FIXED64\020\006\022\020\n\014TYPE_FIXED32\020\007\022\r\n\tTYPE"
-    "_BOOL\020\010\022\017\n\013TYPE_STRING\020\t\022\016\n\nTYPE_GROUP\020\n"
-    "\022\020\n\014TYPE_MESSAGE\020\013\022\016\n\nTYPE_BYTES\020\014\022\017\n\013TY"
-    "PE_UINT32\020\r\022\r\n\tTYPE_ENUM\020\016\022\021\n\rTYPE_SFIXE"
-    "D32\020\017\022\021\n\rTYPE_SFIXED64\020\020\022\017\n\013TYPE_SINT32\020"
-    "\021\022\017\n\013TYPE_SINT64\020\022\"t\n\013Cardinality\022\027\n\023CAR"
-    "DINALITY_UNKNOWN\020\000\022\030\n\024CARDINALITY_OPTION"
-    "AL\020\001\022\030\n\024CARDINALITY_REQUIRED\020\002\022\030\n\024CARDIN"
-    "ALITY_REPEATED\020\003\"\316\001\n\004Enum\022\014\n\004name\030\001 \001(\t\022"
-    "-\n\tenumvalue\030\002 \003(\0132\032.google.protobuf.Enu"
-    "mValue\022(\n\007options\030\003 \003(\0132\027.google.protobu"
-    "f.Option\0226\n\016source_context\030\004 \001(\0132\036.googl"
-    "e.protobuf.SourceContext\022\'\n\006syntax\030\005 \001(\016"
-    "2\027.google.protobuf.Syntax\"S\n\tEnumValue\022\014"
-    "\n\004name\030\001 \001(\t\022\016\n\006number\030\002 \001(\005\022(\n\007options\030"
-    "\003 \003(\0132\027.google.protobuf.Option\";\n\006Option"
-    "\022\014\n\004name\030\001 \001(\t\022#\n\005value\030\002 \001(\0132\024.google.p"
-    "rotobuf.Any*.\n\006Syntax\022\021\n\rSYNTAX_PROTO2\020\000"
-    "\022\021\n\rSYNTAX_PROTO3\020\001B{\n\023com.google.protob"
-    "ufB\tTypeProtoP\001Z-google.golang.org/proto"
-    "buf/types/known/typepb\370\001\001\242\002\003GPB\252\002\036Google"
-    ".Protobuf.WellKnownTypesb\006proto3"
+    "f.Syntax\022\017\n\007edition\030\007 \001(\t\"\325\005\n\005Field\022)\n\004k"
+    "ind\030\001 \001(\0162\033.google.protobuf.Field.Kind\0227"
+    "\n\013cardinality\030\002 \001(\0162\".google.protobuf.Fi"
+    "eld.Cardinality\022\016\n\006number\030\003 \001(\005\022\014\n\004name\030"
+    "\004 \001(\t\022\020\n\010type_url\030\006 \001(\t\022\023\n\013oneof_index\030\007"
+    " \001(\005\022\016\n\006packed\030\010 \001(\010\022(\n\007options\030\t \003(\0132\027."
+    "google.protobuf.Option\022\021\n\tjson_name\030\n \001("
+    "\t\022\025\n\rdefault_value\030\013 \001(\t\"\310\002\n\004Kind\022\020\n\014TYP"
+    "E_UNKNOWN\020\000\022\017\n\013TYPE_DOUBLE\020\001\022\016\n\nTYPE_FLO"
+    "AT\020\002\022\016\n\nTYPE_INT64\020\003\022\017\n\013TYPE_UINT64\020\004\022\016\n"
+    "\nTYPE_INT32\020\005\022\020\n\014TYPE_FIXED64\020\006\022\020\n\014TYPE_"
+    "FIXED32\020\007\022\r\n\tTYPE_BOOL\020\010\022\017\n\013TYPE_STRING\020"
+    "\t\022\016\n\nTYPE_GROUP\020\n\022\020\n\014TYPE_MESSAGE\020\013\022\016\n\nT"
+    "YPE_BYTES\020\014\022\017\n\013TYPE_UINT32\020\r\022\r\n\tTYPE_ENU"
+    "M\020\016\022\021\n\rTYPE_SFIXED32\020\017\022\021\n\rTYPE_SFIXED64\020"
+    "\020\022\017\n\013TYPE_SINT32\020\021\022\017\n\013TYPE_SINT64\020\022\"t\n\013C"
+    "ardinality\022\027\n\023CARDINALITY_UNKNOWN\020\000\022\030\n\024C"
+    "ARDINALITY_OPTIONAL\020\001\022\030\n\024CARDINALITY_REQ"
+    "UIRED\020\002\022\030\n\024CARDINALITY_REPEATED\020\003\"\337\001\n\004En"
+    "um\022\014\n\004name\030\001 \001(\t\022-\n\tenumvalue\030\002 \003(\0132\032.go"
+    "ogle.protobuf.EnumValue\022(\n\007options\030\003 \003(\013"
+    "2\027.google.protobuf.Option\0226\n\016source_cont"
+    "ext\030\004 \001(\0132\036.google.protobuf.SourceContex"
+    "t\022\'\n\006syntax\030\005 \001(\0162\027.google.protobuf.Synt"
+    "ax\022\017\n\007edition\030\006 \001(\t\"S\n\tEnumValue\022\014\n\004name"
+    "\030\001 \001(\t\022\016\n\006number\030\002 \001(\005\022(\n\007options\030\003 \003(\0132"
+    "\027.google.protobuf.Option\";\n\006Option\022\014\n\004na"
+    "me\030\001 \001(\t\022#\n\005value\030\002 \001(\0132\024.google.protobu"
+    "f.Any*C\n\006Syntax\022\021\n\rSYNTAX_PROTO2\020\000\022\021\n\rSY"
+    "NTAX_PROTO3\020\001\022\023\n\017SYNTAX_EDITIONS\020\002B{\n\023co"
+    "m.google.protobufB\tTypeProtoP\001Z-google.g"
+    "olang.org/protobuf/types/known/typepb\370\001\001"
+    "\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypesb"
+    "\006proto3"
 };
 static const ::_pbi::DescriptorTable* const descriptor_table_google_2fprotobuf_2ftype_2eproto_deps[2] =
     {
@@ -284,7 +296,7 @@
 const ::_pbi::DescriptorTable descriptor_table_google_2fprotobuf_2ftype_2eproto = {
     false,
     false,
-    1592,
+    1647,
     descriptor_table_protodef_google_2fprotobuf_2ftype_2eproto,
     "google/protobuf/type.proto",
     &descriptor_table_google_2fprotobuf_2ftype_2eproto_once,
@@ -411,6 +423,7 @@
   switch (value) {
     case 0:
     case 1:
+    case 2:
       return true;
     default:
       return false;
@@ -447,6 +460,8 @@
     , decltype(_impl_.options_){from._impl_.options_}
     , decltype(_impl_.name_) {}
 
+    , decltype(_impl_.edition_) {}
+
     , decltype(_impl_.source_context_){nullptr}
     , decltype(_impl_.syntax_) {}
 
@@ -460,6 +475,13 @@
   if (!from._internal_name().empty()) {
     _this->_impl_.name_.Set(from._internal_name(), _this->GetArenaForAllocation());
   }
+  _impl_.edition_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+        _impl_.edition_.Set("", GetArenaForAllocation());
+  #endif  // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_edition().empty()) {
+    _this->_impl_.edition_.Set(from._internal_edition(), _this->GetArenaForAllocation());
+  }
   if (from._internal_has_source_context()) {
     _this->_impl_.source_context_ = new ::PROTOBUF_NAMESPACE_ID::SourceContext(*from._impl_.source_context_);
   }
@@ -475,6 +497,8 @@
     , decltype(_impl_.options_){arena}
     , decltype(_impl_.name_) {}
 
+    , decltype(_impl_.edition_) {}
+
     , decltype(_impl_.source_context_){nullptr}
     , decltype(_impl_.syntax_) { 0 }
 
@@ -484,6 +508,10 @@
   #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
         _impl_.name_.Set("", GetArenaForAllocation());
   #endif  // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.edition_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+        _impl_.edition_.Set("", GetArenaForAllocation());
+  #endif  // PROTOBUF_FORCE_COPY_DEFAULT_STRING
 }
 
 Type::~Type() {
@@ -501,6 +529,7 @@
   _impl_.oneofs_.~RepeatedPtrField();
   _impl_.options_.~RepeatedPtrField();
   _impl_.name_.Destroy();
+  _impl_.edition_.Destroy();
   if (this != internal_default_instance()) delete _impl_.source_context_;
 }
 
@@ -518,6 +547,7 @@
   _impl_.oneofs_.Clear();
   _impl_.options_.Clear();
   _impl_.name_.ClearToEmpty();
+  _impl_.edition_.ClearToEmpty();
   if (GetArenaForAllocation() == nullptr && _impl_.source_context_ != nullptr) {
     delete _impl_.source_context_;
   }
@@ -599,13 +629,24 @@
       // .google.protobuf.Syntax syntax = 6;
       case 6:
         if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 48)) {
-          ::uint32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          ::int32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
           CHK_(ptr);
           _internal_set_syntax(static_cast<::PROTOBUF_NAMESPACE_ID::Syntax>(val));
         } else {
           goto handle_unusual;
         }
         continue;
+      // string edition = 7;
+      case 7:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 58)) {
+          auto str = _internal_mutable_edition();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Type.edition"));
+        } else {
+          goto handle_unusual;
+        }
+        continue;
       default:
         goto handle_unusual;
     }  // switch
@@ -681,6 +722,14 @@
         6, this->_internal_syntax(), target);
   }
 
+  // string edition = 7;
+  if (!this->_internal_edition().empty()) {
+    const std::string& _s = this->_internal_edition();
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+        _s.data(), static_cast<int>(_s.length()), ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, "google.protobuf.Type.edition");
+    target = stream->WriteStringMaybeAliased(7, _s, target);
+  }
+
   if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
     target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
         _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
@@ -723,6 +772,12 @@
                                     this->_internal_name());
   }
 
+  // string edition = 7;
+  if (!this->_internal_edition().empty()) {
+    total_size += 1 + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+                                    this->_internal_edition());
+  }
+
   // .google.protobuf.SourceContext source_context = 5;
   if (this->_internal_has_source_context()) {
     total_size += 1 +
@@ -760,6 +815,9 @@
   if (!from._internal_name().empty()) {
     _this->_internal_set_name(from._internal_name());
   }
+  if (!from._internal_edition().empty()) {
+    _this->_internal_set_edition(from._internal_edition());
+  }
   if (from._internal_has_source_context()) {
     _this->_internal_mutable_source_context()->::PROTOBUF_NAMESPACE_ID::SourceContext::MergeFrom(
         from._internal_source_context());
@@ -791,6 +849,8 @@
   _impl_.options_.InternalSwap(&other->_impl_.options_);
   ::_pbi::ArenaStringPtr::InternalSwap(&_impl_.name_, lhs_arena,
                                        &other->_impl_.name_, rhs_arena);
+  ::_pbi::ArenaStringPtr::InternalSwap(&_impl_.edition_, lhs_arena,
+                                       &other->_impl_.edition_, rhs_arena);
   ::PROTOBUF_NAMESPACE_ID::internal::memswap<
       PROTOBUF_FIELD_OFFSET(Type, _impl_.syntax_)
       + sizeof(Type::_impl_.syntax_)
@@ -965,7 +1025,7 @@
       // .google.protobuf.Field.Kind kind = 1;
       case 1:
         if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 8)) {
-          ::uint32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          ::int32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
           CHK_(ptr);
           _internal_set_kind(static_cast<::PROTOBUF_NAMESPACE_ID::Field_Kind>(val));
         } else {
@@ -975,7 +1035,7 @@
       // .google.protobuf.Field.Cardinality cardinality = 2;
       case 2:
         if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 16)) {
-          ::uint32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          ::int32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
           CHK_(ptr);
           _internal_set_cardinality(static_cast<::PROTOBUF_NAMESPACE_ID::Field_Cardinality>(val));
         } else {
@@ -1364,6 +1424,8 @@
     , decltype(_impl_.options_){from._impl_.options_}
     , decltype(_impl_.name_) {}
 
+    , decltype(_impl_.edition_) {}
+
     , decltype(_impl_.source_context_){nullptr}
     , decltype(_impl_.syntax_) {}
 
@@ -1377,6 +1439,13 @@
   if (!from._internal_name().empty()) {
     _this->_impl_.name_.Set(from._internal_name(), _this->GetArenaForAllocation());
   }
+  _impl_.edition_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+        _impl_.edition_.Set("", GetArenaForAllocation());
+  #endif  // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  if (!from._internal_edition().empty()) {
+    _this->_impl_.edition_.Set(from._internal_edition(), _this->GetArenaForAllocation());
+  }
   if (from._internal_has_source_context()) {
     _this->_impl_.source_context_ = new ::PROTOBUF_NAMESPACE_ID::SourceContext(*from._impl_.source_context_);
   }
@@ -1391,6 +1460,8 @@
     , decltype(_impl_.options_){arena}
     , decltype(_impl_.name_) {}
 
+    , decltype(_impl_.edition_) {}
+
     , decltype(_impl_.source_context_){nullptr}
     , decltype(_impl_.syntax_) { 0 }
 
@@ -1400,6 +1471,10 @@
   #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
         _impl_.name_.Set("", GetArenaForAllocation());
   #endif  // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  _impl_.edition_.InitDefault();
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+        _impl_.edition_.Set("", GetArenaForAllocation());
+  #endif  // PROTOBUF_FORCE_COPY_DEFAULT_STRING
 }
 
 Enum::~Enum() {
@@ -1416,6 +1491,7 @@
   _impl_.enumvalue_.~RepeatedPtrField();
   _impl_.options_.~RepeatedPtrField();
   _impl_.name_.Destroy();
+  _impl_.edition_.Destroy();
   if (this != internal_default_instance()) delete _impl_.source_context_;
 }
 
@@ -1432,6 +1508,7 @@
   _impl_.enumvalue_.Clear();
   _impl_.options_.Clear();
   _impl_.name_.ClearToEmpty();
+  _impl_.edition_.ClearToEmpty();
   if (GetArenaForAllocation() == nullptr && _impl_.source_context_ != nullptr) {
     delete _impl_.source_context_;
   }
@@ -1497,13 +1574,24 @@
       // .google.protobuf.Syntax syntax = 5;
       case 5:
         if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 40)) {
-          ::uint32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+          ::int32_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
           CHK_(ptr);
           _internal_set_syntax(static_cast<::PROTOBUF_NAMESPACE_ID::Syntax>(val));
         } else {
           goto handle_unusual;
         }
         continue;
+      // string edition = 6;
+      case 6:
+        if (PROTOBUF_PREDICT_TRUE(static_cast<::uint8_t>(tag) == 50)) {
+          auto str = _internal_mutable_edition();
+          ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+          CHK_(ptr);
+          CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Enum.edition"));
+        } else {
+          goto handle_unusual;
+        }
+        continue;
       default:
         goto handle_unusual;
     }  // switch
@@ -1571,6 +1659,14 @@
         5, this->_internal_syntax(), target);
   }
 
+  // string edition = 6;
+  if (!this->_internal_edition().empty()) {
+    const std::string& _s = this->_internal_edition();
+    ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
+        _s.data(), static_cast<int>(_s.length()), ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, "google.protobuf.Enum.edition");
+    target = stream->WriteStringMaybeAliased(6, _s, target);
+  }
+
   if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
     target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
         _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
@@ -1607,6 +1703,12 @@
                                     this->_internal_name());
   }
 
+  // string edition = 6;
+  if (!this->_internal_edition().empty()) {
+    total_size += 1 + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+                                    this->_internal_edition());
+  }
+
   // .google.protobuf.SourceContext source_context = 4;
   if (this->_internal_has_source_context()) {
     total_size += 1 +
@@ -1643,6 +1745,9 @@
   if (!from._internal_name().empty()) {
     _this->_internal_set_name(from._internal_name());
   }
+  if (!from._internal_edition().empty()) {
+    _this->_internal_set_edition(from._internal_edition());
+  }
   if (from._internal_has_source_context()) {
     _this->_internal_mutable_source_context()->::PROTOBUF_NAMESPACE_ID::SourceContext::MergeFrom(
         from._internal_source_context());
@@ -1673,6 +1778,8 @@
   _impl_.options_.InternalSwap(&other->_impl_.options_);
   ::_pbi::ArenaStringPtr::InternalSwap(&_impl_.name_, lhs_arena,
                                        &other->_impl_.name_, rhs_arena);
+  ::_pbi::ArenaStringPtr::InternalSwap(&_impl_.edition_, lhs_arena,
+                                       &other->_impl_.edition_, rhs_arena);
   ::PROTOBUF_NAMESPACE_ID::internal::memswap<
       PROTOBUF_FIELD_OFFSET(Enum, _impl_.syntax_)
       + sizeof(Enum::_impl_.syntax_)
diff --git a/src/google/protobuf/type.pb.h b/src/google/protobuf/type.pb.h
index 64236a6..23ff968 100644
--- a/src/google/protobuf/type.pb.h
+++ b/src/google/protobuf/type.pb.h
@@ -9,7 +9,7 @@
 #include <type_traits>
 
 #include "google/protobuf/port_def.inc"
-#if PROTOBUF_VERSION < 4022000
+#if PROTOBUF_VERSION < 3021000
 #error "This file was generated by a newer version of protoc which is"
 #error "incompatible with your Protocol Buffer headers. Please update"
 #error "your headers."
@@ -168,6 +168,7 @@
 enum Syntax : int {
   SYNTAX_PROTO2 = 0,
   SYNTAX_PROTO3 = 1,
+  SYNTAX_EDITIONS = 2,
   Syntax_INT_MIN_SENTINEL_DO_NOT_USE_ =
       std::numeric_limits<::int32_t>::min(),
   Syntax_INT_MAX_SENTINEL_DO_NOT_USE_ =
@@ -176,8 +177,8 @@
 
 PROTOBUF_EXPORT bool Syntax_IsValid(int value);
 constexpr Syntax Syntax_MIN = static_cast<Syntax>(0);
-constexpr Syntax Syntax_MAX = static_cast<Syntax>(1);
-constexpr int Syntax_ARRAYSIZE = 1 + 1;
+constexpr Syntax Syntax_MAX = static_cast<Syntax>(2);
+constexpr int Syntax_ARRAYSIZE = 2 + 1;
 PROTOBUF_EXPORT const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor*
 Syntax_descriptor();
 template <typename T>
@@ -190,7 +191,7 @@
 template <>
 inline const std::string& Syntax_Name(Syntax value) {
   return ::PROTOBUF_NAMESPACE_ID::internal::NameOfDenseEnum<Syntax_descriptor,
-                                                 0, 1>(
+                                                 0, 2>(
       static_cast<int>(value));
 }
 inline bool Syntax_Parse(absl::string_view name, Syntax* value) {
@@ -234,6 +235,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
@@ -327,6 +335,7 @@
     kOneofsFieldNumber = 3,
     kOptionsFieldNumber = 4,
     kNameFieldNumber = 1,
+    kEditionFieldNumber = 7,
     kSourceContextFieldNumber = 5,
     kSyntaxFieldNumber = 6,
   };
@@ -414,6 +423,26 @@
   std::string* _internal_mutable_name();
 
   public:
+  // string edition = 7;
+  void clear_edition() ;
+  const std::string& edition() const;
+
+
+
+
+  template <typename Arg_ = const std::string&, typename... Args_>
+  void set_edition(Arg_&& arg, Args_... args);
+  std::string* mutable_edition();
+  PROTOBUF_NODISCARD std::string* release_edition();
+  void set_allocated_edition(std::string* ptr);
+
+  private:
+  const std::string& _internal_edition() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_edition(
+      const std::string& value);
+  std::string* _internal_mutable_edition();
+
+  public:
   // .google.protobuf.SourceContext source_context = 5;
   bool has_source_context() const;
   private:
@@ -454,6 +483,7 @@
     ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string> oneofs_;
     ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option > options_;
     ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr edition_;
     ::PROTOBUF_NAMESPACE_ID::SourceContext* source_context_;
     int syntax_;
     mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
@@ -493,6 +523,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
@@ -855,6 +892,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
@@ -947,6 +991,7 @@
     kEnumvalueFieldNumber = 2,
     kOptionsFieldNumber = 3,
     kNameFieldNumber = 1,
+    kEditionFieldNumber = 6,
     kSourceContextFieldNumber = 4,
     kSyntaxFieldNumber = 5,
   };
@@ -1006,6 +1051,26 @@
   std::string* _internal_mutable_name();
 
   public:
+  // string edition = 6;
+  void clear_edition() ;
+  const std::string& edition() const;
+
+
+
+
+  template <typename Arg_ = const std::string&, typename... Args_>
+  void set_edition(Arg_&& arg, Args_... args);
+  std::string* mutable_edition();
+  PROTOBUF_NODISCARD std::string* release_edition();
+  void set_allocated_edition(std::string* ptr);
+
+  private:
+  const std::string& _internal_edition() const;
+  inline PROTOBUF_ALWAYS_INLINE void _internal_set_edition(
+      const std::string& value);
+  std::string* _internal_mutable_edition();
+
+  public:
   // .google.protobuf.SourceContext source_context = 4;
   bool has_source_context() const;
   private:
@@ -1045,6 +1110,7 @@
     ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::EnumValue > enumvalue_;
     ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::PROTOBUF_NAMESPACE_ID::Option > options_;
     ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_;
+    ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr edition_;
     ::PROTOBUF_NAMESPACE_ID::SourceContext* source_context_;
     int syntax_;
     mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
@@ -1084,6 +1150,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
@@ -1273,6 +1346,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
@@ -1742,6 +1822,53 @@
   _impl_.syntax_ = value;
 }
 
+// string edition = 7;
+inline void Type::clear_edition() {
+  _impl_.edition_.ClearToEmpty();
+}
+inline const std::string& Type::edition() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Type.edition)
+  return _internal_edition();
+}
+template <typename Arg_, typename... Args_>
+inline PROTOBUF_ALWAYS_INLINE void Type::set_edition(Arg_&& arg,
+                                                     Args_... args) {
+  ;
+  _impl_.edition_.Set(static_cast<Arg_&&>(arg), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Type.edition)
+}
+inline std::string* Type::mutable_edition() {
+  std::string* _s = _internal_mutable_edition();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Type.edition)
+  return _s;
+}
+inline const std::string& Type::_internal_edition() const {
+  return _impl_.edition_.Get();
+}
+inline void Type::_internal_set_edition(const std::string& value) {
+  ;
+
+
+  _impl_.edition_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Type::_internal_mutable_edition() {
+  ;
+  return _impl_.edition_.Mutable( GetArenaForAllocation());
+}
+inline std::string* Type::release_edition() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Type.edition)
+  return _impl_.edition_.Release();
+}
+inline void Type::set_allocated_edition(std::string* value) {
+  _impl_.edition_.SetAllocated(value, GetArenaForAllocation());
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+        if (_impl_.edition_.IsDefault()) {
+          _impl_.edition_.Set("", GetArenaForAllocation());
+        }
+  #endif  // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Type.edition)
+}
+
 // -------------------------------------------------------------------
 
 // Field
@@ -2305,6 +2432,53 @@
   _impl_.syntax_ = value;
 }
 
+// string edition = 6;
+inline void Enum::clear_edition() {
+  _impl_.edition_.ClearToEmpty();
+}
+inline const std::string& Enum::edition() const {
+  // @@protoc_insertion_point(field_get:google.protobuf.Enum.edition)
+  return _internal_edition();
+}
+template <typename Arg_, typename... Args_>
+inline PROTOBUF_ALWAYS_INLINE void Enum::set_edition(Arg_&& arg,
+                                                     Args_... args) {
+  ;
+  _impl_.edition_.Set(static_cast<Arg_&&>(arg), args..., GetArenaForAllocation());
+  // @@protoc_insertion_point(field_set:google.protobuf.Enum.edition)
+}
+inline std::string* Enum::mutable_edition() {
+  std::string* _s = _internal_mutable_edition();
+  // @@protoc_insertion_point(field_mutable:google.protobuf.Enum.edition)
+  return _s;
+}
+inline const std::string& Enum::_internal_edition() const {
+  return _impl_.edition_.Get();
+}
+inline void Enum::_internal_set_edition(const std::string& value) {
+  ;
+
+
+  _impl_.edition_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Enum::_internal_mutable_edition() {
+  ;
+  return _impl_.edition_.Mutable( GetArenaForAllocation());
+}
+inline std::string* Enum::release_edition() {
+  // @@protoc_insertion_point(field_release:google.protobuf.Enum.edition)
+  return _impl_.edition_.Release();
+}
+inline void Enum::set_allocated_edition(std::string* value) {
+  _impl_.edition_.SetAllocated(value, GetArenaForAllocation());
+  #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+        if (_impl_.edition_.IsDefault()) {
+          _impl_.edition_.Set("", GetArenaForAllocation());
+        }
+  #endif  // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+  // @@protoc_insertion_point(field_set_allocated:google.protobuf.Enum.edition)
+}
+
 // -------------------------------------------------------------------
 
 // EnumValue
diff --git a/src/google/protobuf/type.proto b/src/google/protobuf/type.proto
index fd25a41..48cb11e 100644
--- a/src/google/protobuf/type.proto
+++ b/src/google/protobuf/type.proto
@@ -57,6 +57,8 @@
   SourceContext source_context = 5;
   // The source syntax.
   Syntax syntax = 6;
+  // The source edition string, only valid when syntax is SYNTAX_EDITIONS.
+  string edition = 7;
 }
 
 // A single field of a message type.
@@ -151,6 +153,8 @@
   SourceContext source_context = 4;
   // The source syntax.
   Syntax syntax = 5;
+  // The source edition string, only valid when syntax is SYNTAX_EDITIONS.
+  string edition = 6;
 }
 
 // Enum value definition.
@@ -184,4 +188,6 @@
   SYNTAX_PROTO2 = 0;
   // Syntax `proto3`.
   SYNTAX_PROTO3 = 1;
+  // Syntax `editions`.
+  SYNTAX_EDITIONS = 2;
 }
diff --git a/src/google/protobuf/unittest.proto b/src/google/protobuf/unittest.proto
index 479262f..108572e 100644
--- a/src/google/protobuf/unittest.proto
+++ b/src/google/protobuf/unittest.proto
@@ -552,6 +552,9 @@
     optional SubMessage sub_message = 3;  // Needed because of bug in javatest
     optional TestAllTypes not_in_this_scc = 4;
   }
+  repeated group SubGroupR = 5 {
+    optional TestAllTypes payload = 6;
+  }
 }
 
 message TestMutualRecursionB {
@@ -1544,9 +1547,16 @@
   repeated Arbitrary packed_arbitrary_midfield = 1012 [packed = true];
   repeated Arbitrary packed_arbitrary_hifield = 1000012 [packed = true];
 
+  extensions 2000000 to max;
+  extend EnumParseTester {
+    optional Arbitrary optional_arbitrary_ext = 2000000;
+    repeated Arbitrary repeated_arbitrary_ext = 2000001;
+    repeated Arbitrary packed_arbitrary_ext = 2000002 [packed = true];
+  }
+
   // An arbitrary field we can append to to break the runs of repeated fields.
   optional int32 other_field = 99;
-};
+}
 
 // This message contains different kind of bool fields to exercise the different
 // parsers in table-drived.
@@ -1561,9 +1571,16 @@
   repeated bool packed_bool_midfield = 1003 [packed = true];
   repeated bool packed_bool_hifield = 1000003 [packed = true];
 
+  extensions 2000000 to max;
+  extend BoolParseTester {
+    optional bool optional_bool_ext = 2000000;
+    repeated bool repeated_bool_ext = 2000001;
+    repeated bool packed_bool_ext = 2000002 [packed = true];
+  }
+
   // An arbitrary field we can append to to break the runs of repeated fields.
   optional int32 other_field = 99;
-};
+}
 
 message Int32ParseTester {
   optional int32 optional_int32_lowfield = 1;
@@ -1576,9 +1593,16 @@
   repeated int32 packed_int32_midfield = 1003 [packed = true];
   repeated int32 packed_int32_hifield = 1000003 [packed = true];
 
+  extensions 2000000 to max;
+  extend Int32ParseTester {
+    optional int32 optional_int32_ext = 2000000;
+    repeated int32 repeated_int32_ext = 2000001;
+    repeated int32 packed_int32_ext = 2000002 [packed = true];
+  }
+
   // An arbitrary field we can append to to break the runs of repeated fields.
   optional int32 other_field = 99;
-};
+}
 
 message Int64ParseTester {
   optional int64 optional_int64_lowfield = 1;
@@ -1591,9 +1615,28 @@
   repeated int64 packed_int64_midfield = 1003 [packed = true];
   repeated int64 packed_int64_hifield = 1000003 [packed = true];
 
+  extensions 2000000 to max;
+  extend Int64ParseTester {
+    optional int64 optional_int64_ext = 2000000;
+    repeated int64 repeated_int64_ext = 2000001;
+    repeated int64 packed_int64_ext = 2000002 [packed = true];
+  }
+
   // An arbitrary field we can append to to break the runs of repeated fields.
   optional int32 other_field = 99;
-};
+}
+
+message InlinedStringIdxRegressionProto {
+  // We mix data to make sure aux ids and inlined string idx do not match.
+  // aux_idx == inlined_string_idx == 1
+  optional string str1 = 1;
+  // aux_idx == 2
+  optional InlinedStringIdxRegressionProto sub = 2;
+  // aux_idx == 3, inlined_string_idx == 2
+  optional string str2 = 3;
+  // aux_idx == 4, inlined_string_idx == 3
+  optional bytes str3 = 4;
+}
 
 message StringParseTester {
   optional string optional_string_lowfield = 1;
@@ -1602,7 +1645,13 @@
   repeated string repeated_string_lowfield = 2;
   repeated string repeated_string_midfield = 1002;
   repeated string repeated_string_hifield = 1000002;
-};
+
+  extensions 2000000 to max;
+  extend StringParseTester {
+    optional string optional_string_ext = 2000000;
+    repeated string repeated_string_ext = 2000001;
+  }
+}
 
 message BadFieldNames{
   optional int32 OptionalInt32 = 1;
diff --git a/src/google/protobuf/unknown_field_set_unittest.cc b/src/google/protobuf/unknown_field_set_unittest.cc
index e723394..67b6b3d 100644
--- a/src/google/protobuf/unknown_field_set_unittest.cc
+++ b/src/google/protobuf/unknown_field_set_unittest.cc
@@ -46,6 +46,7 @@
 #include "google/protobuf/testing/googletest.h"
 #include <gtest/gtest.h>
 #include "absl/container/flat_hash_set.h"
+#include "absl/functional/bind_front.h"
 #include "absl/strings/cord.h"
 #include "absl/synchronization/mutex.h"
 #include "absl/time/clock.h"
diff --git a/src/google/protobuf/util/type_resolver_util.cc b/src/google/protobuf/util/type_resolver_util.cc
index 1537708..e408e78 100644
--- a/src/google/protobuf/util/type_resolver_util.cc
+++ b/src/google/protobuf/util/type_resolver_util.cc
@@ -30,10 +30,13 @@
 
 #include "google/protobuf/util/type_resolver_util.h"
 
+#include <string>
+#include <vector>
+
+#include "google/protobuf/source_context.pb.h"
 #include "google/protobuf/type.pb.h"
 #include "google/protobuf/wrappers.pb.h"
 #include "google/protobuf/descriptor.pb.h"
-#include "google/protobuf/descriptor.h"
 #include "absl/log/absl_log.h"
 #include "absl/status/status.h"
 #include "absl/strings/escaping.h"
@@ -63,10 +66,255 @@
 using google::protobuf::Int64Value;
 using google::protobuf::Option;
 using google::protobuf::StringValue;
+using google::protobuf::Syntax;
 using google::protobuf::Type;
 using google::protobuf::UInt32Value;
 using google::protobuf::UInt64Value;
 
+template <typename WrapperT, typename T>
+static WrapperT WrapValue(T value) {
+  WrapperT wrapper;
+  wrapper.set_value(value);
+  return wrapper;
+}
+
+void ConvertOptionField(const Reflection* reflection, const Message& options,
+                        const FieldDescriptor* field, int index, Option* out) {
+  out->set_name(field->is_extension() ? field->full_name() : field->name());
+  Any* value = out->mutable_value();
+  switch (field->cpp_type()) {
+    case FieldDescriptor::CPPTYPE_MESSAGE:
+      value->PackFrom(
+          field->is_repeated()
+              ? reflection->GetRepeatedMessage(options, field, index)
+              : reflection->GetMessage(options, field));
+      return;
+    case FieldDescriptor::CPPTYPE_DOUBLE:
+      value->PackFrom(WrapValue<DoubleValue>(
+          field->is_repeated()
+              ? reflection->GetRepeatedDouble(options, field, index)
+              : reflection->GetDouble(options, field)));
+      return;
+    case FieldDescriptor::CPPTYPE_FLOAT:
+      value->PackFrom(WrapValue<FloatValue>(
+          field->is_repeated()
+              ? reflection->GetRepeatedFloat(options, field, index)
+              : reflection->GetFloat(options, field)));
+      return;
+    case FieldDescriptor::CPPTYPE_INT64:
+      value->PackFrom(WrapValue<Int64Value>(
+          field->is_repeated()
+              ? reflection->GetRepeatedInt64(options, field, index)
+              : reflection->GetInt64(options, field)));
+      return;
+    case FieldDescriptor::CPPTYPE_UINT64:
+      value->PackFrom(WrapValue<UInt64Value>(
+          field->is_repeated()
+              ? reflection->GetRepeatedUInt64(options, field, index)
+              : reflection->GetUInt64(options, field)));
+      return;
+    case FieldDescriptor::CPPTYPE_INT32:
+      value->PackFrom(WrapValue<Int32Value>(
+          field->is_repeated()
+              ? reflection->GetRepeatedInt32(options, field, index)
+              : reflection->GetInt32(options, field)));
+      return;
+    case FieldDescriptor::CPPTYPE_UINT32:
+      value->PackFrom(WrapValue<UInt32Value>(
+          field->is_repeated()
+              ? reflection->GetRepeatedUInt32(options, field, index)
+              : reflection->GetUInt32(options, field)));
+      return;
+    case FieldDescriptor::CPPTYPE_BOOL:
+      value->PackFrom(WrapValue<BoolValue>(
+          field->is_repeated()
+              ? reflection->GetRepeatedBool(options, field, index)
+              : reflection->GetBool(options, field)));
+      return;
+    case FieldDescriptor::CPPTYPE_STRING: {
+      const std::string& val =
+          field->is_repeated()
+              ? reflection->GetRepeatedString(options, field, index)
+              : reflection->GetString(options, field);
+      if (field->type() == FieldDescriptor::TYPE_STRING) {
+        value->PackFrom(WrapValue<StringValue>(val));
+      } else {
+        value->PackFrom(WrapValue<BytesValue>(val));
+      }
+      return;
+    }
+    case FieldDescriptor::CPPTYPE_ENUM: {
+      const EnumValueDescriptor* val =
+          field->is_repeated()
+              ? reflection->GetRepeatedEnum(options, field, index)
+              : reflection->GetEnum(options, field);
+      value->PackFrom(WrapValue<Int32Value>(val->number()));
+      return;
+    }
+  }
+}
+
+// Implementation details for Convert*Options.
+void ConvertOptionsInternal(const Message& options,
+                            RepeatedPtrField<Option>& output) {
+  const Reflection* reflection = options.GetReflection();
+  std::vector<const FieldDescriptor*> fields;
+  reflection->ListFields(options, &fields);
+  for (const FieldDescriptor* field : fields) {
+    if (field->is_repeated()) {
+      const int size = reflection->FieldSize(options, field);
+      for (int i = 0; i < size; ++i) {
+        ConvertOptionField(reflection, options, field, i, output.Add());
+      }
+    } else {
+      ConvertOptionField(reflection, options, field, -1, output.Add());
+    }
+  }
+}
+
+void ConvertMessageOptions(const MessageOptions& options,
+                           RepeatedPtrField<Option>& output) {
+  return ConvertOptionsInternal(options, output);
+}
+
+void ConvertFieldOptions(const FieldOptions& options,
+                         RepeatedPtrField<Option>& output) {
+  return ConvertOptionsInternal(options, output);
+}
+
+void ConvertEnumOptions(const EnumOptions& options,
+                        RepeatedPtrField<Option>& output) {
+  return ConvertOptionsInternal(options, output);
+}
+
+void ConvertEnumValueOptions(const EnumValueOptions& options,
+                             RepeatedPtrField<Option>& output) {
+  return ConvertOptionsInternal(options, output);
+}
+
+std::string DefaultValueAsString(const FieldDescriptor& descriptor) {
+  switch (descriptor.cpp_type()) {
+    case FieldDescriptor::CPPTYPE_INT32:
+      return absl::StrCat(descriptor.default_value_int32());
+      break;
+    case FieldDescriptor::CPPTYPE_INT64:
+      return absl::StrCat(descriptor.default_value_int64());
+      break;
+    case FieldDescriptor::CPPTYPE_UINT32:
+      return absl::StrCat(descriptor.default_value_uint32());
+      break;
+    case FieldDescriptor::CPPTYPE_UINT64:
+      return absl::StrCat(descriptor.default_value_uint64());
+      break;
+    case FieldDescriptor::CPPTYPE_FLOAT:
+      return io::SimpleFtoa(descriptor.default_value_float());
+      break;
+    case FieldDescriptor::CPPTYPE_DOUBLE:
+      return io::SimpleDtoa(descriptor.default_value_double());
+      break;
+    case FieldDescriptor::CPPTYPE_BOOL:
+      return descriptor.default_value_bool() ? "true" : "false";
+      break;
+    case FieldDescriptor::CPPTYPE_STRING:
+      if (descriptor.type() == FieldDescriptor::TYPE_BYTES) {
+        return absl::CEscape(descriptor.default_value_string());
+      } else {
+        return descriptor.default_value_string();
+      }
+      break;
+    case FieldDescriptor::CPPTYPE_ENUM:
+      return descriptor.default_value_enum()->name();
+      break;
+    case FieldDescriptor::CPPTYPE_MESSAGE:
+      ABSL_DLOG(FATAL) << "Messages can't have default values!";
+      break;
+  }
+  return "";
+}
+
+template <typename T>
+std::string GetTypeUrl(absl::string_view url_prefix, const T& descriptor) {
+  return absl::StrCat(url_prefix, "/", descriptor.full_name());
+}
+
+void ConvertFieldDescriptor(absl::string_view url_prefix,
+                            const FieldDescriptor& descriptor, Field* field) {
+  field->set_kind(static_cast<Field::Kind>(descriptor.type()));
+  switch (descriptor.label()) {
+    case FieldDescriptor::LABEL_OPTIONAL:
+      field->set_cardinality(Field::CARDINALITY_OPTIONAL);
+      break;
+    case FieldDescriptor::LABEL_REPEATED:
+      field->set_cardinality(Field::CARDINALITY_REPEATED);
+      break;
+    case FieldDescriptor::LABEL_REQUIRED:
+      field->set_cardinality(Field::CARDINALITY_REQUIRED);
+      break;
+  }
+  field->set_number(descriptor.number());
+  field->set_name(descriptor.name());
+  field->set_json_name(descriptor.json_name());
+  if (descriptor.has_default_value()) {
+    field->set_default_value(DefaultValueAsString(descriptor));
+  }
+  if (descriptor.type() == FieldDescriptor::TYPE_MESSAGE ||
+      descriptor.type() == FieldDescriptor::TYPE_GROUP) {
+    field->set_type_url(GetTypeUrl(url_prefix, *descriptor.message_type()));
+  } else if (descriptor.type() == FieldDescriptor::TYPE_ENUM) {
+    field->set_type_url(GetTypeUrl(url_prefix, *descriptor.enum_type()));
+  }
+  if (descriptor.containing_oneof() != nullptr) {
+    field->set_oneof_index(descriptor.containing_oneof()->index() + 1);
+  }
+  if (descriptor.is_packed()) {
+    field->set_packed(true);
+  }
+
+  ConvertFieldOptions(descriptor.options(), *field->mutable_options());
+}
+
+Syntax ConvertSyntax(FileDescriptor::Syntax syntax) {
+  switch (syntax) {
+    default:
+      return Syntax::SYNTAX_PROTO2;
+  }
+}
+
+void ConvertEnumDescriptor(const EnumDescriptor& descriptor, Enum* enum_type) {
+  enum_type->Clear();
+  enum_type->set_syntax(ConvertSyntax(descriptor.file()->syntax()));
+
+  enum_type->set_name(descriptor.full_name());
+  enum_type->mutable_source_context()->set_file_name(descriptor.file()->name());
+  for (int i = 0; i < descriptor.value_count(); ++i) {
+    const EnumValueDescriptor& value_descriptor = *descriptor.value(i);
+    EnumValue* value = enum_type->mutable_enumvalue()->Add();
+    value->set_name(value_descriptor.name());
+    value->set_number(value_descriptor.number());
+
+    ConvertEnumValueOptions(value_descriptor.options(),
+                            *value->mutable_options());
+  }
+
+  ConvertEnumOptions(descriptor.options(), *enum_type->mutable_options());
+}
+
+void ConvertDescriptor(absl::string_view url_prefix,
+                       const Descriptor& descriptor, Type* type) {
+  type->Clear();
+  type->set_name(descriptor.full_name());
+  type->set_syntax(ConvertSyntax(descriptor.file()->syntax()));
+  for (int i = 0; i < descriptor.field_count(); ++i) {
+    ConvertFieldDescriptor(url_prefix, *descriptor.field(i),
+                           type->add_fields());
+  }
+  for (int i = 0; i < descriptor.oneof_decl_count(); ++i) {
+    type->add_oneofs(descriptor.oneof_decl(i)->name());
+  }
+  type->mutable_source_context()->set_file_name(descriptor.file()->name());
+  ConvertMessageOptions(descriptor.options(), *type->mutable_options());
+}
+
 class DescriptorPoolTypeResolver : public TypeResolver {
  public:
   DescriptorPoolTypeResolver(absl::string_view url_prefix,
@@ -82,11 +330,11 @@
     }
 
     const Descriptor* descriptor = pool_->FindMessageTypeByName(type_name);
-    if (descriptor == NULL) {
+    if (descriptor == nullptr) {
       return absl::NotFoundError(
           absl::StrCat("Invalid type URL, unknown type: ", type_name));
     }
-    ConvertDescriptor(descriptor, type);
+    ConvertDescriptor(url_prefix_, *descriptor, type);
     return absl::Status();
   }
 
@@ -99,213 +347,15 @@
     }
 
     const EnumDescriptor* descriptor = pool_->FindEnumTypeByName(type_name);
-    if (descriptor == NULL) {
+    if (descriptor == nullptr) {
       return absl::InvalidArgumentError(
           absl::StrCat("Invalid type URL, unknown type: ", type_name));
     }
-    ConvertEnumDescriptor(descriptor, enum_type);
+    ConvertEnumDescriptor(*descriptor, enum_type);
     return absl::Status();
   }
 
  private:
-  void ConvertDescriptor(const Descriptor* descriptor, Type* type) {
-    type->Clear();
-    type->set_name(descriptor->full_name());
-    for (int i = 0; i < descriptor->field_count(); ++i) {
-      ConvertFieldDescriptor(descriptor->field(i), type->add_fields());
-    }
-    for (int i = 0; i < descriptor->oneof_decl_count(); ++i) {
-      type->add_oneofs(descriptor->oneof_decl(i)->name());
-    }
-    type->mutable_source_context()->set_file_name(descriptor->file()->name());
-    ConvertMessageOptions(descriptor->options(), type->mutable_options());
-  }
-
-  void ConvertMessageOptions(const MessageOptions& options,
-                             RepeatedPtrField<Option>* output) {
-    return ConvertOptionsInternal(options, output);
-  }
-
-  void ConvertFieldOptions(const FieldOptions& options,
-                           RepeatedPtrField<Option>* output) {
-    return ConvertOptionsInternal(options, output);
-  }
-
-  void ConvertEnumOptions(const EnumOptions& options,
-                          RepeatedPtrField<Option>* output) {
-    return ConvertOptionsInternal(options, output);
-  }
-
-  void ConvertEnumValueOptions(const EnumValueOptions& options,
-                               RepeatedPtrField<Option>* output) {
-    return ConvertOptionsInternal(options, output);
-  }
-
-  // Implementation details for Convert*Options.
-  void ConvertOptionsInternal(const Message& options,
-                              RepeatedPtrField<Option>* output) {
-    const Reflection* reflection = options.GetReflection();
-    std::vector<const FieldDescriptor*> fields;
-    reflection->ListFields(options, &fields);
-    for (const FieldDescriptor* field : fields) {
-      if (field->is_repeated()) {
-        const int size = reflection->FieldSize(options, field);
-        for (int i = 0; i < size; i++) {
-          ConvertOptionField(reflection, options, field, i, output->Add());
-        }
-      } else {
-        ConvertOptionField(reflection, options, field, -1, output->Add());
-      }
-    }
-  }
-
-  static void ConvertOptionField(const Reflection* reflection,
-                                 const Message& options,
-                                 const FieldDescriptor* field, int index,
-                                 Option* out) {
-    out->set_name(field->is_extension() ? field->full_name() : field->name());
-    Any* value = out->mutable_value();
-    switch (field->cpp_type()) {
-      case FieldDescriptor::CPPTYPE_MESSAGE:
-        value->PackFrom(
-            field->is_repeated()
-                ? reflection->GetRepeatedMessage(options, field, index)
-                : reflection->GetMessage(options, field));
-        return;
-      case FieldDescriptor::CPPTYPE_DOUBLE:
-        value->PackFrom(WrapValue<DoubleValue>(
-            field->is_repeated()
-                ? reflection->GetRepeatedDouble(options, field, index)
-                : reflection->GetDouble(options, field)));
-        return;
-      case FieldDescriptor::CPPTYPE_FLOAT:
-        value->PackFrom(WrapValue<FloatValue>(
-            field->is_repeated()
-                ? reflection->GetRepeatedFloat(options, field, index)
-                : reflection->GetFloat(options, field)));
-        return;
-      case FieldDescriptor::CPPTYPE_INT64:
-        value->PackFrom(WrapValue<Int64Value>(
-            field->is_repeated()
-                ? reflection->GetRepeatedInt64(options, field, index)
-                : reflection->GetInt64(options, field)));
-        return;
-      case FieldDescriptor::CPPTYPE_UINT64:
-        value->PackFrom(WrapValue<UInt64Value>(
-            field->is_repeated()
-                ? reflection->GetRepeatedUInt64(options, field, index)
-                : reflection->GetUInt64(options, field)));
-        return;
-      case FieldDescriptor::CPPTYPE_INT32:
-        value->PackFrom(WrapValue<Int32Value>(
-            field->is_repeated()
-                ? reflection->GetRepeatedInt32(options, field, index)
-                : reflection->GetInt32(options, field)));
-        return;
-      case FieldDescriptor::CPPTYPE_UINT32:
-        value->PackFrom(WrapValue<UInt32Value>(
-            field->is_repeated()
-                ? reflection->GetRepeatedUInt32(options, field, index)
-                : reflection->GetUInt32(options, field)));
-        return;
-      case FieldDescriptor::CPPTYPE_BOOL:
-        value->PackFrom(WrapValue<BoolValue>(
-            field->is_repeated()
-                ? reflection->GetRepeatedBool(options, field, index)
-                : reflection->GetBool(options, field)));
-        return;
-      case FieldDescriptor::CPPTYPE_STRING: {
-        const std::string& val =
-            field->is_repeated()
-                ? reflection->GetRepeatedString(options, field, index)
-                : reflection->GetString(options, field);
-        if (field->type() == FieldDescriptor::TYPE_STRING) {
-          value->PackFrom(WrapValue<StringValue>(val));
-        } else {
-          value->PackFrom(WrapValue<BytesValue>(val));
-        }
-        return;
-      }
-      case FieldDescriptor::CPPTYPE_ENUM: {
-        const EnumValueDescriptor* val =
-            field->is_repeated()
-                ? reflection->GetRepeatedEnum(options, field, index)
-                : reflection->GetEnum(options, field);
-        value->PackFrom(WrapValue<Int32Value>(val->number()));
-        return;
-      }
-    }
-  }
-
-  template <typename WrapperT, typename T>
-  static WrapperT WrapValue(T value) {
-    WrapperT wrapper;
-    wrapper.set_value(value);
-    return wrapper;
-  }
-
-  void ConvertFieldDescriptor(const FieldDescriptor* descriptor, Field* field) {
-    field->set_kind(static_cast<Field::Kind>(descriptor->type()));
-    switch (descriptor->label()) {
-      case FieldDescriptor::LABEL_OPTIONAL:
-        field->set_cardinality(Field::CARDINALITY_OPTIONAL);
-        break;
-      case FieldDescriptor::LABEL_REPEATED:
-        field->set_cardinality(Field::CARDINALITY_REPEATED);
-        break;
-      case FieldDescriptor::LABEL_REQUIRED:
-        field->set_cardinality(Field::CARDINALITY_REQUIRED);
-        break;
-    }
-    field->set_number(descriptor->number());
-    field->set_name(descriptor->name());
-    field->set_json_name(descriptor->json_name());
-    if (descriptor->has_default_value()) {
-      field->set_default_value(DefaultValueAsString(descriptor));
-    }
-    if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE ||
-        descriptor->type() == FieldDescriptor::TYPE_GROUP) {
-      field->set_type_url(GetTypeUrl(descriptor->message_type()));
-    } else if (descriptor->type() == FieldDescriptor::TYPE_ENUM) {
-      field->set_type_url(GetTypeUrl(descriptor->enum_type()));
-    }
-    if (descriptor->containing_oneof() != NULL) {
-      field->set_oneof_index(descriptor->containing_oneof()->index() + 1);
-    }
-    if (descriptor->is_packed()) {
-      field->set_packed(true);
-    }
-
-    ConvertFieldOptions(descriptor->options(), field->mutable_options());
-  }
-
-  void ConvertEnumDescriptor(const EnumDescriptor* descriptor,
-                             Enum* enum_type) {
-    enum_type->Clear();
-    enum_type->set_name(descriptor->full_name());
-    enum_type->mutable_source_context()->set_file_name(
-        descriptor->file()->name());
-    for (int i = 0; i < descriptor->value_count(); ++i) {
-      const EnumValueDescriptor* value_descriptor = descriptor->value(i);
-      EnumValue* value = enum_type->mutable_enumvalue()->Add();
-      value->set_name(value_descriptor->name());
-      value->set_number(value_descriptor->number());
-
-      ConvertEnumValueOptions(value_descriptor->options(),
-                              value->mutable_options());
-    }
-
-    ConvertEnumOptions(descriptor->options(), enum_type->mutable_options());
-  }
-
-  std::string GetTypeUrl(const Descriptor* descriptor) {
-    return absl::StrCat(url_prefix_, "/", descriptor->full_name());
-  }
-
-  std::string GetTypeUrl(const EnumDescriptor* descriptor) {
-    return absl::StrCat(url_prefix_, "/", descriptor->full_name());
-  }
-
   absl::Status ParseTypeUrl(absl::string_view type_url,
                             std::string* type_name) {
     absl::string_view stripped = type_url;
@@ -319,46 +369,6 @@
     return absl::Status();
   }
 
-  std::string DefaultValueAsString(const FieldDescriptor* descriptor) {
-    switch (descriptor->cpp_type()) {
-      case FieldDescriptor::CPPTYPE_INT32:
-        return absl::StrCat(descriptor->default_value_int32());
-        break;
-      case FieldDescriptor::CPPTYPE_INT64:
-        return absl::StrCat(descriptor->default_value_int64());
-        break;
-      case FieldDescriptor::CPPTYPE_UINT32:
-        return absl::StrCat(descriptor->default_value_uint32());
-        break;
-      case FieldDescriptor::CPPTYPE_UINT64:
-        return absl::StrCat(descriptor->default_value_uint64());
-        break;
-      case FieldDescriptor::CPPTYPE_FLOAT:
-        return io::SimpleFtoa(descriptor->default_value_float());
-        break;
-      case FieldDescriptor::CPPTYPE_DOUBLE:
-        return io::SimpleDtoa(descriptor->default_value_double());
-        break;
-      case FieldDescriptor::CPPTYPE_BOOL:
-        return descriptor->default_value_bool() ? "true" : "false";
-        break;
-      case FieldDescriptor::CPPTYPE_STRING:
-        if (descriptor->type() == FieldDescriptor::TYPE_BYTES) {
-          return absl::CEscape(descriptor->default_value_string());
-        } else {
-          return descriptor->default_value_string();
-        }
-        break;
-      case FieldDescriptor::CPPTYPE_ENUM:
-        return descriptor->default_value_enum()->name();
-        break;
-      case FieldDescriptor::CPPTYPE_MESSAGE:
-        ABSL_DLOG(FATAL) << "Messages can't have default values!";
-        break;
-    }
-    return "";
-  }
-
   std::string url_prefix_;
   const DescriptorPool* pool_;
 };
@@ -370,6 +380,21 @@
   return new DescriptorPoolTypeResolver(url_prefix, pool);
 }
 
+// Performs a direct conversion from a descriptor to a type proto.
+Type ConvertDescriptorToType(absl::string_view url_prefix,
+                             const Descriptor& descriptor) {
+  Type type;
+  ConvertDescriptor(url_prefix, descriptor, &type);
+  return type;
+}
+
+// Performs a direct conversion from an enum descriptor to a type proto.
+Enum ConvertDescriptorToType(const EnumDescriptor& descriptor) {
+  Enum enum_type;
+  ConvertEnumDescriptor(descriptor, &enum_type);
+  return enum_type;
+}
+
 }  // namespace util
 }  // namespace protobuf
 }  // namespace google
diff --git a/src/google/protobuf/util/type_resolver_util.h b/src/google/protobuf/util/type_resolver_util.h
index 3670fcb..6e43ade 100644
--- a/src/google/protobuf/util/type_resolver_util.h
+++ b/src/google/protobuf/util/type_resolver_util.h
@@ -33,7 +33,9 @@
 #ifndef GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_UTIL_H__
 #define GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_UTIL_H__
 
+#include "google/protobuf/type.pb.h"
 #include "absl/strings/string_view.h"
+#include "google/protobuf/descriptor.h"
 
 // Must be included last.
 #include "google/protobuf/port_def.inc"
@@ -49,6 +51,14 @@
 PROTOBUF_EXPORT TypeResolver* NewTypeResolverForDescriptorPool(
     absl::string_view url_prefix, const DescriptorPool* pool);
 
+// Performs a direct conversion from a descriptor to a type proto.
+PROTOBUF_EXPORT google::protobuf::Type ConvertDescriptorToType(
+    absl::string_view url_prefix, const Descriptor& descriptor);
+
+// Performs a direct conversion from an enum descriptor to a type proto.
+PROTOBUF_EXPORT google::protobuf::Enum ConvertDescriptorToType(
+    const EnumDescriptor& descriptor);
+
 }  // namespace util
 }  // namespace protobuf
 }  // namespace google
diff --git a/src/google/protobuf/util/type_resolver_util_test.cc b/src/google/protobuf/util/type_resolver_util_test.cc
index 8a54bca..eaf6649 100644
--- a/src/google/protobuf/util/type_resolver_util_test.cc
+++ b/src/google/protobuf/util/type_resolver_util_test.cc
@@ -36,16 +36,19 @@
 #include <string>
 #include <vector>
 
+#include "google/protobuf/any.pb.h"
 #include "google/protobuf/type.pb.h"
 #include "google/protobuf/wrappers.pb.h"
+#include "google/protobuf/descriptor.pb.h"
 #include "google/protobuf/util/type_resolver.h"
-#include "google/protobuf/testing/googletest.h"
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include "google/protobuf/descriptor.h"
 #include "google/protobuf/util/json_format_proto3.pb.h"
 #include "google/protobuf/map_unittest.pb.h"
-#include "google/protobuf/test_util.h"
 #include "google/protobuf/unittest.pb.h"
 #include "google/protobuf/unittest_custom_options.pb.h"
+#include "google/protobuf/unittest_import.pb.h"
 
 namespace google {
 namespace protobuf {
@@ -57,11 +60,113 @@
 using google::protobuf::Field;
 using google::protobuf::Int32Value;
 using google::protobuf::Option;
+using google::protobuf::Syntax;
 using google::protobuf::Type;
 using google::protobuf::UInt64Value;
 
 static const char kUrlPrefix[] = "type.googleapis.com";
 
+const Field* FindField(const Type& type, absl::string_view name) {
+  for (const Field& field : type.fields()) {
+    if (field.name() == name) {
+      return &field;
+    }
+  }
+  return nullptr;
+}
+
+bool HasField(const Type& type, Field::Cardinality cardinality,
+              Field::Kind kind, absl::string_view name, int number) {
+  const Field* field = FindField(type, name);
+  if (field == nullptr) {
+    return false;
+  }
+  return field->cardinality() == cardinality && field->kind() == kind &&
+         field->number() == number;
+}
+
+bool CheckFieldTypeUrl(const Type& type, absl::string_view name,
+                       absl::string_view type_url) {
+  const Field* field = FindField(type, name);
+  if (field == nullptr) {
+    return false;
+  }
+  return field->type_url() == type_url;
+}
+
+bool FieldInOneof(const Type& type, absl::string_view name,
+                  absl::string_view oneof_name) {
+  const Field* field = FindField(type, name);
+  if (field == nullptr || field->oneof_index() <= 0 ||
+      field->oneof_index() > type.oneofs_size()) {
+    return false;
+  }
+  return type.oneofs(field->oneof_index() - 1) == oneof_name;
+}
+
+bool IsPacked(const Type& type, absl::string_view name) {
+  const Field* field = FindField(type, name);
+  if (field == nullptr) {
+    return false;
+  }
+  return field->packed();
+}
+
+const EnumValue* FindEnumValue(const Enum& type, absl::string_view name) {
+  for (const EnumValue& value : type.enumvalue()) {
+    if (value.name() == name) {
+      return &value;
+    }
+  }
+  return nullptr;
+}
+
+bool EnumHasValue(const Enum& type, absl::string_view name, int number) {
+  const EnumValue* value = FindEnumValue(type, name);
+  if (value == nullptr) {
+    return false;
+  }
+  return value->number() == number;
+}
+
+template <typename WrapperT, typename T>
+bool HasOption(const RepeatedPtrField<Option>& options, absl::string_view name,
+               T value) {
+  for (const Option& option : options) {
+    if (option.name() == name) {
+      WrapperT wrapper;
+      if (option.value().UnpackTo(&wrapper) && wrapper.value() == value) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+bool HasBoolOption(const RepeatedPtrField<Option>& options,
+                   absl::string_view name, bool value) {
+  return HasOption<BoolValue>(options, name, value);
+}
+
+bool HasInt32Option(const RepeatedPtrField<Option>& options,
+                    absl::string_view name, int32_t value) {
+  return HasOption<Int32Value>(options, name, value);
+}
+
+bool HasUInt64Option(const RepeatedPtrField<Option>& options,
+                     absl::string_view name, uint64_t value) {
+  return HasOption<UInt64Value>(options, name, value);
+}
+
+std::string GetTypeUrl(std::string full_name) {
+  return kUrlPrefix + std::string("/") + full_name;
+}
+
+template <typename T>
+std::string GetTypeUrl() {
+  return GetTypeUrl(T::descriptor()->full_name());
+}
+
 class DescriptorPoolTypeResolverTest : public testing::Test {
  public:
   DescriptorPoolTypeResolverTest() {
@@ -69,105 +174,6 @@
         kUrlPrefix, DescriptorPool::generated_pool()));
   }
 
-  const Field* FindField(const Type& type, const std::string& name) {
-    for (int i = 0; i < type.fields_size(); ++i) {
-      const Field& field = type.fields(i);
-      if (field.name() == name) {
-        return &field;
-      }
-    }
-    return nullptr;
-  }
-
-  bool HasField(const Type& type, Field::Cardinality cardinality,
-                Field::Kind kind, const std::string& name, int number) {
-    const Field* field = FindField(type, name);
-    if (field == nullptr) {
-      return false;
-    }
-    return field->cardinality() == cardinality && field->kind() == kind &&
-           field->number() == number;
-  }
-
-  bool CheckFieldTypeUrl(const Type& type, const std::string& name,
-                         const std::string& type_url) {
-    const Field* field = FindField(type, name);
-    if (field == nullptr) {
-      return false;
-    }
-    return field->type_url() == type_url;
-  }
-
-  bool FieldInOneof(const Type& type, const std::string& name,
-                    const std::string& oneof_name) {
-    const Field* field = FindField(type, name);
-    if (field == nullptr || field->oneof_index() <= 0 ||
-        field->oneof_index() > type.oneofs_size()) {
-      return false;
-    }
-    return type.oneofs(field->oneof_index() - 1) == oneof_name;
-  }
-
-  bool IsPacked(const Type& type, const std::string& name) {
-    const Field* field = FindField(type, name);
-    if (field == nullptr) {
-      return false;
-    }
-    return field->packed();
-  }
-
-  const EnumValue* FindEnumValue(const Enum& type, const std::string& name) {
-    for (const EnumValue& value : type.enumvalue()) {
-      if (value.name() == name) {
-        return &value;
-      }
-    }
-    return nullptr;
-  }
-
-  bool EnumHasValue(const Enum& type, const std::string& name, int number) {
-    const EnumValue* value = FindEnumValue(type, name);
-    return value != nullptr && value->number() == number;
-  }
-
-  bool HasBoolOption(const RepeatedPtrField<Option>& options,
-                     const std::string& name, bool value) {
-    return HasOption<BoolValue>(options, name, value);
-  }
-
-  bool HasInt32Option(const RepeatedPtrField<Option>& options,
-                      const std::string& name, int32_t value) {
-    return HasOption<Int32Value>(options, name, value);
-  }
-
-  bool HasUInt64Option(const RepeatedPtrField<Option>& options,
-                       const std::string& name, uint64_t value) {
-    return HasOption<UInt64Value>(options, name, value);
-  }
-
-  template <typename WrapperT, typename T>
-  bool HasOption(const RepeatedPtrField<Option>& options,
-                 const std::string& name, T value) {
-    for (const Option& option : options) {
-      if (option.name() == name) {
-        WrapperT wrapper;
-        if (option.value().UnpackTo(&wrapper) && wrapper.value() == value) {
-          return true;
-        }
-      }
-    }
-    return false;
-  }
-
-  std::string GetTypeUrl(std::string full_name) {
-    return kUrlPrefix + std::string("/") + full_name;
-  }
-
-  template <typename T>
-  std::string GetTypeUrl() {
-    return GetTypeUrl(T::descriptor()->full_name());
-  }
-
  protected:
   std::unique_ptr<TypeResolver> resolver_;
 };
@@ -432,6 +438,260 @@
   EXPECT_EQ("@value", FindField(type, "value")->json_name());
 }
 
+class DescriptorPoolTypeResolverSyntaxTest : public testing::Test {
+ protected:
+  DescriptorPoolTypeResolverSyntaxTest()
+      : resolver_(NewTypeResolverForDescriptorPool(kUrlPrefix, &pool_)) {}
+
+  const FileDescriptor* BuildFile(
+      absl::string_view syntax,
+      absl::optional<absl::string_view> edition = absl::nullopt) {
+    FileDescriptorProto proto;
+    proto.set_package("test");
+    proto.set_name("foo");
+    proto.set_syntax(syntax);
+    if (edition.has_value()) {
+      proto.set_edition(*edition);
+    }
+    DescriptorProto* message = proto.add_message_type();
+    message->set_name("MyMessage");
+    const FileDescriptor* file = pool_.BuildFile(proto);
+    ABSL_CHECK(file != nullptr);
+    return file;
+  }
+
+  DescriptorPool pool_;
+  std::unique_ptr<TypeResolver> resolver_;
+};
+
+TEST_F(DescriptorPoolTypeResolverSyntaxTest, SyntaxProto2) {
+  const FileDescriptor* file = BuildFile("proto2");
+  ASSERT_EQ(FileDescriptor::SYNTAX_PROTO2, file->syntax());
+
+  Type type;
+  ASSERT_TRUE(
+      resolver_->ResolveMessageType(GetTypeUrl("test.MyMessage"), &type).ok());
+  EXPECT_EQ(type.syntax(), Syntax::SYNTAX_PROTO2);
+  EXPECT_EQ(type.edition(), "");
+}
+
+TEST_F(DescriptorPoolTypeResolverSyntaxTest, SyntaxProto3) {
+  const FileDescriptor* file = BuildFile("proto3");
+  ASSERT_EQ(FileDescriptor::SYNTAX_PROTO3, file->syntax());
+
+  Type type;
+  ASSERT_TRUE(
+      resolver_->ResolveMessageType(GetTypeUrl("test.MyMessage"), &type).ok());
+  // TODO(b/271206501) This should be proto3.
+  EXPECT_EQ(type.syntax(), Syntax::SYNTAX_PROTO2);
+  EXPECT_EQ(type.edition(), "");
+}
+
+
+TEST(ConvertDescriptorToTypeTest, TestAllTypes) {
+  Type type = ConvertDescriptorToType(
+      kUrlPrefix, *protobuf_unittest::TestAllTypes::GetDescriptor());
+
+  // Check all optional fields.
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_INT32,
+                       "optional_int32", 1));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_INT64,
+                       "optional_int64", 2));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_UINT32,
+                       "optional_uint32", 3));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_UINT64,
+                       "optional_uint64", 4));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_SINT32,
+                       "optional_sint32", 5));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_SINT64,
+                       "optional_sint64", 6));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_FIXED32,
+                       "optional_fixed32", 7));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_FIXED64,
+                       "optional_fixed64", 8));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_SFIXED32,
+                       "optional_sfixed32", 9));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_SFIXED64,
+                       "optional_sfixed64", 10));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_FLOAT,
+                       "optional_float", 11));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_DOUBLE,
+                       "optional_double", 12));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_BOOL,
+                       "optional_bool", 13));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_STRING,
+                       "optional_string", 14));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_BYTES,
+                       "optional_bytes", 15));
+
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_GROUP,
+                       "optionalgroup", 16));
+
+  EXPECT_TRUE(CheckFieldTypeUrl(
+      type, "optionalgroup",
+      GetTypeUrl<protobuf_unittest::TestAllTypes::OptionalGroup>()));
+
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_MESSAGE,
+                       "optional_nested_message", 18));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_MESSAGE,
+                       "optional_foreign_message", 19));
+
+  EXPECT_TRUE(CheckFieldTypeUrl(
+      type, "optional_nested_message",
+      GetTypeUrl<protobuf_unittest::TestAllTypes::NestedMessage>()));
+  EXPECT_TRUE(CheckFieldTypeUrl(type, "optional_foreign_message",
+                                GetTypeUrl<protobuf_unittest::ForeignMessage>()));
+
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_ENUM,
+                       "optional_nested_enum", 21));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_ENUM,
+                       "optional_foreign_enum", 22));
+
+  EXPECT_TRUE(
+      CheckFieldTypeUrl(type, "optional_nested_enum",
+                        GetTypeUrl("protobuf_unittest.TestAllTypes.NestedEnum")));
+  EXPECT_TRUE(CheckFieldTypeUrl(type, "optional_foreign_enum",
+                                GetTypeUrl("protobuf_unittest.ForeignEnum")));
+
+  // Check all repeated fields.
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_INT32,
+                       "repeated_int32", 31));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_INT64,
+                       "repeated_int64", 32));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_UINT32,
+                       "repeated_uint32", 33));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_UINT64,
+                       "repeated_uint64", 34));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_SINT32,
+                       "repeated_sint32", 35));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_SINT64,
+                       "repeated_sint64", 36));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_FIXED32,
+                       "repeated_fixed32", 37));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_FIXED64,
+                       "repeated_fixed64", 38));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_SFIXED32,
+                       "repeated_sfixed32", 39));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_SFIXED64,
+                       "repeated_sfixed64", 40));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_FLOAT,
+                       "repeated_float", 41));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_DOUBLE,
+                       "repeated_double", 42));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_BOOL,
+                       "repeated_bool", 43));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_STRING,
+                       "repeated_string", 44));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_BYTES,
+                       "repeated_bytes", 45));
+
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_GROUP,
+                       "repeatedgroup", 46));
+
+  EXPECT_TRUE(CheckFieldTypeUrl(
+      type, "repeatedgroup",
+      GetTypeUrl<protobuf_unittest::TestAllTypes::RepeatedGroup>()));
+
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_MESSAGE,
+                       "repeated_nested_message", 48));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_MESSAGE,
+                       "repeated_foreign_message", 49));
+
+  EXPECT_TRUE(CheckFieldTypeUrl(
+      type, "repeated_nested_message",
+      GetTypeUrl<protobuf_unittest::TestAllTypes::NestedMessage>()));
+  EXPECT_TRUE(CheckFieldTypeUrl(type, "repeated_foreign_message",
+                                GetTypeUrl<protobuf_unittest::ForeignMessage>()));
+
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_ENUM,
+                       "repeated_nested_enum", 51));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_ENUM,
+                       "repeated_foreign_enum", 52));
+
+  EXPECT_TRUE(
+      CheckFieldTypeUrl(type, "repeated_nested_enum",
+                        GetTypeUrl("protobuf_unittest.TestAllTypes.NestedEnum")));
+  EXPECT_TRUE(CheckFieldTypeUrl(type, "repeated_foreign_enum",
+                                GetTypeUrl("protobuf_unittest.ForeignEnum")));
+}
+
+TEST(ConvertDescriptorToTypeTest, ConvertDescriptorToTypePacked) {
+  Type type = ConvertDescriptorToType(
+      kUrlPrefix, *protobuf_unittest::TestPackedTypes::GetDescriptor());
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_INT32,
+                       "packed_int32", 90));
+  EXPECT_TRUE(IsPacked(type, "packed_int32"));
+}
+
+TEST(ConvertDescriptorToTypeTest, TestOneof) {
+  Type type = ConvertDescriptorToType(
+      kUrlPrefix, *protobuf_unittest::TestAllTypes::GetDescriptor());
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_UINT32,
+                       "oneof_uint32", 111));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_MESSAGE,
+                       "oneof_nested_message", 112));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_STRING,
+                       "oneof_string", 113));
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL, Field::TYPE_BYTES,
+                       "oneof_bytes", 114));
+  EXPECT_TRUE(FieldInOneof(type, "oneof_uint32", "oneof_field"));
+  EXPECT_TRUE(FieldInOneof(type, "oneof_nested_message", "oneof_field"));
+  EXPECT_TRUE(FieldInOneof(type, "oneof_string", "oneof_field"));
+  EXPECT_TRUE(FieldInOneof(type, "oneof_bytes", "oneof_field"));
+}
+
+TEST(ConvertDescriptorToTypeTest, TestMap) {
+  Type type = ConvertDescriptorToType(
+      kUrlPrefix, *protobuf_unittest::TestMap::GetDescriptor());
+  EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED, Field::TYPE_MESSAGE,
+                       "map_int32_int32", 1));
+  EXPECT_TRUE(CheckFieldTypeUrl(
+      type, "map_int32_int32",
+      GetTypeUrl("protobuf_unittest.TestMap.MapInt32Int32Entry")));
+}
+
+TEST(ConvertDescriptorToTypeTest, TestCustomOptions) {
+  Type type = ConvertDescriptorToType(
+      kUrlPrefix,
+      *protobuf_unittest::TestMessageWithCustomOptions::GetDescriptor());
+  EXPECT_TRUE(
+      HasInt32Option(type.options(), "protobuf_unittest.message_opt1", -56));
+  const Field* field = FindField(type, "field1");
+  ASSERT_TRUE(field != nullptr);
+  EXPECT_TRUE(HasUInt64Option(field->options(), "protobuf_unittest.field_opt1",
+                              8765432109));
+}
+
+TEST(ConvertDescriptorToTypeTest, TestJsonName) {
+  Type type = ConvertDescriptorToType(
+      kUrlPrefix, *protobuf_unittest::TestAllTypes::GetDescriptor());
+  EXPECT_EQ("optionalInt32", FindField(type, "optional_int32")->json_name());
+
+  type = ConvertDescriptorToType(kUrlPrefix,
+                                 *proto3::TestCustomJsonName::GetDescriptor());
+  EXPECT_EQ("@value", FindField(type, "value")->json_name());
+}
+
+TEST(ConvertDescriptorToTypeTest, TestEnum) {
+  Enum type = ConvertDescriptorToType(
+      *protobuf_unittest::TestAllTypes::NestedEnum_descriptor());
+  EnumHasValue(type, "FOO", 1);
+  EnumHasValue(type, "BAR", 2);
+  EnumHasValue(type, "BAZ", 3);
+  EnumHasValue(type, "NEG", -1);
+}
+
+TEST(ConvertDescriptorToTypeTest, TestCustomEnumOptions) {
+  Enum type = ConvertDescriptorToType(
+      *protobuf_unittest::TestMessageWithCustomOptions::AnEnum_descriptor());
+  ASSERT_TRUE(
+      HasInt32Option(type.options(), "protobuf_unittest.enum_opt1", -789));
+  const EnumValue* value = FindEnumValue(type, "ANENUM_VAL2");
+  ASSERT_TRUE(value != nullptr);
+  ASSERT_TRUE(
+      HasInt32Option(value->options(), "protobuf_unittest.enum_value_opt1", 123));
+}
+
 }  // namespace
 }  // namespace util
 }  // namespace protobuf
diff --git a/src/google/protobuf/wire_format.cc b/src/google/protobuf/wire_format.cc
index 27aa64f..fe62d1c 100644
--- a/src/google/protobuf/wire_format.cc
+++ b/src/google/protobuf/wire_format.cc
@@ -874,7 +874,7 @@
             ptr = internal::PackedEnumParser(rep_enum, ptr, ctx);
           } else {
             return ctx->ReadPackedVarint(
-                ptr, [rep_enum, field, reflection, msg](uint64_t val) {
+                ptr, [rep_enum, field, reflection, msg](int32_t val) {
                   if (field->enum_type()->FindValueByNumber(val) != nullptr) {
                     rep_enum->Add(val);
                   } else {
@@ -1466,11 +1466,11 @@
   // Write type ID.
   target = WireFormatLite::WriteUInt32ToArray(
       WireFormatLite::kMessageSetTypeIdNumber, field->number(), target);
-  // Write message.
-  auto& msg = message_reflection->GetMessage(message, field);
-  target = WireFormatLite::InternalWriteMessage(
-      WireFormatLite::kMessageSetMessageNumber, msg, msg.GetCachedSize(),
-      target, stream);
+    // Write message.
+    auto& msg = message_reflection->GetMessage(message, field);
+    target = WireFormatLite::InternalWriteMessage(
+        WireFormatLite::kMessageSetMessageNumber, msg, msg.GetCachedSize(),
+        target, stream);
   // End group.
   target = stream->EnsureSpace(target);
   target = io::CodedOutputStream::WriteTagToArray(
diff --git a/src/google/protobuf/wrappers.pb.h b/src/google/protobuf/wrappers.pb.h
index 0b41068..e5d676a 100644
--- a/src/google/protobuf/wrappers.pb.h
+++ b/src/google/protobuf/wrappers.pb.h
@@ -9,7 +9,7 @@
 #include <type_traits>
 
 #include "google/protobuf/port_def.inc"
-#if PROTOBUF_VERSION < 4022000
+#if PROTOBUF_VERSION < 3021000
 #error "This file was generated by a newer version of protoc which is"
 #error "incompatible with your Protocol Buffer headers. Please update"
 #error "your headers."
@@ -136,6 +136,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
@@ -283,6 +290,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
@@ -430,6 +444,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
@@ -577,6 +598,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
@@ -724,6 +752,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
@@ -871,6 +906,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
@@ -1018,6 +1060,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
@@ -1165,6 +1214,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
@@ -1322,6 +1378,13 @@
     return *this;
   }
 
+  inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const {
+    return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance);
+  }
+  inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() {
+    return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
+  }
+
   static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
     return GetDescriptor();
   }
diff --git a/src/libprotobuf-lite.map b/src/libprotobuf-lite.map
index 3915546..a1853ca 100644
--- a/src/libprotobuf-lite.map
+++ b/src/libprotobuf-lite.map
@@ -3,6 +3,8 @@
     extern "C++" {
       *google*;
     };
+    scc_info_*;
+    descriptor_table_*;
 
   local:
     *;
diff --git a/src/libprotobuf.map b/src/libprotobuf.map
index 3915546..a1853ca 100644
--- a/src/libprotobuf.map
+++ b/src/libprotobuf.map
@@ -3,6 +3,8 @@
     extern "C++" {
       *google*;
     };
+    scc_info_*;
+    descriptor_table_*;
 
   local:
     *;
diff --git a/src/libprotoc.map b/src/libprotoc.map
index 3915546..a1853ca 100644
--- a/src/libprotoc.map
+++ b/src/libprotoc.map
@@ -3,6 +3,8 @@
     extern "C++" {
       *google*;
     };
+    scc_info_*;
+    descriptor_table_*;
 
   local:
     *;
diff --git a/version.json b/version.json
index 9593f13..695db4b 100644
--- a/version.json
+++ b/version.json
@@ -1,17 +1,17 @@
 {
-    "22.x": {
-        "protoc_version": "22.2-dev",
+    "main": {
+        "protoc_version": "22-dev",
         "lts": false,
-        "date": "2023-03-07",
+        "date": "2022-07-21",
         "languages": {
-            "cpp": "4.22.2-dev",
-            "csharp": "3.22.2-dev",
-            "java": "3.22.2-dev",
-            "javascript": "3.22.2-dev",
-            "objectivec": "3.22.2-dev",
-            "php": "3.22.2-dev",
-            "python": "4.22.2-dev",
-            "ruby": "3.22.2-dev"
+            "cpp": "4.22-dev",
+            "csharp": "3.22-dev",
+            "java": "3.22-dev",
+            "javascript": "3.22-dev",
+            "objectivec": "3.22-dev",
+            "php": "3.22-dev",
+            "python": "4.22-dev",
+            "ruby": "3.22-dev"
         }
     }
-}
\ No newline at end of file
+}