diff --git a/.bazelrc b/.bazelrc index 7bec860d..6e6ef67a 100644 --- a/.bazelrc +++ b/.bazelrc @@ -8,6 +8,12 @@ build --host_cxxopt=-std=c++20 # host toolchain, so the guard is Linux-scoped. common:linux --repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 +# rules_foreign_cc invokes host tools such as cmake from sandboxed actions. +# On macOS those tools usually live under Homebrew, which is not always present +# in Bazel's default action PATH. +build:macos --action_env=PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin +build:macos --repo_env=PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + # Force C11 for host tools to work around rules_foreign_cc pkg-config # bootstrap failure: bundled glib uses 'bool' as a struct field name, # which is a keyword in GCC 15's default C23 mode. diff --git a/.github/scripts/install-cmake-deps-ubuntu.sh b/.github/scripts/install-cmake-deps-ubuntu.sh new file mode 100644 index 00000000..70edc77e --- /dev/null +++ b/.github/scripts/install-cmake-deps-ubuntu.sh @@ -0,0 +1,159 @@ +#!/usr/bin/env bash +# Copyright 2026 keplertech.io +# SPDX-License-Identifier: GPL-3.0-only + +set -euo pipefail + +llvm_version="${KEPLER_LLVM_VERSION:-22}" +# shellcheck disable=SC1091 +source /etc/os-release +ubuntu_codename="${VERSION_CODENAME:-jammy}" +llvm_keyring="/usr/share/keyrings/llvm-snapshot.gpg" +llvm_list="/etc/apt/sources.list.d/llvm-toolchain-${ubuntu_codename}-${llvm_version}.list" + +sudo apt-get update +sudo apt-get install -yq ca-certificates curl gnupg lsb-release + +if [[ ! -f "${llvm_keyring}" ]]; then + curl -fsSL https://apt.llvm.org/llvm-snapshot.gpg.key \ + | sudo gpg --dearmor -o "${llvm_keyring}" +fi +printf 'deb [signed-by=%s] https://apt.llvm.org/%s/ llvm-toolchain-%s-%s main\n' \ + "${llvm_keyring}" "${ubuntu_codename}" "${ubuntu_codename}" "${llvm_version}" \ + | sudo tee "${llvm_list}" >/dev/null + +sudo apt-get update +sudo apt-get install -yq \ + build-essential cmake ninja-build pkg-config curl ca-certificates \ + bison flex doxygen python3-dev \ + "clang-${llvm_version}" "clang-tools-${llvm_version}" \ + "llvm-${llvm_version}-dev" \ + "libclang-${llvm_version}-dev" "libclang-cpp${llvm_version}-dev" \ + libboost-dev libboost-iostreams-dev libfl-dev \ + capnproto libcapnp-dev libtbb-dev libspdlog-dev libfmt-dev \ + libgtest-dev libssl-dev \ + libz3-dev zlib1g-dev \ + "$@" + +absl_version="20260107.0" +protobuf_version="${KEPLER_PROTOBUF_VERSION:-35.1}" +re2_version="${KEPLER_RE2_VERSION:-2025-11-05}" +ortools_version="${KEPLER_ORTOOLS_VERSION:-9.15}" +absl_prefix="/usr/local" +absl_config="${absl_prefix}/lib/cmake/absl/abslConfig.cmake" + +if [[ ! -f "${absl_config}" ]]; then + work_dir="${RUNNER_TEMP:-/tmp}/abseil-cpp-${absl_version}" + src_dir="${work_dir}/src" + build_dir="${work_dir}/build" + + rm -rf "${work_dir}" + mkdir -p "${work_dir}" + curl -L "https://github.com/abseil/abseil-cpp/archive/refs/tags/${absl_version}.tar.gz" \ + -o "${work_dir}/abseil-cpp.tar.gz" + tar -xzf "${work_dir}/abseil-cpp.tar.gz" -C "${work_dir}" + mv "${work_dir}/abseil-cpp-${absl_version}" "${src_dir}" + + cmake -S "${src_dir}" -B "${build_dir}" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_CXX_STANDARD=20 \ + -DCMAKE_C_COMPILER="clang-${llvm_version}" \ + -DCMAKE_CXX_COMPILER="clang++-${llvm_version}" \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DABSL_BUILD_TESTING=OFF \ + -DABSL_ENABLE_INSTALL=ON \ + -DABSL_PROPAGATE_CXX_STD=ON \ + -DCMAKE_INSTALL_PREFIX="${absl_prefix}" + cmake --build "${build_dir}" -j "$(nproc)" + sudo cmake --install "${build_dir}" + sudo ldconfig +fi + +re2_prefix="/usr/local" +re2_header="${re2_prefix}/include/re2/re2.h" + +if [[ ! -f "${re2_header}" ]]; then + work_dir="${RUNNER_TEMP:-/tmp}/re2-${re2_version}" + src_dir="${work_dir}/src" + build_dir="${work_dir}/build" + + rm -rf "${work_dir}" + mkdir -p "${work_dir}" + curl -L "https://github.com/google/re2/archive/refs/tags/${re2_version}.tar.gz" \ + -o "${work_dir}/re2.tar.gz" + tar -xzf "${work_dir}/re2.tar.gz" -C "${work_dir}" + mv "${work_dir}/re2-${re2_version}" "${src_dir}" + + cmake -S "${src_dir}" -B "${build_dir}" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_CXX_STANDARD=20 \ + -DCMAKE_C_COMPILER="clang-${llvm_version}" \ + -DCMAKE_CXX_COMPILER="clang++-${llvm_version}" \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DRE2_BUILD_TESTING=OFF \ + -DCMAKE_PREFIX_PATH="${absl_prefix}" \ + -DCMAKE_INSTALL_PREFIX="${re2_prefix}" + cmake --build "${build_dir}" -j "$(nproc)" + sudo cmake --install "${build_dir}" + sudo ldconfig +fi + +protobuf_prefix="/usr/local" +protobuf_config="${protobuf_prefix}/lib/cmake/protobuf/protobuf-config.cmake" + +if [[ ! -f "${protobuf_config}" ]]; then + work_dir="${RUNNER_TEMP:-/tmp}/protobuf-${protobuf_version}" + src_dir="${work_dir}/src" + build_dir="${work_dir}/build" + + rm -rf "${work_dir}" + mkdir -p "${work_dir}" + curl -L "https://github.com/protocolbuffers/protobuf/releases/download/v${protobuf_version}/protobuf-${protobuf_version}.tar.gz" \ + -o "${work_dir}/protobuf.tar.gz" + tar -xzf "${work_dir}/protobuf.tar.gz" -C "${work_dir}" + mv "${work_dir}/protobuf-${protobuf_version}" "${src_dir}" + + cmake -S "${src_dir}" -B "${build_dir}" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_CXX_STANDARD=20 \ + -DCMAKE_C_COMPILER="clang-${llvm_version}" \ + -DCMAKE_CXX_COMPILER="clang++-${llvm_version}" \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -Dprotobuf_BUILD_TESTS=OFF \ + -DBUILD_SHARED_LIBS=ON \ + -Dprotobuf_BUILD_SHARED_LIBS=ON \ + -Dprotobuf_ABSL_PROVIDER=package \ + -Dprotobuf_BUILD_PROTOC_BINARIES=ON \ + -Dprotobuf_INSTALL=ON \ + -DCMAKE_PREFIX_PATH="${absl_prefix}" \ + -DCMAKE_INSTALL_PREFIX="${protobuf_prefix}" + cmake --build "${build_dir}" -j "$(nproc)" + sudo cmake --install "${build_dir}" + sudo ldconfig +fi + +ortools_prefix="/usr/local" +ortools_header="${ortools_prefix}/include/ortools/graph/graph.h" + +if [[ ! -f "${ortools_header}" ]]; then + work_dir="${RUNNER_TEMP:-/tmp}/or-tools-${ortools_version}" + src_dir="${work_dir}/src" + + rm -rf "${work_dir}" + mkdir -p "${src_dir}" + curl -L \ + "https://github.com/google/or-tools/releases/download/v${ortools_version}/or-tools-${ortools_version}.tar.gz" \ + -o "${work_dir}/or-tools.tar.gz" + tar -xzf "${work_dir}/or-tools.tar.gz" \ + -C "${src_dir}" \ + --strip-components=1 + + sudo mkdir -p "${ortools_prefix}/include" + sudo cp -R "${src_dir}/ortools" "${ortools_prefix}/include/" +fi + +if [[ -n "${GITHUB_ENV:-}" ]]; then + echo "CPATH=${ortools_prefix}/include${CPATH:+:${CPATH}}" >> "${GITHUB_ENV}" + echo "CPLUS_INCLUDE_PATH=${ortools_prefix}/include${CPLUS_INCLUDE_PATH:+:${CPLUS_INCLUDE_PATH}}" >> "${GITHUB_ENV}" + echo "CXXFLAGS=-I${ortools_prefix}/include${CXXFLAGS:+ ${CXXFLAGS}}" >> "${GITHUB_ENV}" +fi diff --git a/.github/workflows/bazel.yml b/.github/workflows/bazel.yml index fa80fc0f..6d266632 100644 --- a/.github/workflows/bazel.yml +++ b/.github/workflows/bazel.yml @@ -13,13 +13,16 @@ jobs: steps: - uses: actions/checkout@v4 - # No submodules needed — Bazel fetches deps via http_archive + with: + submodules: true + # Bazel fetches most deps via http_archive/BCR, but the C2RTL overlay + # deliberately builds from the pinned thirdparty/xls checkout. - name: Cache Bazel outputs uses: actions/cache@v4 with: path: ~/.cache/bazel - key: bazel-${{ runner.os }}-${{ hashFiles('MODULE.bazel', 'bazel/deps.bzl', 'bazel/*.BUILD.bazel') }} + key: bazel-${{ runner.os }}-${{ hashFiles('MODULE.bazel', 'bazel/deps.bzl', 'bazel/*.BUILD.bazel', 'bazel/*.bzl') }} restore-keys: | bazel-${{ runner.os }}- @@ -28,7 +31,7 @@ jobs: sudo apt-get update sudo apt-get install -yq \ build-essential pkg-config \ - bison flex python3-dev + bison cmake flex ninja-build python3-dev - name: Build kepler-formal run: bazelisk build //src/bin:kepler-formal diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index cb04820e..5dcd6157 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -18,25 +18,17 @@ jobs: - uses: actions/checkout@v4 with: submodules: true - # install dependencies - - name: Install GoogleTest - run: sudo apt-get update && sudo apt-get install -y libgtest-dev cmake - name: Checkout submodules run: git submodule update --init --recursive - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -yq \ - build-essential cmake ninja-build clang pkg-config \ - libboost-dev libfl-dev libtbb-dev capnproto libcapnp-dev \ - libgtest-dev libspdlog-dev libfmt-dev libboost-iostreams-dev zlib1g-dev + run: bash .github/scripts/install-cmake-deps-ubuntu.sh - name: Configure CMake working-directory: ${{github.workspace}}/ run: | rm -rf build - cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -GNinja -DCMAKE_C_COMPILER=clang -DENABLE_SANITIZERS=ON -DPYTHON_INTERFACE=OFF -DCMAKE_CXX_STANDARD=20 + cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -GNinja -DCMAKE_C_COMPILER=clang-22 -DCMAKE_CXX_COMPILER=clang++-22 -DCMAKE_PREFIX_PATH="/usr/local;/usr/lib/llvm-22" -DCMAKE_CXX_COMPILER_CLANG_SCAN_DEPS=clang-scan-deps-22 -DENABLE_SANITIZERS=ON -DPYTHON_INTERFACE=OFF -DCMAKE_CXX_STANDARD=20 - name: Build @@ -47,4 +39,3 @@ jobs: env: PYTHONMALLOC: malloc run: ctest -VV -E ".*[p|P]ython.*" -C ${{ env.BUILD_TYPE }} - \ No newline at end of file diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index eb843e1b..b811bec0 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -20,19 +20,11 @@ jobs: fetch-depth: 0 submodules: true - # install dependencies - - name: Install GoogleTest - run: sudo apt-get update && sudo apt-get install -y libgtest-dev cmake - name: Checkout submodules run: git submodule update --init --recursive - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -yq \ - build-essential cmake ninja-build clang pkg-config \ - libboost-dev libfl-dev libtbb-dev capnproto libcapnp-dev \ - libgtest-dev libspdlog-dev libfmt-dev libboost-iostreams-dev zlib1g-dev lcov + run: bash .github/scripts/install-cmake-deps-ubuntu.sh lcov - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. @@ -41,14 +33,14 @@ jobs: GIT_CONFIG_COUNT: "1" GIT_CONFIG_KEY_0: core.abbrev GIT_CONFIG_VALUE_0: "7" - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCODE_COVERAGE=ON -DLONG_TESTS=ON + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-22 -DCMAKE_CXX_COMPILER=clang++-22 -DCMAKE_PREFIX_PATH="/usr/local;/usr/lib/llvm-22" -DCMAKE_CXX_COMPILER_CLANG_SCAN_DEPS=clang-scan-deps-22 -DCODE_COVERAGE=ON -DLONG_TESTS=ON -DCMAKE_CXX_STANDARD=20 - name: Build # Build your program with the given configuration run: cmake --build ${{github.workspace}}/build -j 4 --config ${{env.BUILD_TYPE}} env: - CC: clang - CXX: clang++ + CC: clang-22 + CXX: clang++-22 - name: Test working-directory: ${{github.workspace}}/build diff --git a/.github/workflows/cva6imc.yml b/.github/workflows/cva6imc.yml index f303a8cc..8c35a180 100644 --- a/.github/workflows/cva6imc.yml +++ b/.github/workflows/cva6imc.yml @@ -17,12 +17,7 @@ jobs: submodules: true - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - libgtest-dev cmake pkg-config libboost-dev libfl-dev \ - capnproto libcapnp-dev ninja-build libtbb-dev libspdlog-dev \ - libboost-iostreams-dev zlib1g-dev + run: bash .github/scripts/install-cmake-deps-ubuntu.sh - name: Checkout submodules run: git submodule update --init --recursive @@ -31,6 +26,10 @@ jobs: run: | cmake -B ${{github.workspace}}/build \ -GNinja \ + -DCMAKE_C_COMPILER=clang-22 \ + -DCMAKE_CXX_COMPILER=clang++-22 \ + -DCMAKE_PREFIX_PATH="/usr/local;/usr/lib/llvm-22" \ + -DCMAKE_CXX_COMPILER_CLANG_SCAN_DEPS=clang-scan-deps-22 \ -DPYTHON_INTERFACE=OFF \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_CXX_STANDARD=20 \ diff --git a/.github/workflows/cva6ki.yml b/.github/workflows/cva6ki.yml index 080dff21..7af5d430 100644 --- a/.github/workflows/cva6ki.yml +++ b/.github/workflows/cva6ki.yml @@ -17,12 +17,7 @@ jobs: submodules: true - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - libgtest-dev cmake pkg-config libboost-dev libfl-dev \ - capnproto libcapnp-dev ninja-build libtbb-dev libspdlog-dev \ - libboost-iostreams-dev zlib1g-dev + run: bash .github/scripts/install-cmake-deps-ubuntu.sh - name: Checkout submodules run: git submodule update --init --recursive @@ -31,6 +26,10 @@ jobs: run: | cmake -B ${{github.workspace}}/build \ -GNinja \ + -DCMAKE_C_COMPILER=clang-22 \ + -DCMAKE_CXX_COMPILER=clang++-22 \ + -DCMAKE_PREFIX_PATH="/usr/local;/usr/lib/llvm-22" \ + -DCMAKE_CXX_COMPILER_CLANG_SCAN_DEPS=clang-scan-deps-22 \ -DPYTHON_INTERFACE=OFF \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_CXX_STANDARD=20 \ diff --git a/.github/workflows/cva6pdr.yml b/.github/workflows/cva6pdr.yml index b4718e51..32d857dd 100644 --- a/.github/workflows/cva6pdr.yml +++ b/.github/workflows/cva6pdr.yml @@ -17,12 +17,7 @@ jobs: submodules: true - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - libgtest-dev cmake pkg-config libboost-dev libfl-dev \ - capnproto libcapnp-dev ninja-build libtbb-dev libspdlog-dev \ - libboost-iostreams-dev zlib1g-dev + run: bash .github/scripts/install-cmake-deps-ubuntu.sh - name: Checkout submodules run: git submodule update --init --recursive @@ -31,6 +26,10 @@ jobs: run: | cmake -B ${{github.workspace}}/build \ -GNinja \ + -DCMAKE_C_COMPILER=clang-22 \ + -DCMAKE_CXX_COMPILER=clang++-22 \ + -DCMAKE_PREFIX_PATH="/usr/local;/usr/lib/llvm-22" \ + -DCMAKE_CXX_COMPILER_CLANG_SCAN_DEPS=clang-scan-deps-22 \ -DPYTHON_INTERFACE=OFF \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_CXX_STANDARD=20 \ diff --git a/.github/workflows/macOS.yml b/.github/workflows/macOS.yml index 2fdc1c70..633bef47 100644 --- a/.github/workflows/macOS.yml +++ b/.github/workflows/macOS.yml @@ -27,15 +27,38 @@ jobs: run: git submodule update --init --recursive # install dependencies - name: Install dependencies - run: brew install cmake doxygen capnp tbb bison flex boost spdlog zlib + run: brew install cmake doxygen capnp tbb bison flex boost spdlog zlib abseil protobuf openssl@3 llvm z3 re2 googletest - name: set env variable run: | echo "/usr/local/opt/flex/bin" >> $GITHUB_PATH; echo "/usr/local/opt/bison/bin" >> $GITHUB_PATH; echo "/opt/homebrew/opt/flex/bin" >> $GITHUB_PATH; echo "/opt/homebrew/opt/bison/bin" >> $GITHUB_PATH - - name: Install GTest - run: brew install googletest + echo "GTEST_PREFIX=$(brew --prefix googletest)" >> $GITHUB_ENV + echo "CMAKE_PREFIX_PATH=$(brew --prefix llvm);$(brew --prefix openssl@3);$(brew --prefix abseil);$(brew --prefix protobuf);$(brew --prefix re2);$(brew --prefix z3);$(brew --prefix googletest)" >> $GITHUB_ENV + echo "CPATH=$(brew --prefix googletest)/include${CPATH:+:${CPATH}}" >> $GITHUB_ENV + echo "CPLUS_INCLUDE_PATH=$(brew --prefix googletest)/include${CPLUS_INCLUDE_PATH:+:${CPLUS_INCLUDE_PATH}}" >> $GITHUB_ENV + - name: Install OR-Tools headers + run: | + ORTOOLS_VERSION=9.15 + ORTOOLS_HEADER=/usr/local/include/ortools/graph/graph.h + if [ ! -f "${ORTOOLS_HEADER}" ]; then + WORK_DIR="${RUNNER_TEMP}/or-tools-${ORTOOLS_VERSION}" + SRC_DIR="${WORK_DIR}/src" + rm -rf "${WORK_DIR}" + mkdir -p "${SRC_DIR}" + curl -L \ + "https://github.com/google/or-tools/releases/download/v${ORTOOLS_VERSION}/or-tools-${ORTOOLS_VERSION}.tar.gz" \ + -o "${WORK_DIR}/or-tools.tar.gz" + tar -xzf "${WORK_DIR}/or-tools.tar.gz" \ + -C "${SRC_DIR}" \ + --strip-components=1 + sudo mkdir -p /usr/local/include + sudo cp -R "${SRC_DIR}/ortools" /usr/local/include/ + fi + echo "CPATH=/usr/local/include${CPATH:+:${CPATH}}" >> "${GITHUB_ENV}" + echo "CPLUS_INCLUDE_PATH=/usr/local/include${CPLUS_INCLUDE_PATH:+:${CPLUS_INCLUDE_PATH}}" >> "${GITHUB_ENV}" + echo "CXXFLAGS=-I/usr/local/include${CXXFLAGS:+ ${CXXFLAGS}}" >> "${GITHUB_ENV}" - name: Configure CMake - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -GNinja -DCMAKE_C_COMPILER=clang -DENABLE_SANITIZERS=ON -DPYTHON_INTERFACE=OFF -DCMAKE_CXX_STANDARD=20 + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -GNinja -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -DCMAKE_CXX_FLAGS="-I/usr/local/include -I${GTEST_PREFIX}/include" -DCMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH}" -DENABLE_SANITIZERS=ON -DPYTHON_INTERFACE=OFF -DCMAKE_CXX_STANDARD=20 - name: Build # Build your program with the given configuration run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} diff --git a/.github/workflows/regress-imc.yml b/.github/workflows/regress-imc.yml index d0b9ad5a..6a8fd903 100644 --- a/.github/workflows/regress-imc.yml +++ b/.github/workflows/regress-imc.yml @@ -32,12 +32,7 @@ jobs: submodules: true - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - libgtest-dev cmake pkg-config libboost-dev libfl-dev \ - capnproto libcapnp-dev ninja-build libtbb-dev libspdlog-dev \ - libboost-iostreams-dev zlib1g-dev + run: bash .github/scripts/install-cmake-deps-ubuntu.sh - name: Checkout submodules run: git submodule update --init --recursive @@ -46,6 +41,10 @@ jobs: run: | cmake -B ${{github.workspace}}/build-sec-regress \ -GNinja \ + -DCMAKE_C_COMPILER=clang-22 \ + -DCMAKE_CXX_COMPILER=clang++-22 \ + -DCMAKE_PREFIX_PATH="/usr/local;/usr/lib/llvm-22" \ + -DCMAKE_CXX_COMPILER_CLANG_SCAN_DEPS=clang-scan-deps-22 \ -DPYTHON_INTERFACE=OFF \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_CXX_STANDARD=20 \ diff --git a/.github/workflows/regress-ki.yml b/.github/workflows/regress-ki.yml index 20c5c632..7288beb9 100644 --- a/.github/workflows/regress-ki.yml +++ b/.github/workflows/regress-ki.yml @@ -32,12 +32,7 @@ jobs: submodules: true - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - libgtest-dev cmake pkg-config libboost-dev libfl-dev \ - capnproto libcapnp-dev ninja-build libtbb-dev libspdlog-dev \ - libboost-iostreams-dev zlib1g-dev + run: bash .github/scripts/install-cmake-deps-ubuntu.sh - name: Checkout submodules run: git submodule update --init --recursive @@ -46,6 +41,10 @@ jobs: run: | cmake -B ${{github.workspace}}/build-sec-regress \ -GNinja \ + -DCMAKE_C_COMPILER=clang-22 \ + -DCMAKE_CXX_COMPILER=clang++-22 \ + -DCMAKE_PREFIX_PATH="/usr/local;/usr/lib/llvm-22" \ + -DCMAKE_CXX_COMPILER_CLANG_SCAN_DEPS=clang-scan-deps-22 \ -DPYTHON_INTERFACE=OFF \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_CXX_STANDARD=20 \ diff --git a/.github/workflows/regress-lec.yml b/.github/workflows/regress-lec.yml index c8489b41..7c97586b 100644 --- a/.github/workflows/regress-lec.yml +++ b/.github/workflows/regress-lec.yml @@ -20,17 +20,14 @@ jobs: with: submodules: true - - name: Install GoogleTest - run: sudo apt-get update && sudo apt-get install -y libgtest-dev cmake - - name: Checkout submodules run: git submodule update --init --recursive - - name: Install boost & capnproto - run: sudo apt-get update && sudo apt-get install -y pkg-config libboost-dev libfl-dev capnproto libcapnp-dev ninja-build clang libtbb-dev libspdlog-dev libboost-iostreams-dev zlib1g-dev + - name: Install dependencies + run: bash .github/scripts/install-cmake-deps-ubuntu.sh - name: Configure CMake - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -GNinja -DCMAKE_C_COMPILER=clang -DENABLE_SANITIZERS=ON -DPYTHON_INTERFACE=OFF -DCMAKE_CXX_STANDARD=20 + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -GNinja -DCMAKE_C_COMPILER=clang-22 -DCMAKE_CXX_COMPILER=clang++-22 -DCMAKE_PREFIX_PATH="/usr/local;/usr/lib/llvm-22" -DCMAKE_CXX_COMPILER_CLANG_SCAN_DEPS=clang-scan-deps-22 -DENABLE_SANITIZERS=ON -DPYTHON_INTERFACE=OFF -DCMAKE_CXX_STANDARD=20 - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} diff --git a/.github/workflows/regress-pdr.yml b/.github/workflows/regress-pdr.yml index cdeb52be..eff346f8 100644 --- a/.github/workflows/regress-pdr.yml +++ b/.github/workflows/regress-pdr.yml @@ -38,12 +38,7 @@ jobs: submodules: true - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - libgtest-dev cmake pkg-config libboost-dev libfl-dev \ - capnproto libcapnp-dev ninja-build libtbb-dev libspdlog-dev \ - libboost-iostreams-dev zlib1g-dev + run: bash .github/scripts/install-cmake-deps-ubuntu.sh - name: Checkout submodules run: git submodule update --init --recursive @@ -52,6 +47,10 @@ jobs: run: | cmake -B ${{github.workspace}}/build-sec-regress \ -GNinja \ + -DCMAKE_C_COMPILER=clang-22 \ + -DCMAKE_CXX_COMPILER=clang++-22 \ + -DCMAKE_PREFIX_PATH="/usr/local;/usr/lib/llvm-22" \ + -DCMAKE_CXX_COMPILER_CLANG_SCAN_DEPS=clang-scan-deps-22 \ -DPYTHON_INTERFACE=OFF \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_CXX_STANDARD=20 \ diff --git a/.github/workflows/regress-sec.yml b/.github/workflows/regress-sec.yml index a5624f52..6efd3586 100644 --- a/.github/workflows/regress-sec.yml +++ b/.github/workflows/regress-sec.yml @@ -22,12 +22,7 @@ jobs: submodules: true - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - libgtest-dev cmake pkg-config libboost-dev libfl-dev \ - capnproto libcapnp-dev ninja-build libtbb-dev libspdlog-dev \ - libboost-iostreams-dev zlib1g-dev + run: bash .github/scripts/install-cmake-deps-ubuntu.sh - name: Checkout submodules run: git submodule update --init --recursive @@ -36,6 +31,10 @@ jobs: run: | cmake -B ${{github.workspace}}/build-sec-regress \ -GNinja \ + -DCMAKE_C_COMPILER=clang-22 \ + -DCMAKE_CXX_COMPILER=clang++-22 \ + -DCMAKE_PREFIX_PATH="/usr/local;/usr/lib/llvm-22" \ + -DCMAKE_CXX_COMPILER_CLANG_SCAN_DEPS=clang-scan-deps-22 \ -DPYTHON_INTERFACE=OFF \ -DENABLE_UNIT_TESTS=ON \ -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/stage \ @@ -233,11 +232,7 @@ jobs: - uses: actions/checkout@v4 - name: Install runtime dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - git python3 python3-pip capnproto libcapnp-dev libtbb-dev \ - libboost-iostreams-dev zlib1g-dev + run: bash .github/scripts/install-cmake-deps-ubuntu.sh git python3 python3-pip - name: Download SEC runtime uses: actions/download-artifact@v4 @@ -504,11 +499,7 @@ jobs: - uses: actions/checkout@v4 - name: Install runtime dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - git python3 python3-pip capnproto libcapnp-dev libtbb-dev \ - libboost-iostreams-dev zlib1g-dev + run: bash .github/scripts/install-cmake-deps-ubuntu.sh git python3 python3-pip - name: Download SEC runtime uses: actions/download-artifact@v4 @@ -609,11 +600,7 @@ jobs: - uses: actions/checkout@v4 - name: Install runtime dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - git python3 python3-pip capnproto libcapnp-dev libtbb-dev \ - libboost-iostreams-dev zlib1g-dev + run: bash .github/scripts/install-cmake-deps-ubuntu.sh git python3 python3-pip - name: Download SEC runtime uses: actions/download-artifact@v4 diff --git a/.gitmodules b/.gitmodules index 0839eb19..d94358fd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,9 @@ [submodule "thirdparty/cadical"] path = thirdparty/cadical url = https://github.com/arminbiere/cadical +[submodule "thirdparty/xls"] + path = thirdparty/xls + url = https://github.com/keplertech/xls +[submodule "thirdparty/cppitertools"] + path = thirdparty/cppitertools + url = https://github.com/ryanhaining/cppitertools.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ad637f4..565eeb59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,59 @@ project(kepler-formal HOMEPAGE_URL https://github.com/keplertech/kepler-formal ) +set(KEPLER_XLS_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/xls) +set(CPPITERTOOLS_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/cppitertools) + +add_library(cppitertools INTERFACE) +target_include_directories(cppitertools INTERFACE ${CPPITERTOOLS_ROOT}) + +find_package(absl CONFIG REQUIRED) +find_package(Protobuf REQUIRED) +find_package(OpenSSL REQUIRED) +if(EXISTS "/opt/homebrew/opt/llvm/lib/cmake/llvm") + list(PREPEND CMAKE_PREFIX_PATH "/opt/homebrew/opt/llvm") +endif() +find_package(LLVM CONFIG REQUIRED) +find_package(Clang CONFIG REQUIRED) +find_path(Z3_INCLUDE_DIR z3.h + HINTS + /opt/homebrew/opt/z3/include + /opt/homebrew/include +) +find_library(Z3_LIBRARY z3 + HINTS + /opt/homebrew/opt/z3/lib + /opt/homebrew/lib +) +if(NOT Z3_INCLUDE_DIR OR NOT Z3_LIBRARY) + message(FATAL_ERROR "Z3 is required for XLS C2RTL") +endif() +add_library(z3::z3 UNKNOWN IMPORTED) +set_target_properties(z3::z3 PROPERTIES + IMPORTED_LOCATION "${Z3_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${Z3_INCLUDE_DIR}" +) +find_path(RE2_INCLUDE_DIR re2/re2.h + HINTS + ${CMAKE_BINARY_DIR}/_deps/re2-install/include + /opt/homebrew/opt/re2/include + /opt/homebrew/include +) +find_library(RE2_LIBRARY re2 + HINTS + ${CMAKE_BINARY_DIR}/_deps/re2-install/lib + /opt/homebrew/opt/re2/lib + /opt/homebrew/lib +) +if(NOT RE2_INCLUDE_DIR OR NOT RE2_LIBRARY) + message(FATAL_ERROR "RE2 is required for XLS C2RTL") +endif() +add_library(re2::re2 UNKNOWN IMPORTED) +set_target_properties(re2::re2 PROPERTIES + IMPORTED_LOCATION "${RE2_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${RE2_INCLUDE_DIR}" +) + set(ARGPARSE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/naja/thirdparty/argparse-3.1/include) set(CMAKE_CXX_STANDARD 20 CACHE STRING "C++ standard" FORCE) @@ -73,6 +126,28 @@ string(JOIN " " CADICAL_KITTEN_SYMBOL_PREFIX_FLAGS ${CADICAL_KITTEN_SYMBOL_PREFIX_DEFINES} ) +set(CADICAL_CFLAGS "${CADICAL_KITTEN_SYMBOL_PREFIX_FLAGS}") +set(CADICAL_CXXFLAGS "${CADICAL_KITTEN_SYMBOL_PREFIX_FLAGS}") +if(CMAKE_C_FLAGS) + string(PREPEND CADICAL_CFLAGS "${CMAKE_C_FLAGS} ") +endif() +if(CMAKE_CXX_FLAGS) + string(PREPEND CADICAL_CXXFLAGS "${CMAKE_CXX_FLAGS} ") +endif() +if(CMAKE_BUILD_TYPE) + string(TOUPPER "${CMAKE_BUILD_TYPE}" KEPLER_BUILD_TYPE_UPPER) + if(CMAKE_C_FLAGS_${KEPLER_BUILD_TYPE_UPPER}) + string(PREPEND CADICAL_CFLAGS "${CMAKE_C_FLAGS_${KEPLER_BUILD_TYPE_UPPER}} ") + endif() + if(CMAKE_CXX_FLAGS_${KEPLER_BUILD_TYPE_UPPER}) + string(PREPEND CADICAL_CXXFLAGS "${CMAKE_CXX_FLAGS_${KEPLER_BUILD_TYPE_UPPER}} ") + endif() +endif() +if(ENABLE_SANITIZERS AND CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") + string(APPEND CADICAL_CFLAGS " -fsanitize=address -fno-omit-frame-pointer") + string(APPEND CADICAL_CXXFLAGS " -fsanitize=address -fno-omit-frame-pointer") +endif() + ExternalProject_Add(kissat_external SOURCE_DIR ${KISSAT_ROOT} # Keep Kissat small and quiet for embedded use, but do not use @@ -115,8 +190,8 @@ ExternalProject_Add(cadical_external CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env "CC=${CMAKE_C_COMPILER}" "CXX=${CMAKE_CXX_COMPILER}" - "CFLAGS=${CADICAL_KITTEN_SYMBOL_PREFIX_FLAGS}" - "CXXFLAGS=${CADICAL_KITTEN_SYMBOL_PREFIX_FLAGS}" + "CFLAGS=${CADICAL_CFLAGS}" + "CXXFLAGS=${CADICAL_CXXFLAGS}" ./configure -q --no-tracing --no-contrib BUILD_COMMAND make -C build libcadical.a INSTALL_COMMAND "" diff --git a/MODULE.bazel b/MODULE.bazel index 09427263..3b64c0ed 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -14,6 +14,31 @@ bazel_dep(name = "yaml-cpp", version = "0.9.0") bazel_dep(name = "googletest", version = "1.17.0.bcr.2", dev_dependency = True) bazel_dep(name = "zlib", version = "1.3.2") bazel_dep(name = "spdlog", version = "1.17.0") +bazel_dep(name = "abseil-cpp", version = "20260107.0") +bazel_dep(name = "boringssl", version = "0.20250114.0") +bazel_dep(name = "googleapis", version = "0.0.0-20240819-fe8ba054a") +bazel_dep(name = "protobuf", version = "33.0") +bazel_dep(name = "re2", version = "2025-11-05.bcr.1") +bazel_dep(name = "rules_proto", version = "7.1.0") +bazel_dep(name = "rules_python", version = "1.9.0") +bazel_dep(name = "rules_shell", version = "0.6.1") +bazel_dep(name = "rules_go", version = "0.59.0") + +single_version_override( + module_name = "googleapis", + version = "0.0.0-20240819-fe8ba054a", +) +PYTHON_VERSION = "3.13" +PYTHON_VERSION_NAME = PYTHON_VERSION.replace(".", "_") +python = use_extension("@rules_python//python/extensions:python.bzl", "python") +python.toolchain( + python_version = PYTHON_VERSION, +) +use_repo( + python, + project_python = "python_{}".format(PYTHON_VERSION_NAME), + project_python_host = "python_{}_host".format(PYTHON_VERSION_NAME), +) # TBB comes from the cmake-built @onetbb repo (bazel/deps.bzl), not the BCR # onetbb module (which is Bazel-8-compatible nowadays): naja's shared libs # and the kepler-formal binary must share a single libtbb.so.12 runtime, @@ -62,6 +87,37 @@ register_toolchains( "@rules_foreign_cc//toolchains:preinstalled_pkgconfig_toolchain", ) +xls_c2rtl_repository = use_repo_rule( + "//bazel:xls_c2rtl_repo.bzl", + "xls_c2rtl_repository", +) + +xls_c2rtl_repository( + name = "xls_c2rtl_src", + build_file = "//bazel:xls_c2rtl.BUILD.bazel", + src_marker = "//:MODULE.bazel", + src_subdir = "thirdparty/xls", +) + +llvm_raw_ext = use_extension("//bazel:xls_llvm_extension.bzl", "llvm_raw_ext") +use_repo(llvm_raw_ext, "llvm-raw") +llvm_configure = use_repo_rule( + "@llvm-raw//utils/bazel:configure.bzl", + "llvm_configure", +) +llvm_configure( + name = "llvm-project", + targets = [ + "AArch64", + "X86", + ], +) +bazel_dep(name = "zlib-ng", version = "2.3.3", repo_name = "llvm_zlib") +bazel_dep(name = "zstd", version = "1.5.7.bcr.1", repo_name = "llvm_zstd") + +z3_ext = use_extension("//bazel:xls_z3_extension.bzl", "z3_extension") +use_repo(z3_ext, "z3") + # Third-party dependencies fetched via http_archive for BCR compatibility. # BUILD file overlays live in bazel/*.BUILD.bazel. # The cmake flow uses git submodules (thirdparty/) directly. @@ -72,6 +128,7 @@ use_repo( "boost_headers", "cadical", "capnproto", + "cppitertools", "flex_src", "glucose", "kissat", diff --git a/README.md b/README.md index 13645190..19bf87d4 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,8 @@ On Ubuntu: ```bash sudo apt-get install g++ libboost-dev python3.9-dev capnproto libcapnp-dev libtbb-dev pkg-config bison flex doxygen libspdlog-dev libfmt-dev libboost-iostreams-dev zlib1g-dev +# C2RTL CMake builds also need LLVM/Clang, Abseil, Protobuf, RE2, OpenSSL, +# Z3, and OR-Tools graph headers. See .github/scripts/install-cmake-deps-ubuntu.sh. ``` On macOS, using [Homebrew](https://brew.sh/): @@ -85,6 +87,8 @@ On macOS, using [Homebrew](https://brew.sh/): ```bash brew install cmake doxygen capnp tbb bison flex boost spdlog zlib ``` +C2RTL CMake builds also need LLVM, Abseil, Protobuf, OpenSSL, Z3, RE2, and +OR-Tools graph headers. The macOS CI workflow installs the pinned headers. Ensure the versions of `bison` and `flex` installed via Homebrew take precedence over the macOS defaults by modifying your $PATH environment variable as follows: @@ -139,7 +143,7 @@ errors are execution failures rather than SEC verdicts. ```bash # Single file per design -build/src/bin/kepler-formal <-verilog/-naja_if/-systemverilog/-sv/-sv2v> [options] \ +build/src/bin/kepler-formal <-verilog/-naja_if/-systemverilog/-sv/-sv2v/-cc/-cxx> [options] \ [...] # Multi-file Verilog @@ -162,9 +166,13 @@ build/src/bin/kepler-formal -sv -v sec \ | `-naja_if` | Parse both designs as Naja IF. | | `-systemverilog`, `-sv` | Parse both designs as SystemVerilog. Requires SEC. | | `-sv2v` | Parse design 1 as SystemVerilog and design 2 as Verilog for SEC RTL-vs-gate comparison. | +| `-cc`, `-cxx` | Synthesize one C/C++ translation unit per design to SystemVerilog with XLS, then run SEC on the generated RTL. | | `--design1 ` | Explicit source list for design 1 in multi-file Verilog mode. | | `--design2 ` | Explicit source list for design 2 in multi-file Verilog mode. | | `-sv`, `-systemverilog` | Use SystemVerilog input mode. | +| `--cc_top ` | C/C++ top function for both designs. Use `--cc_design1_top` / `--cc_design2_top` when they differ. | +| `--cc_include ` | Add an include directory for XLS C/C++ synthesis. May be repeated. | +| `--cc_output_dir ` | Directory for generated SystemVerilog. Defaults to `./kepler_formal_c2rtl`. | | `--liberty `, `--lib ` | Liberty library files. | | `--verilog_preprocessing` | Enable preprocessing for Verilog inputs. | @@ -177,12 +185,15 @@ build/src/bin/kepler-formal --config | Key | Type | Meaning | | --- | --- | --- | -| `format` | string | `verilog`, `v`, `naja_if`, `systemverilog`, `sv`, or `sv2v`. Defaults to `verilog` if omitted. | +| `format` | string | `verilog`, `v`, `naja_if`, `systemverilog`, `sv`, `sv2v`, `cc`, `c`, `cxx`, `cpp`, or `c2rtl`. Defaults to `verilog` if omitted. | | `verification` | string | `lec` or `sec`. Defaults to `lec`. | | `input_paths` | list | Required. Either `[design0, design1]` or `[[design0_file...], [design1_file...]]`. The nested form is for multi-file Verilog. | | `liberty_files` | list[string] | Liberty libraries loaded through `SNLLibertyConstructor`. | | `py_tech_files` | list[string] | Python primitive loaders loaded through `SNLPyLoader`. | | `verilog_preprocessing` | bool | Enable preprocessing for Verilog inputs. | +| `cc_top` | string | C/C++ top function for both designs. Use `cc_design1_top` / `cc_design2_top` when they differ. | +| `cc_include_paths` | list[string] | Include directories passed to XLS C/C++ synthesis. | +| `cc_output_dir` | string | Directory for generated SystemVerilog. Defaults to `./kepler_formal_c2rtl`. | | `solver` | string | `kissat` or `glucose`. Defaults to `kissat`. | | `log_file` | string | Path for the miter log file. Default logs are `miter_log_.txt` in the current working directory. | @@ -202,6 +213,22 @@ py_tech_files: verilog_preprocessing: true # Optional: enables Verilog preprocessor ``` +### C/C++ C2RTL Inputs + +The C/C++ mode uses the Kepler fork of XLS under `thirdparty/xls`. The +`kepler-formal` CMake build compiles the KF C2RTL bridge as the normal +`kepler_xls_c2rtl` library target and links it into the binary: + +```bash +cmake --build build --target kepler-formal +``` + +Then run: + +```bash +build/src/bin/kepler-formal -cc -v sec --cc_top top_function design0.cc design1.cc +``` + ## Examples See [example tests](example) diff --git a/bazel/BUILD.bazel b/bazel/BUILD.bazel index f2e6b73f..c7d20b04 100644 --- a/bazel/BUILD.bazel +++ b/bazel/BUILD.bazel @@ -1,6 +1,11 @@ # Package marker for bazel/ overlay directory -exports_files(["export_deps.sh"]) +exports_files([ + "export_deps.sh", + "xls_llvm.patch", + "xls_llvm_run_lit.patch", + "xls_llvm_zlib_header.patch", +]) load("@bazel_skylib//rules:common_settings.bzl", "bool_flag") load("@rules_cc//cc:cc_import.bzl", "cc_import") diff --git a/bazel/cppitertools.BUILD.bazel b/bazel/cppitertools.BUILD.bazel new file mode 100644 index 00000000..0e738785 --- /dev/null +++ b/bazel/cppitertools.BUILD.bazel @@ -0,0 +1,46 @@ +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "cppitertools", + hdrs = [ + "cppitertools/accumulate.hpp", + "cppitertools/batched.hpp", + "cppitertools/chain.hpp", + "cppitertools/chunked.hpp", + "cppitertools/combinations.hpp", + "cppitertools/combinations_with_replacement.hpp", + "cppitertools/compress.hpp", + "cppitertools/count.hpp", + "cppitertools/cycle.hpp", + "cppitertools/dropwhile.hpp", + "cppitertools/enumerate.hpp", + "cppitertools/filter.hpp", + "cppitertools/filterfalse.hpp", + "cppitertools/groupby.hpp", + "cppitertools/imap.hpp", + "cppitertools/itertools.hpp", + "cppitertools/permutations.hpp", + "cppitertools/powerset.hpp", + "cppitertools/product.hpp", + "cppitertools/range.hpp", + "cppitertools/repeat.hpp", + "cppitertools/reversed.hpp", + "cppitertools/slice.hpp", + "cppitertools/sliding_window.hpp", + "cppitertools/sorted.hpp", + "cppitertools/starmap.hpp", + "cppitertools/takewhile.hpp", + "cppitertools/unique_everseen.hpp", + "cppitertools/unique_justseen.hpp", + "cppitertools/zip.hpp", + "cppitertools/zip_longest.hpp", + ], + srcs = [ + "cppitertools/internal/iter_tuples.hpp", + "cppitertools/internal/iterator_wrapper.hpp", + "cppitertools/internal/iteratoriterator.hpp", + "cppitertools/internal/iterbase.hpp", + ], +) diff --git a/bazel/deps.bzl b/bazel/deps.bzl index b12b3e59..e0bc8e29 100644 --- a/bazel/deps.bzl +++ b/bazel/deps.bzl @@ -122,6 +122,7 @@ _NAJA_COMMIT = "e1a649e2fce182d8b7ca5c5c80ab5d04aad3ffa3" _NAJA_VERILOG_COMMIT = "8a13b5986c765035548775808273d61defcaf738" _NAJA_IF_COMMIT = "8719bf93fdcd65534c75eb7a8a1f69393f74a75a" _CPPTRACE_COMMIT = "3db8da80111171c219ab5839905771386bee06b3" +_CPPITERTOOLS_COMMIT = "5a7f4aa357ed9b0ad59823e3d2acd57217d5beaf" _SLANG_COMMIT = "512c327c209d3043aa98ecfd02d06a1b73fcd5fb" _GOOGLETEST_COMMIT = "52eb8108c5bdec04579160ae17225d66034bd723" @@ -158,6 +159,14 @@ def _deps_impl(_module_ctx): build_file = Label("//bazel:flexlexer.BUILD.bazel"), ) + http_archive( + name = "cppitertools", + url = "https://github.com/ryanhaining/cppitertools/archive/{}.tar.gz".format(_CPPITERTOOLS_COMMIT), + sha256 = "d61fdb7be3222c7b6c039daafd1f6ff54d7f2b9edb77240b1c34376a3038972e", + strip_prefix = "cppitertools-{}".format(_CPPITERTOOLS_COMMIT), + build_file = Label("//bazel:cppitertools.BUILD.bazel"), + ) + http_archive( name = "cadical", url = "https://github.com/arminbiere/cadical/archive/{}.tar.gz".format(_CADICAL_COMMIT), diff --git a/bazel/xls_c2rtl.BUILD.bazel b/bazel/xls_c2rtl.BUILD.bazel new file mode 100644 index 00000000..7ffbd2af --- /dev/null +++ b/bazel/xls_c2rtl.BUILD.bazel @@ -0,0 +1,267 @@ +load("@protobuf//bazel:cc_proto_library.bzl", "cc_proto_library") +load("@protobuf//bazel:proto_library.bzl", "proto_library") +load("@rules_cc//cc:cc_library.bzl", "cc_library") + +package(default_visibility = ["//visibility:public"]) + +proto_library( + name = "xls_c2rtl_proto", + srcs = [ + "xls/codegen/codegen_residual_data.proto", + "xls/codegen/module_signature.proto", + "xls/codegen/verilog_line_map.proto", + "xls/codegen/xls_metrics.proto", + "xls/contrib/xlscc/hls_block.proto", + "xls/contrib/xlscc/metadata_output.proto", + "xls/ir/channel.proto", + "xls/ir/evaluator_result.proto", + "xls/ir/foreign_function_data.proto", + "xls/ir/op.proto", + "xls/ir/ram_rewrite.proto", + "xls/ir/transform_metrics.proto", + "xls/ir/xls_ir_interface.proto", + "xls/ir/xls_type.proto", + "xls/ir/xls_value.proto", + "xls/jit/type_layout.proto", + "xls/netlist/netlist.proto", + "xls/passes/optimization_pass_pipeline.proto", + "xls/passes/pass_metrics.proto", + "xls/passes/pass_pipeline.proto", + "xls/scheduling/pipeline_schedule.proto", + ], + deps = [ + "@googleapis//google/api:field_behavior_proto", + "@protobuf//:duration_proto", + ], +) + +cc_proto_library( + name = "xls_c2rtl_cc_proto", + deps = [":xls_c2rtl_proto"], +) + +cc_library( + name = "xls_c2rtl", + srcs = [ + "xls/codegen/bdd_io_analysis.cc", + "xls/codegen/block_conversion.cc", + "xls/codegen/block_metrics.cc", + "xls/codegen/block_stitching_pass.cc", + "xls/codegen/clone_nodes_into_block_handler.cc", + "xls/codegen/codegen_checker.cc", + "xls/codegen/codegen_options.cc", + "xls/codegen/codegen_pass.cc", + "xls/codegen/codegen_pass_pipeline.cc", + "xls/codegen/codegen_util.cc", + "xls/codegen/codegen_wrapper_pass.cc", + "xls/codegen/combinational_generator.cc", + "xls/codegen/concurrent_stage_groups.cc", + "xls/codegen/conversion_utils.cc", + "xls/codegen/expression_flattening.cc", + "xls/codegen/ffi_instantiation_pass.cc", + "xls/codegen/finite_state_machine.cc", + "xls/codegen/lint_annotate.cc", + "xls/codegen/mark_channel_fifos_pass.cc", + "xls/codegen/maybe_materialize_fifos_pass.cc", + "xls/codegen/module_builder.cc", + "xls/codegen/module_signature.cc", + "xls/codegen/mulp_combining_pass.cc", + "xls/codegen/name_legalization_pass.cc", + "xls/codegen/node_expressions.cc", + "xls/codegen/op_override_impls.cc", + "xls/codegen/pipeline_generator.cc", + "xls/codegen/port_legalization_pass.cc", + "xls/codegen/priority_select_reduction_pass.cc", + "xls/codegen/proc_block_conversion.cc", + "xls/codegen/ram_configuration.cc", + "xls/codegen/ram_rewrite_pass.cc", + "xls/codegen/register_chaining_analysis.cc", + "xls/codegen/register_combining_pass.cc", + "xls/codegen/register_legalization_pass.cc", + "xls/codegen/side_effect_condition_pass.cc", + "xls/codegen/signature_generation_pass.cc", + "xls/codegen/signature_generator.cc", + "xls/codegen/trace_verbosity_pass.cc", + "xls/codegen/vast/fold_vast_constants.cc", + "xls/codegen/vast/infer_vast_types.cc", + "xls/codegen/vast/vast.cc", + "xls/codegen/vast/verilog_keywords.cc", + "xls/codegen/verilog_conversion.cc", + "xls/common/attribute_data.cc", + "xls/common/case_converters.cc", + "xls/common/file/filesystem.cc", + "xls/common/file/path.cc", + "xls/common/indent.cc", + "xls/common/logging/log_lines.cc", + "xls/common/math_util.cc", + "xls/common/status/error_code_to_status.cc", + "xls/common/status/ret_check.cc", + "xls/common/status/status_builder.cc", + "xls/common/status/status_builder_oss.cc", + "xls/common/stopwatch.cc", + "xls/common/strerror.cc", + "xls/common/string_to_int.cc", + "xls/common/symbolized_stacktrace.cc", + "xls/common/to_string_utils.cc", + "xls/contrib/xlscc/cc_parser.cc", + "xls/contrib/xlscc/continuations.cc", + "xls/contrib/xlscc/expr_clone.cc", + "xls/contrib/xlscc/generate_fsm.cc", + "xls/contrib/xlscc/node_manipulation.cc", + "xls/contrib/xlscc/tracked_bvalue.cc", + "xls/contrib/xlscc/translate_block.cc", + "xls/contrib/xlscc/translate_io.cc", + "xls/contrib/xlscc/translate_loops.cc", + "xls/contrib/xlscc/translate_metadata.cc", + "xls/contrib/xlscc/translator.cc", + "xls/contrib/xlscc/translator_types.cc", + "xls/data_structures/binary_decision_diagram.cc", + "xls/data_structures/binary_search.cc", + "xls/data_structures/inline_bitmap.cc", + "xls/data_structures/leaf_type_tree.cc", + "xls/data_structures/transitive_closure.cc", + "xls/dev_tools/link_to_source.cc", + "xls/estimators/delay_model/delay_estimator.cc", + "xls/estimators/delay_model/delay_estimators.cc", + "xls/estimators/delay_model/ffi_delay_estimator.cc", + "xls/interpreter/function_interpreter.cc", + "xls/interpreter/ir_interpreter.cc", + "xls/ir/big_int.cc", + "xls/ir/bit_push_buffer.cc", + "xls/ir/bits.cc", + "xls/ir/bits_ops.cc", + "xls/ir/block.cc", + "xls/ir/block_elaboration.cc", + "xls/ir/call_graph.cc", + "xls/ir/caret.cc", + "xls/ir/channel.cc", + "xls/ir/channel_ops.cc", + "xls/ir/clone_package.cc", + "xls/ir/code_template.cc", + "xls/ir/dfs_visitor.cc", + "xls/ir/elaborated_block_dfs_visitor.cc", + "xls/ir/elaboration.cc", + "xls/ir/events.cc", + "xls/ir/foreign_function.cc", + "xls/ir/format_preference.cc", + "xls/ir/format_strings.cc", + "xls/ir/function.cc", + "xls/ir/function_base.cc", + "xls/ir/function_builder.cc", + "xls/ir/instantiation.cc", + "xls/ir/interval.cc", + "xls/ir/interval_ops.cc", + "xls/ir/interval_set.cc", + "xls/ir/ir_annotator.cc", + "xls/ir/ir_parser.cc", + "xls/ir/ir_scanner.cc", + "xls/ir/keyword_args.cc", + "xls/ir/name_uniquer.cc", + "xls/ir/node.cc", + "xls/ir/node_util.cc", + "xls/ir/nodes.cc", + "xls/ir/number_parser.cc", + "xls/ir/op.cc", + "xls/ir/package.cc", + "xls/ir/partial_information.cc", + "xls/ir/partial_ops.cc", + "xls/ir/proc.cc", + "xls/ir/proc_conversion.cc", + "xls/ir/proc_elaboration.cc", + "xls/ir/proc_instantiation.cc", + "xls/ir/register.cc", + "xls/ir/scheduled_builder.cc", + "xls/ir/state_element.cc", + "xls/ir/ternary.cc", + "xls/ir/topo_sort.cc", + "xls/ir/type.cc", + "xls/ir/type_manager.cc", + "xls/ir/value.cc", + "xls/ir/value_builder.cc", + "xls/ir/value_conversion_utils.cc", + "xls/ir/value_flattening.cc", + "xls/ir/value_utils.cc", + "xls/ir/verifier.cc", + "xls/ir/verify_node.cc", + "xls/passes/array_simplification_pass.cc", + "xls/passes/basic_simplification_pass.cc", + "xls/passes/bdd_query_engine.cc", + "xls/passes/constant_folding_pass.cc", + "xls/passes/cse_pass.cc", + "xls/passes/dataflow_simplification_pass.cc", + "xls/passes/dce_pass.cc", + "xls/passes/identity_removal_pass.cc", + "xls/passes/inlining_pass.cc", + "xls/passes/lazy_ternary_query_engine.cc", + "xls/passes/node_dependency_analysis.cc", + "xls/passes/optimization_pass.cc", + "xls/passes/optimization_pass_registry.cc", + "xls/passes/partial_info_query_engine.cc", + "xls/passes/pass_base.cc", + "xls/passes/query_engine.cc", + "xls/passes/stateless_query_engine.cc", + "xls/passes/union_query_engine.cc", + "xls/solvers/solver.cc", + "xls/solvers/z3_ir_translator.cc", + "xls/solvers/z3_op_translator.cc", + "xls/solvers/z3_utils.cc", + ], + hdrs = glob([ + "xls/**/*.h", + "xls/**/*.inc", + "xls/**/*.td", + "xls/**/*.textproto", + "xls/**/*.tpl", + ], allow_empty = True), + copts = [ + "-Wno-deprecated-declarations", + "-Wno-deprecated-ofast", + "-Wno-inconsistent-missing-override", + "-Wno-nullability-completeness", + "-Wno-unused-result", + "-Wno-unused-value", + ], + includes = ["."], + deps = [ + ":xls_c2rtl_cc_proto", + "@abseil-cpp//absl/algorithm:container", + "@abseil-cpp//absl/base", + "@abseil-cpp//absl/base:core_headers", + "@abseil-cpp//absl/base:nullability", + "@abseil-cpp//absl/cleanup", + "@abseil-cpp//absl/container:btree", + "@abseil-cpp//absl/container:fixed_array", + "@abseil-cpp//absl/container:flat_hash_map", + "@abseil-cpp//absl/container:flat_hash_set", + "@abseil-cpp//absl/container:inlined_vector", + "@abseil-cpp//absl/functional:function_ref", + "@abseil-cpp//absl/hash", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:check", + "@abseil-cpp//absl/log:vlog_is_on", + "@abseil-cpp//absl/status", + "@abseil-cpp//absl/status:statusor", + "@abseil-cpp//absl/strings", + "@abseil-cpp//absl/strings:str_format", + "@abseil-cpp//absl/synchronization", + "@abseil-cpp//absl/time", + "@abseil-cpp//absl/types:compare", + "@abseil-cpp//absl/types:span", + "@boringssl//:crypto", + "@cppitertools", + "@llvm-project//clang:ast", + "@llvm-project//clang:basic", + "@llvm-project//clang:frontend", + "@llvm-project//clang:lex", + "@llvm-project//clang:sema", + "@llvm-project//clang:tooling", + "@llvm-project//llvm:Support", + "@protobuf", + "@protobuf//:duration_cc_proto", + "@protobuf//:protobuf_lite", + "@protobuf//src/google/protobuf/io", + "@protobuf//src/google/protobuf/io:tokenizer", + "@re2", + "@z3//:api", + ], +) diff --git a/bazel/xls_c2rtl_repo.bzl b/bazel/xls_c2rtl_repo.bzl new file mode 100644 index 00000000..8f90793e --- /dev/null +++ b/bazel/xls_c2rtl_repo.bzl @@ -0,0 +1,55 @@ +# Copyright 2026 keplertech.io +# SPDX-License-Identifier: GPL-3.0-only + +"""Repository rule that overlays a flat BUILD file on the local XLS tree.""" + +def _xls_c2rtl_repository_impl(repo_ctx): + src_root = repo_ctx.path(repo_ctx.attr.src_marker).dirname + if repo_ctx.attr.src_subdir: + src_root = src_root.get_child(repo_ctx.attr.src_subdir) + dst_root = repo_ctx.path(".") + copy_result = repo_ctx.execute([ + "/bin/bash", + "-c", + "cd \"$1\" && tar cf - . | (cd \"$2\" && tar xf -)", + "copy_xls", + str(src_root), + str(dst_root), + ]) + if copy_result.return_code != 0: + fail("failed to copy XLS source tree: " + copy_result.stderr) + + clean_result = repo_ctx.execute([ + "find", + ".", + "(", + "-name", + "BUILD", + "-o", + "-name", + "BUILD.bazel", + "-o", + "-name", + "MODULE.bazel", + "-o", + "-name", + "WORKSPACE", + "-o", + "-name", + "WORKSPACE.bazel", + ")", + "-delete", + ]) + if clean_result.return_code != 0: + fail("failed to clean XLS package markers: " + clean_result.stderr) + + repo_ctx.file("BUILD.bazel", repo_ctx.read(repo_ctx.attr.build_file)) + +xls_c2rtl_repository = repository_rule( + implementation = _xls_c2rtl_repository_impl, + attrs = { + "build_file": attr.label(mandatory = True, allow_single_file = True), + "src_marker": attr.label(mandatory = True, allow_single_file = True), + "src_subdir": attr.string(default = ""), + }, +) diff --git a/bazel/xls_llvm.patch b/bazel/xls_llvm.patch new file mode 100644 index 00000000..60a39d06 --- /dev/null +++ b/bazel/xls_llvm.patch @@ -0,0 +1,6233 @@ +Auto generated patch. Do not edit or delete it, even if empty. +diff -ruN --strip-trailing-cr a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html +--- a/clang/docs/LibASTMatchersReference.html ++++ b/clang/docs/LibASTMatchersReference.html +@@ -4301,11 +4301,6 @@ + + + +-Matcher<Decl>declaresSameEntityAsNodeconst Decl * Other +-
Matches a declaration if it declares the same entity as the node.
+-
+- +- + Matcher<Decl>equalsBoundNodestd::string ID +
Matches if a node equals a previously bound node.
+ 
+diff -ruN --strip-trailing-cr a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
+--- a/clang/docs/ReleaseNotes.md
++++ b/clang/docs/ReleaseNotes.md
+@@ -810,7 +810,6 @@
+ - Fixed an alias template CTAD crash.
+ - Correctly diagnose uses of `co_await` / `co_yield` in the default argument of nested function declarations. (#GH98923)
+ - Fixed a crash when diagnosing an invalid static member function with an explicit object parameter (#GH177741)
+-- Fixed clang incorrectly rejecting several cases of out-of-line definitions. (#GH101330)
+ - Clang incorrectly instantiated variable specializations outside of the immediate context. (#GH54439)
+ - Fixed a crash when pack expansions are used as arguments for non-pack parameters of built-in templates. (#GH180307)
+ - Fixed crash instantiating class member specializations.
+diff -ruN --strip-trailing-cr a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
+--- a/clang/include/clang/AST/Decl.h
++++ b/clang/include/clang/AST/Decl.h
+@@ -2131,6 +2131,34 @@
+   /// the DeclaratorDecl base class.
+   DeclarationNameLoc DNLoc;
+ 
++  /// Specify that this function declaration is actually a function
++  /// template specialization.
++  ///
++  /// \param C the ASTContext.
++  ///
++  /// \param Template the function template that this function template
++  /// specialization specializes.
++  ///
++  /// \param TemplateArgs the template arguments that produced this
++  /// function template specialization from the template.
++  ///
++  /// \param InsertPos If non-NULL, the position in the function template
++  /// specialization set where the function template specialization data will
++  /// be inserted.
++  ///
++  /// \param TSK the kind of template specialization this is.
++  ///
++  /// \param TemplateArgsAsWritten location info of template arguments.
++  ///
++  /// \param PointOfInstantiation point at which the function template
++  /// specialization was first instantiated.
++  void setFunctionTemplateSpecialization(
++      ASTContext &C, FunctionTemplateDecl *Template,
++      TemplateArgumentList *TemplateArgs, void *InsertPos,
++      TemplateSpecializationKind TSK,
++      const TemplateArgumentListInfo *TemplateArgsAsWritten,
++      SourceLocation PointOfInstantiation);
++
+   /// Specify that this record is an instantiation of the
+   /// member function FD.
+   void setInstantiationOfMemberFunction(ASTContext &C, FunctionDecl *FD,
+@@ -2226,8 +2254,6 @@
+     return SourceLocation();
+   }
+ 
+-  SourceLocation getFunctionLocStart() const;
+-
+   SourceRange getSourceRange() const override LLVM_READONLY;
+ 
+   // Function definitions.
+@@ -3057,14 +3083,9 @@
+   const ASTTemplateArgumentListInfo*
+   getTemplateSpecializationArgsAsWritten() const;
+ 
+-  /// Returns the template parameter list for an explicit specialization.
+-  const TemplateParameterList *getTemplateSpecializationParameters() const;
+-
+   /// Specify that this function declaration is actually a function
+   /// template specialization.
+   ///
+-  /// \param C the ASTContext.
+-  ///
+   /// \param Template the function template that this function template
+   /// specialization specializes.
+   ///
+@@ -3077,30 +3098,25 @@
+   ///
+   /// \param TSK the kind of template specialization this is.
+   ///
+-  /// \param TemplateParams the template parameters if this is an explicit
+-  /// specialization.
+-  ///
+   /// \param TemplateArgsAsWritten location info of template arguments.
+   ///
+   /// \param PointOfInstantiation point at which the function template
+   /// specialization was first instantiated.
+-  ///
+-  /// \param AddSpecialization whether to add this specialization to the
+-  /// template's specialization set.
+-  ///
+   void setFunctionTemplateSpecialization(
+-      ASTContext &C, FunctionTemplateDecl *Template,
+-      TemplateArgumentList *TemplateArgs, void *InsertPos,
+-      TemplateSpecializationKind TSK,
+-      const TemplateParameterList *TemplateParams,
+-      const TemplateArgumentListInfo *TemplateArgsAsWritten,
+-      SourceLocation PointOfInstantiation, bool AddSpecialization);
++      FunctionTemplateDecl *Template, TemplateArgumentList *TemplateArgs,
++      void *InsertPos,
++      TemplateSpecializationKind TSK = TSK_ImplicitInstantiation,
++      TemplateArgumentListInfo *TemplateArgsAsWritten = nullptr,
++      SourceLocation PointOfInstantiation = SourceLocation()) {
++    setFunctionTemplateSpecialization(getASTContext(), Template, TemplateArgs,
++                                      InsertPos, TSK, TemplateArgsAsWritten,
++                                      PointOfInstantiation);
++  }
+ 
+   /// Specifies that this function declaration is actually a
+   /// dependent function template specialization.
+   void setDependentTemplateSpecialization(
+       ASTContext &Context, const UnresolvedSetImpl &Templates,
+-      const TemplateParameterList *TemplateParams,
+       const TemplateArgumentListInfo *TemplateArgs);
+ 
+   DependentFunctionTemplateSpecializationInfo *
+diff -ruN --strip-trailing-cr a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
+--- a/clang/include/clang/AST/DeclTemplate.h
++++ b/clang/include/clang/AST/DeclTemplate.h
+@@ -73,7 +73,7 @@
+     : private llvm::TrailingObjects {
+   /// The template argument list of the template parameter list.
+-  mutable TemplateArgument *InjectedArgs = nullptr;
++  TemplateArgument *InjectedArgs = nullptr;
+ 
+   /// The location of the 'template' keyword.
+   SourceLocation TemplateLoc;
+@@ -200,8 +200,7 @@
+   bool hasAssociatedConstraints() const;
+ 
+   /// Get the template argument list of the template parameter list.
+-  ArrayRef
+-  getInjectedTemplateArgs(const ASTContext &Context) const;
++  ArrayRef getInjectedTemplateArgs(const ASTContext &Context);
+ 
+   SourceLocation getTemplateLoc() const { return TemplateLoc; }
+   SourceLocation getLAngleLoc() const { return LAngleLoc; }
+@@ -476,18 +475,14 @@
+   /// The function template from which this function template
+   /// specialization was generated.
+   ///
+-  /// The three bits contain the TemplateSpecializationKind.
+-  llvm::PointerIntPair Template;
++  /// The two bits contain the top 4 values of TemplateSpecializationKind.
++  llvm::PointerIntPair Template;
+ 
+ public:
+   /// The template arguments used to produce the function template
+   /// specialization from the function template.
+   TemplateArgumentList *TemplateArguments;
+ 
+-  // The template parameters if this is an explicit specialization.
+-  /// FIXME: Normally null; tail-allocate this.
+-  const TemplateParameterList *TemplateParameters;
+-
+   /// The template arguments as written in the sources, if provided.
+   /// FIXME: Normally null; tail-allocate this.
+   const ASTTemplateArgumentListInfo *TemplateArgumentsAsWritten;
+@@ -500,14 +495,12 @@
+   FunctionTemplateSpecializationInfo(
+       FunctionDecl *FD, FunctionTemplateDecl *Template,
+       TemplateSpecializationKind TSK, TemplateArgumentList *TemplateArgs,
+-      const TemplateParameterList *TemplateParameters,
+       const ASTTemplateArgumentListInfo *TemplateArgsAsWritten,
+       SourceLocation POI, MemberSpecializationInfo *MSInfo)
+       : Function(FD, MSInfo ? true : false), Template(Template, TSK - 1),
+-        TemplateArguments(TemplateArgs), TemplateParameters(TemplateParameters),
++        TemplateArguments(TemplateArgs),
+         TemplateArgumentsAsWritten(TemplateArgsAsWritten),
+         PointOfInstantiation(POI) {
+-    assert(TemplateParameters == nullptr || TSK == TSK_ExplicitSpecialization);
+     if (MSInfo)
+       getTrailingObjects()[0] = MSInfo;
+   }
+@@ -520,7 +513,6 @@
+   static FunctionTemplateSpecializationInfo *
+   Create(ASTContext &C, FunctionDecl *FD, FunctionTemplateDecl *Template,
+          TemplateSpecializationKind TSK, TemplateArgumentList *TemplateArgs,
+-         const TemplateParameterList *TemplateParameters,
+          const TemplateArgumentListInfo *TemplateArgsAsWritten,
+          SourceLocation POI, MemberSpecializationInfo *MSInfo);
+ 
+@@ -621,8 +613,8 @@
+ /// member class or member enumeration.
+ class MemberSpecializationInfo {
+   // The member declaration from which this member was instantiated, and the
+-  // manner in which the instantiation occurred (in the lower three bits).
+-  llvm::PointerIntPair MemberAndTSK;
++  // manner in which the instantiation occurred (in the lower two bits).
++  llvm::PointerIntPair MemberAndTSK;
+ 
+   // The point at which this member was first instantiated.
+   SourceLocation PointOfInstantiation;
+@@ -701,19 +693,14 @@
+ 
+   DependentFunctionTemplateSpecializationInfo(
+       const UnresolvedSetImpl &Candidates,
+-      const TemplateParameterList *TemplateParams,
+       const ASTTemplateArgumentListInfo *TemplateArgsWritten);
+ 
+ public:
+-  // The template parameters if this is an explicit specialization.
+-  const TemplateParameterList *TemplateParameters;
+-
+   /// The template arguments as written in the sources, if provided.
+   const ASTTemplateArgumentListInfo *TemplateArgumentsAsWritten;
+ 
+   static DependentFunctionTemplateSpecializationInfo *
+   Create(ASTContext &Context, const UnresolvedSetImpl &Candidates,
+-         const TemplateParameterList *TemplateParams,
+          const TemplateArgumentListInfo *TemplateArgs);
+ 
+   /// Returns the candidates for the primary function template.
+@@ -1018,6 +1005,11 @@
+     return getTemplatedDecl()->isThisDeclarationADefinition();
+   }
+ 
++  bool isCompatibleWithDefinition() const {
++    return getTemplatedDecl()->isInstantiatedFromMemberTemplate() ||
++           isThisDeclarationADefinition();
++  }
++
+   // This bit closely tracks 'RedeclarableTemplateDecl::InstantiatedFromMember',
+   // except this is per declaration, while the redeclarable field is
+   // per chain. This indicates a template redeclaration which
+@@ -1815,18 +1807,8 @@
+   ExplicitInstantiationInfo() = default;
+ };
+ 
+-struct ExplicitSpecializationInfo {
+-  /// The list of template parameters
+-  TemplateParameterList *TemplateParams = nullptr;
+-
+-  /// The template arguments as written.
+-  const ASTTemplateArgumentListInfo *TemplateArgsAsWritten = nullptr;
+-
+-  ExplicitSpecializationInfo() = default;
+-};
+-
+ using SpecializationOrInstantiationInfo =
+-    llvm::PointerUnion;
+ 
+ /// Represents a class template specialization, which refers to
+@@ -2056,38 +2038,49 @@
+   /// Retrieve the template argument list as written in the sources,
+   /// if any.
+   const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
+-    if (const auto *Info = getExplicitSpecializationInfo())
+-      return Info->TemplateArgsAsWritten;
+-    if (const auto *Info = getExplicitInstantiationInfo())
++    if (auto *Info =
++            dyn_cast_if_present(ExplicitInfo))
+       return Info->TemplateArgsAsWritten;
+-    return nullptr;
++    return cast(ExplicitInfo);
++  }
++
++  /// Set the template argument list as written in the sources.
++  void
++  setTemplateArgsAsWritten(const ASTTemplateArgumentListInfo *ArgsWritten) {
++    if (auto *Info =
++            dyn_cast_if_present(ExplicitInfo))
++      Info->TemplateArgsAsWritten = ArgsWritten;
++    else
++      ExplicitInfo = ArgsWritten;
++  }
++
++  /// Set the template argument list as written in the sources.
++  void setTemplateArgsAsWritten(const TemplateArgumentListInfo &ArgsInfo) {
++    setTemplateArgsAsWritten(
++        ASTTemplateArgumentListInfo::Create(getASTContext(), ArgsInfo));
++  }
++
++  /// Gets the location of the extern keyword, if present.
++  SourceLocation getExternKeywordLoc() const {
++    if (auto *Info =
++            dyn_cast_if_present(ExplicitInfo))
++      return Info->ExternKeywordLoc;
++    return SourceLocation();
++  }
++
++  /// Sets the location of the extern keyword.
++  void setExternKeywordLoc(SourceLocation Loc);
++
++  /// Gets the location of the template keyword, if present.
++  SourceLocation getTemplateKeywordLoc() const {
++    if (auto *Info =
++            dyn_cast_if_present(ExplicitInfo))
++      return Info->TemplateKeywordLoc;
++    return SourceLocation();
+   }
+ 
+-  /// Gets the explicit instantiation info, if present.
+-  const ExplicitInstantiationInfo *getExplicitInstantiationInfo() const {
+-    return dyn_cast_if_present(ExplicitInfo);
+-  }
+-
+-  /// Sets the explicit instantiation info.
+-  void setExplicitInstantiationInfo(
+-      SourceLocation ExternKeywordLoc, SourceLocation TemplateKeywordLoc,
+-      const ASTTemplateArgumentListInfo *TemplateArgsAsWritten) {
+-    auto *Info = new (getASTContext()) ExplicitInstantiationInfo();
+-    Info->ExternKeywordLoc = ExternKeywordLoc;
+-    Info->TemplateKeywordLoc = TemplateKeywordLoc;
+-    Info->TemplateArgsAsWritten = TemplateArgsAsWritten;
+-    ExplicitInfo = Info;
+-  }
+-
+-  /// Gets the explicit specialization info, if present.
+-  const ExplicitSpecializationInfo *getExplicitSpecializationInfo() const {
+-    return dyn_cast_if_present(ExplicitInfo);
+-  }
+-
+-  /// Sets the explicit specialization info.
+-  void setExplicitSpecializationInfo(
+-      TemplateParameterList *TemplateParams,
+-      const ASTTemplateArgumentListInfo *TemplateArgsAsWritten);
++  /// Sets the location of the template keyword.
++  void setTemplateKeywordLoc(SourceLocation Loc);
+ 
+   SourceRange getSourceRange() const override LLVM_READONLY;
+ 
+@@ -2112,7 +2105,10 @@
+ };
+ 
+ class ClassTemplatePartialSpecializationDecl
+-    : public ClassTemplateSpecializationDecl {
++  : public ClassTemplateSpecializationDecl {
++  /// The list of template parameters
++  TemplateParameterList *TemplateParams = nullptr;
++
+   /// The class template partial specialization from which this
+   /// class template partial specialization was instantiated.
+   ///
+@@ -2126,7 +2122,6 @@
+   ClassTemplatePartialSpecializationDecl(
+       ASTContext &Context, TagKind TK, DeclContext *DC, SourceLocation StartLoc,
+       SourceLocation IdLoc, TemplateParameterList *Params,
+-      const ASTTemplateArgumentListInfo *ArgsAsWritten,
+       ClassTemplateDecl *SpecializedTemplate, ArrayRef Args,
+       CanQualType CanonInjectedTST,
+       ClassTemplatePartialSpecializationDecl *PrevDecl);
+@@ -2144,9 +2139,7 @@
+   static ClassTemplatePartialSpecializationDecl *
+   Create(ASTContext &Context, TagKind TK, DeclContext *DC,
+          SourceLocation StartLoc, SourceLocation IdLoc,
+-         TemplateParameterList *Params,
+-         const ASTTemplateArgumentListInfo *TemplateArgsAsWritten,
+-         ClassTemplateDecl *SpecializedTemplate,
++         TemplateParameterList *Params, ClassTemplateDecl *SpecializedTemplate,
+          ArrayRef Args, CanQualType CanonInjectedTST,
+          ClassTemplatePartialSpecializationDecl *PrevDecl);
+ 
+@@ -2161,10 +2154,7 @@
+ 
+   /// Get the list of template parameters
+   TemplateParameterList *getTemplateParameters() const {
+-    auto *ExplicitSpecInfo = getExplicitSpecializationInfo();
+-    assert(ExplicitSpecInfo &&
+-           "A partial specialization is always an explicit specialization");
+-    return ExplicitSpecInfo->TemplateParams;
++    return TemplateParams;
+   }
+ 
+   /// \brief All associated constraints of this partial specialization,
+@@ -2175,11 +2165,11 @@
+   /// conjunction ("and").
+   void getAssociatedConstraints(
+       llvm::SmallVectorImpl &AC) const {
+-    getTemplateParameters()->getAssociatedConstraints(AC);
++    TemplateParams->getAssociatedConstraints(AC);
+   }
+ 
+   bool hasAssociatedConstraints() const {
+-    return getTemplateParameters()->hasAssociatedConstraints();
++    return TemplateParams->hasAssociatedConstraints();
+   }
+ 
+   /// Retrieve the member class template partial specialization from
+@@ -2816,38 +2806,49 @@
+   /// Retrieve the template argument list as written in the sources,
+   /// if any.
+   const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
+-    if (const auto *Info = getExplicitSpecializationInfo())
+-      return Info->TemplateArgsAsWritten;
+-    if (const auto *Info = getExplicitInstantiationInfo())
++    if (auto *Info =
++            dyn_cast_if_present(ExplicitInfo))
+       return Info->TemplateArgsAsWritten;
+-    return nullptr;
++    return cast(ExplicitInfo);
++  }
++
++  /// Set the template argument list as written in the sources.
++  void
++  setTemplateArgsAsWritten(const ASTTemplateArgumentListInfo *ArgsWritten) {
++    if (auto *Info =
++            dyn_cast_if_present(ExplicitInfo))
++      Info->TemplateArgsAsWritten = ArgsWritten;
++    else
++      ExplicitInfo = ArgsWritten;
+   }
+ 
+-  /// Gets the explicit instantiation info, if present.
+-  const ExplicitInstantiationInfo *getExplicitInstantiationInfo() const {
+-    return dyn_cast_if_present(ExplicitInfo);
+-  }
+-
+-  /// Sets the explicit instantiation info.
+-  void setExplicitInstantiationInfo(
+-      SourceLocation ExternKeywordLoc, SourceLocation TemplateKeywordLoc,
+-      const ASTTemplateArgumentListInfo *TemplateArgsAsWritten) {
+-    auto *Info = new (getASTContext()) ExplicitInstantiationInfo();
+-    Info->ExternKeywordLoc = ExternKeywordLoc;
+-    Info->TemplateKeywordLoc = TemplateKeywordLoc;
+-    Info->TemplateArgsAsWritten = TemplateArgsAsWritten;
+-    ExplicitInfo = Info;
+-  }
+-
+-  /// Gets the explicit specialization info, if present.
+-  const ExplicitSpecializationInfo *getExplicitSpecializationInfo() const {
+-    return dyn_cast_if_present(ExplicitInfo);
+-  }
+-
+-  /// Sets the explicit specialization info.
+-  void setExplicitSpecializationInfo(
+-      TemplateParameterList *TemplateParams,
+-      const ASTTemplateArgumentListInfo *TemplateArgsAsWritten);
++  /// Set the template argument list as written in the sources.
++  void setTemplateArgsAsWritten(const TemplateArgumentListInfo &ArgsInfo) {
++    setTemplateArgsAsWritten(
++        ASTTemplateArgumentListInfo::Create(getASTContext(), ArgsInfo));
++  }
++
++  /// Gets the location of the extern keyword, if present.
++  SourceLocation getExternKeywordLoc() const {
++    if (auto *Info =
++            dyn_cast_if_present(ExplicitInfo))
++      return Info->ExternKeywordLoc;
++    return SourceLocation();
++  }
++
++  /// Sets the location of the extern keyword.
++  void setExternKeywordLoc(SourceLocation Loc);
++
++  /// Gets the location of the template keyword, if present.
++  SourceLocation getTemplateKeywordLoc() const {
++    if (auto *Info =
++            dyn_cast_if_present(ExplicitInfo))
++      return Info->TemplateKeywordLoc;
++    return SourceLocation();
++  }
++
++  /// Sets the location of the template keyword.
++  void setTemplateKeywordLoc(SourceLocation Loc);
+ 
+   SourceRange getSourceRange() const override LLVM_READONLY;
+ 
+@@ -2873,6 +2874,9 @@
+ 
+ class VarTemplatePartialSpecializationDecl
+     : public VarTemplateSpecializationDecl {
++  /// The list of template parameters
++  TemplateParameterList *TemplateParams = nullptr;
++
+   /// The variable template partial specialization from which this
+   /// variable template partial specialization was instantiated.
+   ///
+@@ -2884,7 +2888,6 @@
+   VarTemplatePartialSpecializationDecl(
+       ASTContext &Context, DeclContext *DC, SourceLocation StartLoc,
+       SourceLocation IdLoc, TemplateParameterList *Params,
+-      const ASTTemplateArgumentListInfo *TemplateArgsAsWritten,
+       VarTemplateDecl *SpecializedTemplate, QualType T, TypeSourceInfo *TInfo,
+       StorageClass S, ArrayRef Args);
+ 
+@@ -2902,7 +2905,6 @@
+   static VarTemplatePartialSpecializationDecl *
+   Create(ASTContext &Context, DeclContext *DC, SourceLocation StartLoc,
+          SourceLocation IdLoc, TemplateParameterList *Params,
+-         const ASTTemplateArgumentListInfo *TemplateArgsAsWritten,
+          VarTemplateDecl *SpecializedTemplate, QualType T,
+          TypeSourceInfo *TInfo, StorageClass S,
+          ArrayRef Args);
+@@ -2918,10 +2920,7 @@
+ 
+   /// Get the list of template parameters
+   TemplateParameterList *getTemplateParameters() const {
+-    auto *ExplicitSpecInfo = getExplicitSpecializationInfo();
+-    assert(ExplicitSpecInfo &&
+-           "A partial specialization is always an explicit specialization");
+-    return ExplicitSpecInfo->TemplateParams;
++    return TemplateParams;
+   }
+ 
+   /// Get the template argument list of the template parameter list.
+@@ -2938,11 +2937,11 @@
+   /// conjunction ("and").
+   void getAssociatedConstraints(
+       llvm::SmallVectorImpl &AC) const {
+-    getTemplateParameters()->getAssociatedConstraints(AC);
++    TemplateParams->getAssociatedConstraints(AC);
+   }
+ 
+   bool hasAssociatedConstraints() const {
+-    return getTemplateParameters()->hasAssociatedConstraints();
++    return TemplateParams->hasAssociatedConstraints();
+   }
+ 
+   /// \brief Retrieve the member variable template partial specialization from
+diff -ruN --strip-trailing-cr a/clang/include/clang/AST/JSONNodeDumper.h b/clang/include/clang/AST/JSONNodeDumper.h
+--- a/clang/include/clang/AST/JSONNodeDumper.h
++++ b/clang/include/clang/AST/JSONNodeDumper.h
+@@ -417,7 +417,6 @@
+           Visit(Redecl);
+         DumpedAny = true;
+         break;
+-      case TSK_FriendDeclaration:
+       case TSK_ExplicitSpecialization:
+         break;
+       }
+diff -ruN --strip-trailing-cr a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
+--- a/clang/include/clang/AST/RecursiveASTVisitor.h
++++ b/clang/include/clang/AST/RecursiveASTVisitor.h
+@@ -2002,7 +2002,6 @@
+       case TSK_ExplicitInstantiationDeclaration:
+       case TSK_ExplicitInstantiationDefinition:
+       case TSK_ExplicitSpecialization:
+-      case TSK_FriendDeclaration:
+         break;
+       }
+     }
+@@ -2023,7 +2022,6 @@
+         TRY_TO(TraverseDecl(RD));
+         break;
+ 
+-      case TSK_FriendDeclaration:
+       case TSK_ExplicitInstantiationDeclaration:
+       case TSK_ExplicitInstantiationDefinition:
+       case TSK_ExplicitSpecialization:
+@@ -2058,7 +2056,6 @@
+         TRY_TO(TraverseDecl(RD));
+         break;
+ 
+-      case TSK_FriendDeclaration:
+       case TSK_ExplicitSpecialization:
+         break;
+       }
+@@ -2225,9 +2222,8 @@
+        handles traversal of template args and qualifier.                       \
+        For explicit specializations ("template<> set {...};"),            \
+        we traverse template args here since there is no EID. */                \
+-    if (const auto *Info = D->getExplicitSpecializationInfo()) {               \
+-      const auto *ArgsWritten = Info->TemplateArgsAsWritten;                   \
+-      TRY_TO(TraverseTemplateParameterListHelper(Info->TemplateParams));       \
++    if (D->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) {    \
++      const auto *ArgsWritten = D->getTemplateArgsAsWritten();                 \
+       TRY_TO(TraverseTemplateArgumentLocsHelper(                               \
+           ArgsWritten->getTemplateArgs(), ArgsWritten->NumTemplateArgs));      \
+     } else if (!getDerived().shouldVisitTemplateInstantiations()) {            \
+diff -ruN --strip-trailing-cr a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
+--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
++++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
+@@ -5753,11 +5753,6 @@
+   return (Else != nullptr && InnerMatcher.matches(*Else, Finder, Builder));
+ }
+ 
+-/// Matches a declaration if it declares the same entity as the node.
+-AST_MATCHER_P(Decl, declaresSameEntityAsNode, const Decl *, Other) {
+-  return clang::declaresSameEntity(&Node, Other);
+-}
+-
+ /// Matches if a node equals a previously bound node.
+ ///
+ /// Matches a node if it equals the node previously bound to \p ID.
+diff -ruN --strip-trailing-cr a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
++++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+@@ -1993,12 +1993,16 @@
+ 
+ inline unsigned
+ getNumTemplateArgsWritten(const ClassTemplateSpecializationDecl &D) {
+-  return getTemplateArgsWritten(D).size();
++  if (const ASTTemplateArgumentListInfo *Args = D.getTemplateArgsAsWritten())
++    return Args->getNumTemplateArgs();
++  return 0;
+ }
+ 
+ inline unsigned
+ getNumTemplateArgsWritten(const VarTemplateSpecializationDecl &D) {
+-  return getTemplateArgsWritten(D).size();
++  if (const ASTTemplateArgumentListInfo *Args = D.getTemplateArgsAsWritten())
++    return Args->getNumTemplateArgs();
++  return 0;
+ }
+ 
+ inline unsigned getNumTemplateArgsWritten(const FunctionDecl &FD) {
+diff -ruN --strip-trailing-cr a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h
+--- a/clang/include/clang/Basic/Specifiers.h
++++ b/clang/include/clang/Basic/Specifiers.h
+@@ -193,8 +193,6 @@
+     /// This template specialization was implicitly instantiated from a
+     /// template. (C++ [temp.inst]).
+     TSK_ImplicitInstantiation,
+-    /// This template is a friend declaration.
+-    TSK_FriendDeclaration,
+     /// This template specialization was declared or defined by an
+     /// explicit specialization (C++ [temp.expl.spec]) or partial
+     /// specialization (C++ [temp.class.spec]).
+@@ -229,7 +227,6 @@
+ 
+     case TSK_Undeclared:
+     case TSK_ImplicitInstantiation:
+-    case TSK_FriendDeclaration:
+       return false;
+     }
+     llvm_unreachable("bad template specialization kind");
+diff -ruN --strip-trailing-cr a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
+--- a/clang/include/clang/Sema/Sema.h
++++ b/clang/include/clang/Sema/Sema.h
+@@ -11748,7 +11748,7 @@
+   TemplateParameterList *MatchTemplateParametersToScopeSpecifier(
+       SourceLocation DeclStartLoc, SourceLocation DeclLoc,
+       const CXXScopeSpec &SS, TemplateIdAnnotation *TemplateId,
+-      ArrayRef &ParamLists, bool IsFriend,
++      ArrayRef ParamLists, bool IsFriend,
+       bool &IsMemberSpecialization, bool &Invalid,
+       bool SuppressDiagnostic = false);
+ 
+@@ -11814,7 +11814,8 @@
+   DeclResult CheckVarTemplateId(VarTemplateDecl *Template,
+                                 SourceLocation TemplateLoc,
+                                 SourceLocation TemplateNameLoc,
+-                                const TemplateArgumentListInfo &TemplateArgs);
++                                const TemplateArgumentListInfo &TemplateArgs,
++                                bool SetWrittenArgs);
+ 
+   /// Form a reference to the specialization of the given variable template
+   /// corresponding to the specified argument list, or a null-but-valid result
+@@ -11898,7 +11899,7 @@
+       Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
+       SourceLocation ModulePrivateLoc, CXXScopeSpec &SS,
+       TemplateIdAnnotation &TemplateId, const ParsedAttributesView &Attr,
+-      MultiTemplateParamsArg &TemplateParameterLists,
++      MultiTemplateParamsArg TemplateParameterLists,
+       SkipBodyInfo *SkipBody = nullptr);
+ 
+   /// Check the non-type template arguments of a class template
+@@ -11971,8 +11972,7 @@
+   /// There really isn't any useful analysis we can do here, so we
+   /// just store the information.
+   bool CheckDependentFunctionTemplateSpecialization(
+-      FunctionDecl *FD, const TemplateParameterList *TemplateParams,
+-      const TemplateArgumentListInfo *ExplicitTemplateArgs,
++      FunctionDecl *FD, const TemplateArgumentListInfo *ExplicitTemplateArgs,
+       LookupResult &Previous);
+ 
+   /// Perform semantic analysis for the given function template
+@@ -11998,9 +11998,8 @@
+   /// declaration with no explicit template argument list that might be
+   /// befriending a function template specialization.
+   bool CheckFunctionTemplateSpecialization(
+-      FunctionDecl *FD, const TemplateParameterList *TemplateParams,
+-      TemplateArgumentListInfo *ExplicitTemplateArgs, LookupResult &Previous,
+-      bool QualifiedFriend = false);
++      FunctionDecl *FD, TemplateArgumentListInfo *ExplicitTemplateArgs,
++      LookupResult &Previous, bool QualifiedFriend = false);
+ 
+   /// Perform semantic analysis for the given non-template member
+   /// specialization.
+@@ -12266,6 +12265,46 @@
+     TPL_TemplateParamsEquivalent,
+   };
+ 
++  // A struct to represent the 'new' declaration, which is either itself just
++  // the named decl, or the important information we need about it in order to
++  // do constraint comparisons.
++  class TemplateCompareNewDeclInfo {
++    const NamedDecl *ND = nullptr;
++    const DeclContext *DC = nullptr;
++    const DeclContext *LexicalDC = nullptr;
++    SourceLocation Loc;
++
++  public:
++    TemplateCompareNewDeclInfo(const NamedDecl *ND) : ND(ND) {}
++    TemplateCompareNewDeclInfo(const DeclContext *DeclCtx,
++                               const DeclContext *LexicalDeclCtx,
++                               SourceLocation Loc)
++
++        : DC(DeclCtx), LexicalDC(LexicalDeclCtx), Loc(Loc) {
++      assert(DC && LexicalDC &&
++             "Constructor only for cases where we have the information to put "
++             "in here");
++    }
++
++    // If this was constructed with no information, we cannot do substitution
++    // for constraint comparison, so make sure we can check that.
++    bool isInvalid() const { return !ND && !DC; }
++
++    const NamedDecl *getDecl() const { return ND; }
++
++    bool ContainsDecl(const NamedDecl *ND) const { return this->ND == ND; }
++
++    const DeclContext *getLexicalDeclContext() const {
++      return ND ? ND->getLexicalDeclContext() : LexicalDC;
++    }
++
++    const DeclContext *getDeclContext() const {
++      return ND ? ND->getDeclContext() : DC;
++    }
++
++    SourceLocation getLocation() const { return ND ? ND->getLocation() : Loc; }
++  };
++
+   /// Determine whether the given template parameter lists are
+   /// equivalent.
+   ///
+@@ -12290,11 +12329,19 @@
+   /// \returns True if the template parameter lists are equal, false
+   /// otherwise.
+   bool TemplateParameterListsAreEqual(
+-      const Decl *NewInstFrom, TemplateParameterList *New,
+-      const Decl *OldInstFrom, TemplateParameterList *Old, bool Complain,
++      const TemplateCompareNewDeclInfo &NewInstFrom, TemplateParameterList *New,
++      const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain,
+       TemplateParameterListEqualKind Kind,
+       SourceLocation TemplateArgLoc = SourceLocation());
+ 
++  bool TemplateParameterListsAreEqual(
++      TemplateParameterList *New, TemplateParameterList *Old, bool Complain,
++      TemplateParameterListEqualKind Kind,
++      SourceLocation TemplateArgLoc = SourceLocation()) {
++    return TemplateParameterListsAreEqual(nullptr, New, nullptr, Old, Complain,
++                                          Kind, TemplateArgLoc);
++  }
++
+   /// Check whether a template can be declared within this scope.
+   ///
+   /// If the template declaration is valid in this scope, returns
+@@ -13525,27 +13572,44 @@
+   /// Retrieve the template argument list(s) that should be used to
+   /// instantiate the definition of the given declaration.
+   ///
+-  /// \param D the declaration for which we are computing template
++  /// \param ND the declaration for which we are computing template
+   /// instantiation arguments.
+   ///
+-  /// \param Innermost if present, specifies a template argument list for the
+-  /// template-like (TemplateDecl or PartialSpec) declaration passed as D.
+-  ///
+-  /// \param NumLevels if present, specifies the maximum number of template
+-  /// levels of the result. This is useful for instantiating a pattern that has
+-  /// already had some levels instantiated. In that case, the Template Depth of
+-  /// the pattern can be passed here.
+-  ///
+-  /// \param SkipInnerNonInstantiated Skips adding template-like levels to the
+-  /// result until hitting the first non-template-like level. This is a
+-  /// workaround for dealing with the instantiation of the definition of generic
+-  /// lambdas, which currently are eagerly substituted.
+-  ///
++  /// \param DC In the event we don't HAVE a declaration yet, we instead provide
++  ///  the decl context where it will be created.  In this case, the `Innermost`
++  ///  should likely be provided.  If ND is non-null, this is ignored.
++  ///
++  /// \param Innermost if non-NULL, specifies a template argument list for the
++  /// template declaration passed as ND.
++  ///
++  /// \param RelativeToPrimary true if we should get the template
++  /// arguments relative to the primary template, even when we're
++  /// dealing with a specialization. This is only relevant for function
++  /// template specializations.
++  ///
++  /// \param Pattern If non-NULL, indicates the pattern from which we will be
++  /// instantiating the definition of the given declaration, \p ND. This is
++  /// used to determine the proper set of template instantiation arguments for
++  /// friend function template specializations.
++  ///
++  /// \param ForConstraintInstantiation when collecting arguments,
++  /// ForConstraintInstantiation indicates we should continue looking when
++  /// encountering a lambda generic call operator, and continue looking for
++  /// arguments on an enclosing class template.
++  ///
++  /// \param SkipForSpecialization when specified, any template specializations
++  /// in a traversal would be ignored.
++  ///
++  /// \param ForDefaultArgumentSubstitution indicates we should continue looking
++  /// when encountering a specialized member function template, rather than
++  /// returning immediately.
+   MultiLevelTemplateArgumentList getTemplateInstantiationArgs(
+-      const Decl *D,
++      const NamedDecl *D, const DeclContext *DC = nullptr, bool Final = false,
+       std::optional> Innermost = std::nullopt,
+-      UnsignedOrNone NumLevels = std::nullopt,
+-      bool SkipInnerNonInstantiated = false);
++      bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr,
++      bool ForConstraintInstantiation = false,
++      bool SkipForSpecialization = false,
++      bool ForDefaultArgumentSubstitution = false);
+ 
+   /// RAII object to handle the state changes required to synthesize
+   /// a function body.
+@@ -14962,13 +15026,14 @@
+   // for figuring out the relative 'depth' of the constraint. The depth of the
+   // 'primary template' and the 'instantiated from' templates aren't necessarily
+   // the same, such as a case when one is a 'friend' defined in a class.
+-  bool AreConstraintExpressionsEqual(const Decl *Old, const Expr *OldConstr,
+-                                     const Decl *New, const Expr *NewConstr);
++  bool AreConstraintExpressionsEqual(const NamedDecl *Old,
++                                     const Expr *OldConstr,
++                                     const TemplateCompareNewDeclInfo &New,
++                                     const Expr *NewConstr);
+ 
+   // Calculates whether the friend function depends on an enclosing template for
+   // the purposes of [temp.friend] p9.
+-  bool
+-  FriendConstraintsDependOnEnclosingTemplate(const FunctionTemplateDecl *FTD);
++  bool FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD);
+ 
+   /// \brief Ensure that the given template arguments satisfy the constraints
+   /// associated with the given template, emitting a diagnostic if they do not.
+@@ -14989,19 +15054,10 @@
+       SourceRange TemplateIDRange);
+ 
+   bool CheckFunctionTemplateConstraints(SourceLocation PointOfInstantiation,
+-                                        FunctionTemplateDecl *Template,
++                                        FunctionDecl *Decl,
+                                         ArrayRef TemplateArgs,
+                                         ConstraintSatisfaction &Satisfaction);
+ 
+-  // FIXME: Constraints should be always checked before the declaration is
+-  // specialized. This function exists to support a workaround for templated
+-  // lambdas, where handling the instantiation scope for the captures is not
+-  // implemented yet.
+-  bool
+-  CheckFunctionSpecializationConstraints(SourceLocation PointOfInstantiation,
+-                                         FunctionDecl *Decl,
+-                                         ConstraintSatisfaction &Satisfaction);
+-
+   /// \brief Emit diagnostics explaining why a constraint expression was deemed
+   /// unsatisfied.
+   /// \param First whether this is the first time an unsatisfied constraint is
+@@ -15082,16 +15138,19 @@
+   /// Used by SetupConstraintCheckingTemplateArgumentsAndScope to set up the
+   /// LocalInstantiationScope of the current non-lambda function. For lambdas,
+   /// use LambdaScopeForCallOperatorInstantiationRAII.
+-  bool SetupConstraintScope(FunctionDecl *FD,
+-                            const MultiLevelTemplateArgumentList &MLTAL,
+-                            LocalInstantiationScope &Scope);
++  bool
++  SetupConstraintScope(FunctionDecl *FD,
++                       std::optional> TemplateArgs,
++                       const MultiLevelTemplateArgumentList &MLTAL,
++                       LocalInstantiationScope &Scope);
+ 
+   /// Used during constraint checking, sets up the constraint template argument
+   /// lists, and calls SetupConstraintScope to set up the
+   /// LocalInstantiationScope to have the proper set of ParVarDecls configured.
+   std::optional
+   SetupConstraintCheckingTemplateArgumentsAndScope(
+-      FunctionDecl *FD, LocalInstantiationScope &Scope);
++      FunctionDecl *FD, std::optional> TemplateArgs,
++      LocalInstantiationScope &Scope);
+ 
+   ///@}
+ 
+diff -ruN --strip-trailing-cr a/clang/lib/Analysis/ExprMutationAnalyzer.cpp b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
+--- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp
++++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
+@@ -690,7 +690,7 @@
+       canResolveToExpr(Exp),
+       parmVarDecl(hasType(nonConstReferenceType())).bind("parm"));
+   const auto IsInstantiated = hasDeclaration(isInstantiated());
+-  const auto FuncDecl = hasDeclaration(functionDecl());
++  const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
+   const auto Matches = match(
+       traverse(
+           TK_AsIs,
+@@ -704,16 +704,13 @@
+       Stm, Context);
+   for (const auto &Nodes : Matches) {
+     const auto *Exp = Nodes.getNodeAs(NodeID::value);
+-    const auto *Parm = Nodes.getNodeAs("parm");
+-    const auto *Func =
+-        cast(Parm->getDeclContext())->getDefinition();
+-    if (!Func || !Func->doesThisDeclarationHaveABody())
++    const auto *Func = Nodes.getNodeAs("func");
++    if (!Func->getBody() || !Func->getPrimaryTemplate())
+       return Exp;
+-    Parm = Func->getParamDecl(Parm->getFunctionScopeIndex());
+ 
++    const auto *Parm = Nodes.getNodeAs("parm");
+     const ArrayRef AllParams =
+-        Func->getTemplateInstantiationPattern(/*ForDefinition=*/true)
+-            ->parameters();
++        Func->getPrimaryTemplate()->getTemplatedDecl()->parameters();
+     QualType ParmType =
+         AllParams[std::min(Parm->getFunctionScopeIndex(),
+                                    AllParams.size() - 1)]
+diff -ruN --strip-trailing-cr a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
+--- a/clang/lib/AST/ASTContext.cpp
++++ b/clang/lib/AST/ASTContext.cpp
+@@ -13084,7 +13084,6 @@
+   case TSK_ExplicitInstantiationDeclaration:
+     return GVA_AvailableExternally;
+ 
+-  case TSK_FriendDeclaration:
+   case TSK_ImplicitInstantiation:
+     External = GVA_DiscardableODR;
+     break;
+@@ -13274,7 +13273,6 @@
+   case TSK_ExplicitInstantiationDeclaration:
+     return GVA_AvailableExternally;
+ 
+-  case TSK_FriendDeclaration:
+   case TSK_ImplicitInstantiation:
+     return GVA_DiscardableODR;
+   }
+diff -ruN --strip-trailing-cr a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp
+--- a/clang/lib/AST/ASTDumper.cpp
++++ b/clang/lib/AST/ASTDumper.cpp
+@@ -140,7 +140,6 @@
+         Visit(Redecl);
+       DumpedAny = true;
+       break;
+-    case TSK_FriendDeclaration:
+     case TSK_ExplicitSpecialization:
+       break;
+     }
+diff -ruN --strip-trailing-cr a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
+--- a/clang/lib/AST/ASTImporter.cpp
++++ b/clang/lib/AST/ASTImporter.cpp
+@@ -816,8 +816,6 @@
+ template <>
+ Expected
+ ASTNodeImporter::import(TemplateParameterList *From) {
+-  if (!From)
+-    return nullptr;
+   SmallVector To(From->size());
+   if (Error Err = ImportContainerChecked(*From, To))
+     return std::move(Err);
+@@ -956,8 +954,7 @@
+       ToInfo = TemplateArgumentLocInfo(*TSIOrErr);
+     else
+       return TSIOrErr.takeError();
+-  } else if (Arg.getKind() == TemplateArgument::Template ||
+-             Arg.getKind() == TemplateArgument::TemplateExpansion) {
++  } else {
+     auto ToTemplateKWLocOrErr = import(FromInfo.getTemplateKwLoc());
+     if (!ToTemplateKWLocOrErr)
+       return ToTemplateKWLocOrErr.takeError();
+@@ -975,9 +972,6 @@
+         Importer.getToContext(), *ToTemplateKWLocOrErr,
+         *ToTemplateQualifierLocOrErr, *ToTemplateNameLocOrErr,
+         *ToTemplateEllipsisLocOrErr);
+-  } else {
+-    ToInfo = TemplateArgumentLocInfo(Importer.getToContext(),
+-                                     FromInfo.getTrivialLoc());
+   }
+ 
+   return TemplateArgumentLoc(Arg, ToInfo);
+@@ -3670,12 +3664,6 @@
+           Importer.getToContext(), std::get<1>(*FunctionAndArgsOrErr));
+ 
+     auto *FTSInfo = FromFD->getTemplateSpecializationInfo();
+-
+-    Expected TemplateParamsOrErr =
+-        import(FTSInfo->TemplateParameters);
+-    if (!TemplateParamsOrErr)
+-      return TemplateParamsOrErr.takeError();
+-
+     TemplateArgumentListInfo ToTAInfo;
+     const auto *FromTAArgsAsWritten = FTSInfo->TemplateArgumentsAsWritten;
+     if (FromTAArgsAsWritten)
+@@ -3692,10 +3680,8 @@
+ 
+     TemplateSpecializationKind TSK = FTSInfo->getTemplateSpecializationKind();
+     ToFD->setFunctionTemplateSpecialization(
+-        Importer.getToContext(), std::get<0>(*FunctionAndArgsOrErr), ToTAList,
+-        /*InsertPos=*/nullptr, TSK, *TemplateParamsOrErr,
+-        FromTAArgsAsWritten ? &ToTAInfo : nullptr, *POIOrErr,
+-        /*AddSpecialization=*/true);
++        std::get<0>(*FunctionAndArgsOrErr), ToTAList, /* InsertPos= */ nullptr,
++        TSK, FromTAArgsAsWritten ? &ToTAInfo : nullptr, *POIOrErr);
+     return Error::success();
+   }
+ 
+@@ -3709,11 +3695,6 @@
+         return ToFTDOrErr.takeError();
+     }
+ 
+-    Expected TemplateParamsOrErr =
+-        import(FromInfo->TemplateParameters);
+-    if (!TemplateParamsOrErr)
+-      return TemplateParamsOrErr.takeError();
+-
+     // Import TemplateArgumentListInfo.
+     TemplateArgumentListInfo ToTAInfo;
+     const auto *FromTAArgsAsWritten = FromInfo->TemplateArgumentsAsWritten;
+@@ -3723,7 +3704,7 @@
+         return Err;
+ 
+     ToFD->setDependentTemplateSpecialization(
+-        Importer.getToContext(), Candidates, *TemplateParamsOrErr,
++        Importer.getToContext(), Candidates,
+         FromTAArgsAsWritten ? &ToTAInfo : nullptr);
+     return Error::success();
+   }
+@@ -6544,22 +6525,19 @@
+   if (!IdLocOrErr)
+     return IdLocOrErr.takeError();
+ 
++  // Import TemplateArgumentListInfo.
++  TemplateArgumentListInfo ToTAInfo;
++  if (const auto *ASTTemplateArgs = D->getTemplateArgsAsWritten()) {
++    if (Error Err = ImportTemplateArgumentListInfo(*ASTTemplateArgs, ToTAInfo))
++      return std::move(Err);
++  }
++
+   // Create the specialization.
+   ClassTemplateSpecializationDecl *D2 = nullptr;
+   if (PartialSpec) {
+-    TemplateArgumentListInfo ToTAInfo;
+-    if (Error Err = ImportTemplateArgumentListInfo(
+-            *cast(D)
+-                 ->getTemplateArgsAsWritten(),
+-            ToTAInfo))
+-      return std::move(Err);
+-
+     if (GetImportedOrCreateDecl(
+             D2, D, Importer.getToContext(), D->getTagKind(), DC, *BeginLocOrErr,
+-            *IdLocOrErr, ToTPList,
+-            ASTTemplateArgumentListInfo::Create(Importer.getToContext(),
+-                                                ToTAInfo),
+-            ClassTemplate, ArrayRef(TemplateArgs),
++            *IdLocOrErr, ToTPList, ClassTemplate, ArrayRef(TemplateArgs),
+             /*CanonInjectedTST=*/CanQualType(),
+             cast_or_null(PrevDecl)))
+       return D2;
+@@ -6590,34 +6568,6 @@
+     if (!ClassTemplate->findSpecialization(TemplateArgs, InsertPos))
+       // Add this specialization to the class template.
+       ClassTemplate->AddSpecialization(D2, InsertPos);
+-
+-    if (const auto *Info = D->getExplicitInstantiationInfo()) {
+-      auto ExternKeywordLocOrErr = import(Info->ExternKeywordLoc);
+-      if (!ExternKeywordLocOrErr)
+-        return ExternKeywordLocOrErr.takeError();
+-      auto TemplateKeywordLocOrErr = import(Info->TemplateKeywordLoc);
+-      if (!TemplateKeywordLocOrErr)
+-        return TemplateKeywordLocOrErr.takeError();
+-      TemplateArgumentListInfo ToTAInfo;
+-      if (Error Err = ImportTemplateArgumentListInfo(
+-              *Info->TemplateArgsAsWritten, ToTAInfo))
+-        return std::move(Err);
+-      D2->setExplicitInstantiationInfo(*ExternKeywordLocOrErr,
+-                                       *TemplateKeywordLocOrErr,
+-                                       ASTTemplateArgumentListInfo::Create(
+-                                           Importer.getToContext(), ToTAInfo));
+-    } else if (const auto *Info = D->getExplicitSpecializationInfo()) {
+-      auto ParamsOrErr = import(Info->TemplateParams);
+-      if (!ParamsOrErr)
+-        return ParamsOrErr.takeError();
+-      TemplateArgumentListInfo ToTAInfo;
+-      if (Error Err = ImportTemplateArgumentListInfo(
+-              *Info->TemplateArgsAsWritten, ToTAInfo))
+-        return std::move(Err);
+-      D2->setExplicitSpecializationInfo(*ParamsOrErr,
+-                                        ASTTemplateArgumentListInfo::Create(
+-                                            Importer.getToContext(), ToTAInfo));
+-    }
+   }
+ 
+   D2->setSpecializationKind(D->getSpecializationKind());
+@@ -6644,6 +6594,19 @@
+   else
+     return LocOrErr.takeError();
+ 
++  if (D->getTemplateArgsAsWritten())
++    D2->setTemplateArgsAsWritten(ToTAInfo);
++
++  if (auto LocOrErr = import(D->getTemplateKeywordLoc()))
++    D2->setTemplateKeywordLoc(*LocOrErr);
++  else
++    return LocOrErr.takeError();
++
++  if (auto LocOrErr = import(D->getExternKeywordLoc()))
++    D2->setExternKeywordLoc(*LocOrErr);
++  else
++    return LocOrErr.takeError();
++
+   if (D->getPointOfInstantiation().isValid()) {
+     if (auto POIOrErr = import(D->getPointOfInstantiation()))
+       D2->setPointOfInstantiation(*POIOrErr);
+@@ -6861,6 +6824,12 @@
+ 
+   VarTemplateSpecializationDecl *D2 = nullptr;
+ 
++  TemplateArgumentListInfo ToTAInfo;
++  if (const auto *Args = D->getTemplateArgsAsWritten()) {
++    if (Error Err = ImportTemplateArgumentListInfo(*Args, ToTAInfo))
++      return std::move(Err);
++  }
++
+   using PartVarSpecDecl = VarTemplatePartialSpecializationDecl;
+   // Create a new specialization.
+   if (auto *FromPartial = dyn_cast(D)) {
+@@ -6868,16 +6837,9 @@
+     if (!ToTPListOrErr)
+       return ToTPListOrErr.takeError();
+ 
+-    TemplateArgumentListInfo ToTAInfo;
+-    if (const auto *Args = D->getTemplateArgsAsWritten())
+-      if (Error Err = ImportTemplateArgumentListInfo(*Args, ToTAInfo))
+-        return std::move(Err);
+-
+     PartVarSpecDecl *ToPartial;
+     if (GetImportedOrCreateDecl(ToPartial, D, Importer.getToContext(), DC,
+                                 *BeginLocOrErr, *IdLocOrErr, *ToTPListOrErr,
+-                                ASTTemplateArgumentListInfo::Create(
+-                                    Importer.getToContext(), ToTAInfo),
+                                 VarTemplate, QualType(), nullptr,
+                                 D->getStorageClass(), TemplateArgs))
+       return ToPartial;
+@@ -6902,34 +6864,6 @@
+                                 QualType(), nullptr, D->getStorageClass(),
+                                 TemplateArgs))
+       return D2;
+-
+-    if (const auto *Info = D->getExplicitInstantiationInfo()) {
+-      auto ExternKeywordLocOrErr = import(Info->ExternKeywordLoc);
+-      if (!ExternKeywordLocOrErr)
+-        return ExternKeywordLocOrErr.takeError();
+-      auto TemplateKeywordLocOrErr = import(Info->TemplateKeywordLoc);
+-      if (!TemplateKeywordLocOrErr)
+-        return TemplateKeywordLocOrErr.takeError();
+-      TemplateArgumentListInfo ToTAInfo;
+-      if (Error Err = ImportTemplateArgumentListInfo(
+-              *Info->TemplateArgsAsWritten, ToTAInfo))
+-        return std::move(Err);
+-      D2->setExplicitInstantiationInfo(*ExternKeywordLocOrErr,
+-                                       *TemplateKeywordLocOrErr,
+-                                       ASTTemplateArgumentListInfo::Create(
+-                                           Importer.getToContext(), ToTAInfo));
+-    } else if (const auto *Info = D->getExplicitSpecializationInfo()) {
+-      auto ParamsOrErr = import(Info->TemplateParams);
+-      if (!ParamsOrErr)
+-        return ParamsOrErr.takeError();
+-      TemplateArgumentListInfo ToTAInfo;
+-      if (Error Err = ImportTemplateArgumentListInfo(
+-              *Info->TemplateArgsAsWritten, ToTAInfo))
+-        return std::move(Err);
+-      D2->setExplicitSpecializationInfo(*ParamsOrErr,
+-                                        ASTTemplateArgumentListInfo::Create(
+-                                            Importer.getToContext(), ToTAInfo));
+-    }
+   }
+ 
+   // Update InsertPos, because preceding import calls may have invalidated
+@@ -6956,6 +6890,9 @@
+ 
+   D2->setSpecializationKind(D->getSpecializationKind());
+ 
++  if (D->getTemplateArgsAsWritten())
++    D2->setTemplateArgsAsWritten(ToTAInfo);
++
+   if (auto LocOrErr = import(D->getQualifierLoc()))
+     D2->setQualifierInfo(*LocOrErr);
+   else
+diff -ruN --strip-trailing-cr a/clang/lib/AST/Comment.cpp b/clang/lib/AST/Comment.cpp
+--- a/clang/lib/AST/Comment.cpp
++++ b/clang/lib/AST/Comment.cpp
+@@ -233,13 +233,11 @@
+     Kind = FunctionKind;
+     ParamVars = FD->parameters();
+     ReturnType = FD->getReturnType();
+-    TemplateParameters = FD->getTemplateSpecializationParameters();
+-    if (ArrayRef TPLs =
+-            FD->getTemplateParameterLists();
+-        !TemplateParameters && !TPLs.empty())
+-      TemplateParameters = TPLs.back();
+-    if (TemplateParameters)
++    ArrayRef TPLs = FD->getTemplateParameterLists();
++    if (!TPLs.empty()) {
+       TemplateKind = TemplateSpecialization;
++      TemplateParameters = TPLs.back();
++    }
+ 
+     if (K == Decl::CXXMethod || K == Decl::CXXConstructor ||
+         K == Decl::CXXDestructor || K == Decl::CXXConversion) {
+diff -ruN --strip-trailing-cr a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
+--- a/clang/lib/AST/Decl.cpp
++++ b/clang/lib/AST/Decl.cpp
+@@ -51,7 +51,6 @@
+ #include "clang/Basic/TargetCXXABI.h"
+ #include "clang/Basic/TargetInfo.h"
+ #include "clang/Basic/Visibility.h"
+-#include "clang/Lex/Lexer.h"
+ #include "llvm/ADT/APSInt.h"
+ #include "llvm/ADT/ArrayRef.h"
+ #include "llvm/ADT/STLExtras.h"
+@@ -4208,7 +4207,6 @@
+   case TSK_ExplicitSpecialization:
+     return false;
+ 
+-  case TSK_FriendDeclaration:
+   case TSK_ImplicitInstantiation:
+     return true;
+ 
+@@ -4329,21 +4327,12 @@
+   return nullptr;
+ }
+ 
+-const TemplateParameterList *
+-FunctionDecl::getTemplateSpecializationParameters() const {
+-  if (const auto *Info = getTemplateSpecializationInfo())
+-    return Info->TemplateParameters;
+-  if (const auto *Info = getDependentSpecializationInfo())
+-    return Info->TemplateParameters;
+-  return nullptr;
+-}
+-
+ void FunctionDecl::setFunctionTemplateSpecialization(
+     ASTContext &C, FunctionTemplateDecl *Template,
+     TemplateArgumentList *TemplateArgs, void *InsertPos,
+-    TemplateSpecializationKind TSK, const TemplateParameterList *TemplateParams,
++    TemplateSpecializationKind TSK,
+     const TemplateArgumentListInfo *TemplateArgsAsWritten,
+-    SourceLocation PointOfInstantiation, bool AddSpecialization) {
++    SourceLocation PointOfInstantiation) {
+   assert((TemplateOrSpecialization.isNull() ||
+           isa(TemplateOrSpecialization)) &&
+          "Member function is already a specialization");
+@@ -4355,23 +4344,21 @@
+          "Member specialization must be an explicit specialization");
+   FunctionTemplateSpecializationInfo *Info =
+       FunctionTemplateSpecializationInfo::Create(
+-          C, this, Template, TSK, TemplateArgs, TemplateParams,
+-          TemplateArgsAsWritten, PointOfInstantiation,
++          C, this, Template, TSK, TemplateArgs, TemplateArgsAsWritten,
++          PointOfInstantiation,
+           dyn_cast_if_present(
+               TemplateOrSpecialization));
+   TemplateOrSpecialization = Info;
+-  if (AddSpecialization)
+-    Template->addSpecialization(Info, InsertPos);
++  Template->addSpecialization(Info, InsertPos);
+ }
+ 
+ void FunctionDecl::setDependentTemplateSpecialization(
+     ASTContext &Context, const UnresolvedSetImpl &Templates,
+-    const TemplateParameterList *TemplateParams,
+     const TemplateArgumentListInfo *TemplateArgs) {
+   assert(TemplateOrSpecialization.isNull());
+   DependentFunctionTemplateSpecializationInfo *Info =
+-      DependentFunctionTemplateSpecializationInfo::Create(
+-          Context, Templates, TemplateParams, TemplateArgs);
++      DependentFunctionTemplateSpecializationInfo::Create(Context, Templates,
++                                                          TemplateArgs);
+   TemplateOrSpecialization = Info;
+ }
+ 
+@@ -4384,22 +4371,19 @@
+ DependentFunctionTemplateSpecializationInfo *
+ DependentFunctionTemplateSpecializationInfo::Create(
+     ASTContext &Context, const UnresolvedSetImpl &Candidates,
+-    const TemplateParameterList *TemplateParams,
+     const TemplateArgumentListInfo *TArgs) {
+   const auto *TArgsWritten =
+       TArgs ? ASTTemplateArgumentListInfo::Create(Context, *TArgs) : nullptr;
+   return new (Context.Allocate(
+       totalSizeToAlloc(Candidates.size())))
+-      DependentFunctionTemplateSpecializationInfo(Candidates, TemplateParams,
+-                                                  TArgsWritten);
++      DependentFunctionTemplateSpecializationInfo(Candidates, TArgsWritten);
+ }
+ 
+ DependentFunctionTemplateSpecializationInfo::
+     DependentFunctionTemplateSpecializationInfo(
+         const UnresolvedSetImpl &Candidates,
+-        const TemplateParameterList *TemplateParams,
+         const ASTTemplateArgumentListInfo *TemplateArgsWritten)
+-    : NumCandidates(Candidates.size()), TemplateParameters(TemplateParams),
++    : NumCandidates(Candidates.size()),
+       TemplateArgumentsAsWritten(TemplateArgsWritten) {
+   std::transform(Candidates.begin(), Candidates.end(), getTrailingObjects(),
+                  [](NamedDecl *ND) {
+@@ -4559,17 +4543,6 @@
+   return false;
+ }
+ 
+-SourceLocation FunctionDecl::getFunctionLocStart() const {
+-  if (const TemplateParameterList *TemplateParams =
+-          getTemplateSpecializationParameters()) {
+-    const ASTContext &Ctx = getASTContext();
+-    return Lexer::findNextToken(TemplateParams->getSourceRange().getEnd(),
+-                                Ctx.getSourceManager(), Ctx.getLangOpts())
+-        ->getLocation();
+-  }
+-  return getInnerLocStart();
+-}
+-
+ SourceRange FunctionDecl::getSourceRange() const {
+   return SourceRange(getOuterLocStart(), EndRangeLoc);
+ }
+diff -ruN --strip-trailing-cr a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
+--- a/clang/lib/AST/DeclPrinter.cpp
++++ b/clang/lib/AST/DeclPrinter.cpp
+@@ -1105,7 +1105,6 @@
+     Out << *Attrs << ' ';
+ 
+   if (D->getIdentifier()) {
+-    // FIXME: Missing template parameter lists.
+     D->getQualifier().print(Out, Policy);
+     Out << *D;
+ 
+diff -ruN --strip-trailing-cr a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
+--- a/clang/lib/AST/DeclTemplate.cpp
++++ b/clang/lib/AST/DeclTemplate.cpp
+@@ -245,8 +245,8 @@
+   return HasRequiresClause || HasConstrainedParameters;
+ }
+ 
+-ArrayRef TemplateParameterList::getInjectedTemplateArgs(
+-    const ASTContext &Context) const {
++ArrayRef
++TemplateParameterList::getInjectedTemplateArgs(const ASTContext &Context) {
+   if (!InjectedArgs) {
+     InjectedArgs = new (Context) TemplateArgument[size()];
+     llvm::transform(*this, InjectedArgs, [&](NamedDecl *ND) {
+@@ -946,7 +946,6 @@
+ FunctionTemplateSpecializationInfo *FunctionTemplateSpecializationInfo::Create(
+     ASTContext &C, FunctionDecl *FD, FunctionTemplateDecl *Template,
+     TemplateSpecializationKind TSK, TemplateArgumentList *TemplateArgs,
+-    const TemplateParameterList *TemplateParams,
+     const TemplateArgumentListInfo *TemplateArgsAsWritten, SourceLocation POI,
+     MemberSpecializationInfo *MSInfo) {
+   const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr;
+@@ -957,8 +956,7 @@
+   void *Mem =
+       C.Allocate(totalSizeToAlloc(MSInfo ? 1 : 0));
+   return new (Mem) FunctionTemplateSpecializationInfo(
+-      FD, Template, TSK, TemplateArgs, TemplateParams, ArgsAsWritten, POI,
+-      MSInfo);
++      FD, Template, TSK, TemplateArgs, ArgsAsWritten, POI, MSInfo);
+ }
+ 
+ //===----------------------------------------------------------------------===//
+@@ -1052,38 +1050,54 @@
+       return CTPSD->getSourceRange();
+     return cast(Pattern)->getSourceRange();
+   }
+-  case TSK_FriendDeclaration:
+   case TSK_ExplicitSpecialization: {
+-    const auto *Info = getExplicitSpecializationInfo();
+-    auto TPLs = getTemplateParameterLists();
+-    return SourceRange(TPLs.empty() ? Info->TemplateParams->getTemplateLoc()
+-                                    : TPLs.front()->getTemplateLoc(),
+-                       isThisDeclarationADefinition()
+-                           ? CXXRecordDecl::getSourceRange().getEnd()
+-                           : Info->TemplateArgsAsWritten->getRAngleLoc());
++    SourceRange Range = CXXRecordDecl::getSourceRange();
++    if (const ASTTemplateArgumentListInfo *Args = getTemplateArgsAsWritten();
++        !isThisDeclarationADefinition() && Args)
++      Range.setEnd(Args->getRAngleLoc());
++    return Range;
+   }
+   case TSK_ExplicitInstantiationDeclaration:
+   case TSK_ExplicitInstantiationDefinition: {
+-    const auto *Info = getExplicitInstantiationInfo();
+-    return SourceRange(Info->ExternKeywordLoc.isValid()
+-                           ? Info->ExternKeywordLoc
+-                           : Info->TemplateKeywordLoc,
+-                       Info->TemplateArgsAsWritten->getRAngleLoc());
++    SourceRange Range = CXXRecordDecl::getSourceRange();
++    if (SourceLocation ExternKW = getExternKeywordLoc(); ExternKW.isValid())
++      Range.setBegin(ExternKW);
++    else if (SourceLocation TemplateKW = getTemplateKeywordLoc();
++             TemplateKW.isValid())
++      Range.setBegin(TemplateKW);
++    if (const ASTTemplateArgumentListInfo *Args = getTemplateArgsAsWritten())
++      Range.setEnd(Args->getRAngleLoc());
++    return Range;
+   }
+   }
+   llvm_unreachable("unhandled template specialization kind");
+ }
+ 
+-void ClassTemplateSpecializationDecl::setExplicitSpecializationInfo(
+-    TemplateParameterList *TemplateParams,
+-    const ASTTemplateArgumentListInfo *TemplateArgsAsWritten) {
+-  auto *Info = new (getASTContext()) ExplicitSpecializationInfo();
+-  Info->TemplateParams = TemplateParams;
+-  Info->TemplateArgsAsWritten = TemplateArgsAsWritten;
+-  ExplicitInfo = Info;
+-
+-  if (AdoptTemplateParameterList(TemplateParams, this))
+-    setInvalidDecl();
++void ClassTemplateSpecializationDecl::setExternKeywordLoc(SourceLocation Loc) {
++  auto *Info = dyn_cast_if_present(ExplicitInfo);
++  if (!Info) {
++    // Don't allocate if the location is invalid.
++    if (Loc.isInvalid())
++      return;
++    Info = new (getASTContext()) ExplicitInstantiationInfo;
++    Info->TemplateArgsAsWritten = getTemplateArgsAsWritten();
++    ExplicitInfo = Info;
++  }
++  Info->ExternKeywordLoc = Loc;
++}
++
++void ClassTemplateSpecializationDecl::setTemplateKeywordLoc(
++    SourceLocation Loc) {
++  auto *Info = dyn_cast_if_present(ExplicitInfo);
++  if (!Info) {
++    // Don't allocate if the location is invalid.
++    if (Loc.isInvalid())
++      return;
++    Info = new (getASTContext()) ExplicitInstantiationInfo;
++    Info->TemplateArgsAsWritten = getTemplateArgsAsWritten();
++    ExplicitInfo = Info;
++  }
++  Info->TemplateKeywordLoc = Loc;
+ }
+ 
+ //===----------------------------------------------------------------------===//
+@@ -1154,7 +1168,6 @@
+ ClassTemplatePartialSpecializationDecl::ClassTemplatePartialSpecializationDecl(
+     ASTContext &Context, TagKind TK, DeclContext *DC, SourceLocation StartLoc,
+     SourceLocation IdLoc, TemplateParameterList *Params,
+-    const ASTTemplateArgumentListInfo *ArgsAsWritten,
+     ClassTemplateDecl *SpecializedTemplate, ArrayRef Args,
+     CanQualType CanonInjectedTST,
+     ClassTemplatePartialSpecializationDecl *PrevDecl)
+@@ -1163,24 +1176,25 @@
+           // Tracking StrictPackMatch for Partial
+           // Specializations is not needed.
+           SpecializedTemplate, Args, /*StrictPackMatch=*/false, PrevDecl),
+-      InstantiatedFromMember(nullptr, false),
++      TemplateParams(Params), InstantiatedFromMember(nullptr, false),
+       CanonInjectedTST(CanonInjectedTST) {
+-  setSpecializationKind(TSK_ExplicitSpecialization);
+-  setExplicitSpecializationInfo(Params, ArgsAsWritten);
++  if (AdoptTemplateParameterList(Params, this))
++    setInvalidDecl();
+ }
+ 
+ ClassTemplatePartialSpecializationDecl *
+ ClassTemplatePartialSpecializationDecl::Create(
+     ASTContext &Context, TagKind TK, DeclContext *DC, SourceLocation StartLoc,
+     SourceLocation IdLoc, TemplateParameterList *Params,
+-    const ASTTemplateArgumentListInfo *ArgsAsWritten,
+     ClassTemplateDecl *SpecializedTemplate, ArrayRef Args,
+     CanQualType CanonInjectedTST,
+     ClassTemplatePartialSpecializationDecl *PrevDecl) {
+   assert(!Params->empty() && "template with no template parameters");
+-  return new (Context, DC) ClassTemplatePartialSpecializationDecl(
+-      Context, TK, DC, StartLoc, IdLoc, Params, ArgsAsWritten,
+-      SpecializedTemplate, Args, CanonInjectedTST, PrevDecl);
++  auto *Result = new (Context, DC) ClassTemplatePartialSpecializationDecl(
++      Context, TK, DC, StartLoc, IdLoc, Params, SpecializedTemplate, Args,
++      CanonInjectedTST, PrevDecl);
++  Result->setSpecializationKind(TSK_ExplicitSpecialization);
++  return Result;
+ }
+ 
+ ClassTemplatePartialSpecializationDecl *
+@@ -1207,7 +1221,11 @@
+           getInstantiatedFromMember();
+       MT && !isMemberSpecialization())
+     return MT->getSourceRange();
+-  return ClassTemplateSpecializationDecl::getSourceRange();
++  SourceRange Range = ClassTemplateSpecializationDecl::getSourceRange();
++  if (const TemplateParameterList *TPL = getTemplateParameters();
++      TPL && getTemplateParameterLists().empty())
++    Range.setBegin(TPL->getTemplateLoc());
++  return Range;
+ }
+ 
+ //===----------------------------------------------------------------------===//
+@@ -1466,11 +1484,8 @@
+     }
+     return VTD->getCanonicalDecl()->getSourceRange();
+   }
+-  case TSK_FriendDeclaration:
+   case TSK_ExplicitSpecialization: {
+     SourceRange Range = VarDecl::getSourceRange();
+-    if (const auto *Info = getExplicitSpecializationInfo())
+-      Range.setBegin(Info->TemplateParams->getTemplateLoc());
+     if (const ASTTemplateArgumentListInfo *Args = getTemplateArgsAsWritten();
+         !hasInit() && Args)
+       Range.setEnd(Args->getRAngleLoc());
+@@ -1478,11 +1493,10 @@
+   }
+   case TSK_ExplicitInstantiationDeclaration:
+   case TSK_ExplicitInstantiationDefinition: {
+-    const auto *Info = getExplicitInstantiationInfo();
+     SourceRange Range = VarDecl::getSourceRange();
+-    if (SourceLocation ExternKW = Info->ExternKeywordLoc; ExternKW.isValid())
++    if (SourceLocation ExternKW = getExternKeywordLoc(); ExternKW.isValid())
+       Range.setBegin(ExternKW);
+-    else if (SourceLocation TemplateKW = Info->TemplateKeywordLoc;
++    else if (SourceLocation TemplateKW = getTemplateKeywordLoc();
+              TemplateKW.isValid())
+       Range.setBegin(TemplateKW);
+     if (const ASTTemplateArgumentListInfo *Args = getTemplateArgsAsWritten())
+@@ -1493,16 +1507,30 @@
+   llvm_unreachable("unhandled template specialization kind");
+ }
+ 
+-void VarTemplateSpecializationDecl::setExplicitSpecializationInfo(
+-    TemplateParameterList *TemplateParams,
+-    const ASTTemplateArgumentListInfo *TemplateArgsAsWritten) {
+-  auto *Info = new (getASTContext()) ExplicitSpecializationInfo();
+-  Info->TemplateParams = TemplateParams;
+-  Info->TemplateArgsAsWritten = TemplateArgsAsWritten;
+-  ExplicitInfo = Info;
+-
+-  if (AdoptTemplateParameterList(TemplateParams, getDeclContext()))
+-    setInvalidDecl();
++void VarTemplateSpecializationDecl::setExternKeywordLoc(SourceLocation Loc) {
++  auto *Info = dyn_cast_if_present(ExplicitInfo);
++  if (!Info) {
++    // Don't allocate if the location is invalid.
++    if (Loc.isInvalid())
++      return;
++    Info = new (getASTContext()) ExplicitInstantiationInfo;
++    Info->TemplateArgsAsWritten = getTemplateArgsAsWritten();
++    ExplicitInfo = Info;
++  }
++  Info->ExternKeywordLoc = Loc;
++}
++
++void VarTemplateSpecializationDecl::setTemplateKeywordLoc(SourceLocation Loc) {
++  auto *Info = dyn_cast_if_present(ExplicitInfo);
++  if (!Info) {
++    // Don't allocate if the location is invalid.
++    if (Loc.isInvalid())
++      return;
++    Info = new (getASTContext()) ExplicitInstantiationInfo;
++    Info->TemplateArgsAsWritten = getTemplateArgsAsWritten();
++    ExplicitInfo = Info;
++  }
++  Info->TemplateKeywordLoc = Loc;
+ }
+ 
+ //===----------------------------------------------------------------------===//
+@@ -1514,28 +1542,28 @@
+ VarTemplatePartialSpecializationDecl::VarTemplatePartialSpecializationDecl(
+     ASTContext &Context, DeclContext *DC, SourceLocation StartLoc,
+     SourceLocation IdLoc, TemplateParameterList *Params,
+-    const ASTTemplateArgumentListInfo *ArgsAsWritten,
+     VarTemplateDecl *SpecializedTemplate, QualType T, TypeSourceInfo *TInfo,
+     StorageClass S, ArrayRef Args)
+     : VarTemplateSpecializationDecl(VarTemplatePartialSpecialization, Context,
+                                     DC, StartLoc, IdLoc, SpecializedTemplate, T,
+                                     TInfo, S, Args),
+-      InstantiatedFromMember(nullptr, false) {
+-  setSpecializationKind(TSK_ExplicitSpecialization);
+-  setExplicitSpecializationInfo(Params, ArgsAsWritten);
++      TemplateParams(Params), InstantiatedFromMember(nullptr, false) {
++  if (AdoptTemplateParameterList(Params, DC))
++    setInvalidDecl();
+ }
+ 
+ VarTemplatePartialSpecializationDecl *
+ VarTemplatePartialSpecializationDecl::Create(
+     ASTContext &Context, DeclContext *DC, SourceLocation StartLoc,
+     SourceLocation IdLoc, TemplateParameterList *Params,
+-    const ASTTemplateArgumentListInfo *ArgsAsWritten,
+     VarTemplateDecl *SpecializedTemplate, QualType T, TypeSourceInfo *TInfo,
+     StorageClass S, ArrayRef Args) {
+   assert(!Params->empty() && "template with no template parameters");
+-  return new (Context, DC) VarTemplatePartialSpecializationDecl(
+-      Context, DC, StartLoc, IdLoc, Params, ArgsAsWritten, SpecializedTemplate,
+-      T, TInfo, S, Args);
++  auto *Result = new (Context, DC) VarTemplatePartialSpecializationDecl(
++      Context, DC, StartLoc, IdLoc, Params, SpecializedTemplate, T, TInfo, S,
++      Args);
++  Result->setSpecializationKind(TSK_ExplicitSpecialization);
++  return Result;
+ }
+ 
+ VarTemplatePartialSpecializationDecl *
+@@ -1549,7 +1577,11 @@
+           getInstantiatedFromMember();
+       MT && !isMemberSpecialization())
+     return MT->getSourceRange();
+-  return VarTemplateSpecializationDecl::getSourceRange();
++  SourceRange Range = VarTemplateSpecializationDecl::getSourceRange();
++  if (const TemplateParameterList *TPL = getTemplateParameters();
++      TPL && getTemplateParameterLists().empty())
++    Range.setBegin(TPL->getTemplateLoc());
++  return Range;
+ }
+ 
+ static TemplateParameterList *createBuiltinTemplateParameterList(
+diff -ruN --strip-trailing-cr a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp
+--- a/clang/lib/AST/JSONNodeDumper.cpp
++++ b/clang/lib/AST/JSONNodeDumper.cpp
+@@ -1131,9 +1131,6 @@
+   case TSK_ImplicitInstantiation:
+     JOS.attribute("templateSpecializationKind", "implicit_instantiation");
+     break;
+-  case TSK_FriendDeclaration:
+-    JOS.attribute("templateSpecializationKind", "friend_declaration");
+-    break;
+   case TSK_ExplicitSpecialization:
+     JOS.attribute("templateSpecializationKind", "explicit_specialization");
+     break;
+diff -ruN --strip-trailing-cr a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
+--- a/clang/lib/AST/TextNodeDumper.cpp
++++ b/clang/lib/AST/TextNodeDumper.cpp
+@@ -1044,9 +1044,6 @@
+   case TSK_ImplicitInstantiation:
+     OS << " implicit_instantiation";
+     break;
+-  case TSK_FriendDeclaration:
+-    OS << " friend_declaration";
+-    break;
+   case TSK_ExplicitSpecialization:
+     OS << " explicit_specialization";
+     break;
+diff -ruN --strip-trailing-cr a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
++++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+@@ -99,7 +99,6 @@
+   //
+   // Other:
+   // equalsNode
+-  // declaresSameEntityAsNode
+ 
+   registerMatcher("mapAnyOf",
+                   std::make_unique());
+diff -ruN --strip-trailing-cr a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
+--- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
++++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
+@@ -365,7 +365,6 @@
+                    : cir::GlobalLinkageKind::InternalLinkage;
+       return cir::GlobalLinkageKind::ExternalLinkage;
+ 
+-    case TSK_FriendDeclaration:
+     case TSK_ImplicitInstantiation:
+       return cir::GlobalLinkageKind::LinkOnceODRLinkage;
+ 
+@@ -398,7 +397,6 @@
+   case TSK_Undeclared:
+   case TSK_ExplicitSpecialization:
+   case TSK_ImplicitInstantiation:
+-  case TSK_FriendDeclaration:
+     return discardableODRLinkage;
+ 
+   case TSK_ExplicitInstantiationDeclaration:
+diff -ruN --strip-trailing-cr a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp
+--- a/clang/lib/CodeGen/CGVTables.cpp
++++ b/clang/lib/CodeGen/CGVTables.cpp
+@@ -1149,22 +1149,21 @@
+ 
+       return llvm::GlobalVariable::ExternalLinkage;
+ 
+-    case TSK_FriendDeclaration:
+-    case TSK_ImplicitInstantiation:
+-      return !Context.getLangOpts().AppleKext
+-                 ? llvm::GlobalVariable::LinkOnceODRLinkage
+-                 : llvm::Function::InternalLinkage;
+-
+-    case TSK_ExplicitInstantiationDefinition:
+-      return !Context.getLangOpts().AppleKext
+-                 ? llvm::GlobalVariable::WeakODRLinkage
+-                 : llvm::Function::InternalLinkage;
+-
+-    case TSK_ExplicitInstantiationDeclaration:
+-      return IsExternalDefinition
+-                 ? llvm::GlobalVariable::AvailableExternallyLinkage
+-                 : llvm::GlobalVariable::ExternalLinkage;
+-    }
++      case TSK_ImplicitInstantiation:
++        return !Context.getLangOpts().AppleKext ?
++                 llvm::GlobalVariable::LinkOnceODRLinkage :
++                 llvm::Function::InternalLinkage;
++
++      case TSK_ExplicitInstantiationDefinition:
++        return !Context.getLangOpts().AppleKext ?
++                 llvm::GlobalVariable::WeakODRLinkage :
++                 llvm::Function::InternalLinkage;
++
++      case TSK_ExplicitInstantiationDeclaration:
++        return IsExternalDefinition
++                   ? llvm::GlobalVariable::AvailableExternallyLinkage
++                   : llvm::GlobalVariable::ExternalLinkage;
++      }
+   }
+ 
+   // -fapple-kext mode does not support weak linkage, so we must use
+@@ -1189,7 +1188,6 @@
+     case TSK_Undeclared:
+     case TSK_ExplicitSpecialization:
+     case TSK_ImplicitInstantiation:
+-    case TSK_FriendDeclaration:
+       return DiscardableODRLinkage;
+ 
+     case TSK_ExplicitInstantiationDeclaration:
+diff -ruN --strip-trailing-cr a/clang/lib/Index/IndexingContext.cpp b/clang/lib/Index/IndexingContext.cpp
+--- a/clang/lib/Index/IndexingContext.cpp
++++ b/clang/lib/Index/IndexingContext.cpp
+@@ -185,7 +185,6 @@
+       return isa(D);
+     case TSK_ExplicitSpecialization:
+       return false;
+-    case TSK_FriendDeclaration:
+     case TSK_ImplicitInstantiation:
+     case TSK_ExplicitInstantiationDeclaration:
+     case TSK_ExplicitInstantiationDefinition:
+diff -ruN --strip-trailing-cr a/clang/lib/InstallAPI/Visitor.cpp b/clang/lib/InstallAPI/Visitor.cpp
+--- a/clang/lib/InstallAPI/Visitor.cpp
++++ b/clang/lib/InstallAPI/Visitor.cpp
+@@ -318,7 +318,6 @@
+     case TSK_Undeclared:
+     case TSK_ExplicitSpecialization:
+     case TSK_ImplicitInstantiation:
+-    case TSK_FriendDeclaration:
+     case TSK_ExplicitInstantiationDefinition:
+       return true;
+     case TSK_ExplicitInstantiationDeclaration:
+@@ -335,7 +334,6 @@
+   case TSK_Undeclared:
+   case TSK_ExplicitSpecialization:
+   case TSK_ImplicitInstantiation:
+-  case TSK_FriendDeclaration:
+     return false;
+ 
+   case TSK_ExplicitInstantiationDeclaration:
+@@ -363,7 +361,6 @@
+       if (isInlined(KeyFunctionD))
+         return CXXLinkage::LinkOnceODRLinkage;
+       return CXXLinkage::ExternalLinkage;
+-    case TSK_FriendDeclaration:
+     case TSK_ImplicitInstantiation:
+       llvm_unreachable("No external vtable for implicit instantiations");
+     case TSK_ExplicitInstantiationDefinition:
+@@ -378,7 +375,6 @@
+   case TSK_Undeclared:
+   case TSK_ExplicitSpecialization:
+   case TSK_ImplicitInstantiation:
+-  case TSK_FriendDeclaration:
+     return CXXLinkage::LinkOnceODRLinkage;
+   case TSK_ExplicitInstantiationDeclaration:
+   case TSK_ExplicitInstantiationDefinition:
+@@ -605,7 +601,6 @@
+     case TSK_Undeclared:
+     case TSK_ExplicitSpecialization:
+       break;
+-    case TSK_FriendDeclaration:
+     case TSK_ImplicitInstantiation:
+       continue;
+     case TSK_ExplicitInstantiationDeclaration:
+diff -ruN --strip-trailing-cr a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
+--- a/clang/lib/Parse/ParseDeclCXX.cpp
++++ b/clang/lib/Parse/ParseDeclCXX.cpp
+@@ -2059,17 +2059,15 @@
+           TemplateParams = &FakedParamLists;
+         }
+       }
+-      MultiTemplateParamsArg ParamLists(
+-          TemplateParams ? &(*TemplateParams)[0] : nullptr,
+-          TemplateParams ? TemplateParams->size() : 0);
++
+       // Build the class template specialization.
+       TagOrTempResult = Actions.ActOnClassTemplateSpecialization(
+           getCurScope(), TagType, TUK, StartLoc, DS.getModulePrivateSpecLoc(),
+-          SS, *TemplateId, attrs, ParamLists, &SkipBody);
+-      // Some template parameter lists may have been dropped because they were
+-      // extraneous.
+-      if (TemplateParams)
+-        TemplateParams->resize(ParamLists.size());
++          SS, *TemplateId, attrs,
++          MultiTemplateParamsArg(TemplateParams ? &(*TemplateParams)[0]
++                                                : nullptr,
++                                 TemplateParams ? TemplateParams->size() : 0),
++          &SkipBody);
+     }
+   } else if (TemplateInfo.Kind == ParsedTemplateKind::ExplicitInstantiation &&
+              TUK == TagUseKind::Declaration) {
+diff -ruN --strip-trailing-cr a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
+--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
++++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
+@@ -317,6 +317,12 @@
+           ElaboratedTypeKeyword::Class, TemplateName(TextureTemplate),
+           {TemplateArgument(VectorType)}, {}));
+ 
++  auto *PartialSpec = ClassTemplatePartialSpecializationDecl::Create(
++      AST, TagDecl::TagKind::Class, HLSLNamespace, SourceLocation(),
++      SourceLocation(), TemplateParams, TextureTemplate,
++      {TemplateArgument(VectorType)},
++      CanQualType::CreateUnsafe(CanonInjectedTST), nullptr);
++
+   // Set the template arguments as written.
+   TemplateArgument Arg(VectorType);
+   TemplateArgumentLoc ArgLoc =
+@@ -324,13 +330,8 @@
+   TemplateArgumentListInfo ArgsInfo =
+       TemplateArgumentListInfo(SourceLocation(), SourceLocation());
+   ArgsInfo.addArgument(ArgLoc);
+-
+-  auto *PartialSpec = ClassTemplatePartialSpecializationDecl::Create(
+-      AST, TagDecl::TagKind::Class, HLSLNamespace, SourceLocation(),
+-      SourceLocation(), TemplateParams,
+-      ASTTemplateArgumentListInfo::Create(AST, ArgsInfo), TextureTemplate,
+-      {TemplateArgument(VectorType)},
+-      CanQualType::CreateUnsafe(CanonInjectedTST), nullptr);
++  PartialSpec->setTemplateArgsAsWritten(
++      ASTTemplateArgumentListInfo::Create(AST, ArgsInfo));
+ 
+   PartialSpec->setImplicit(true);
+   PartialSpec->setLexicalDeclContext(HLSLNamespace);
+diff -ruN --strip-trailing-cr a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
+--- a/clang/lib/Sema/SemaConcept.cpp
++++ b/clang/lib/Sema/SemaConcept.cpp
+@@ -200,10 +200,15 @@
+ // Figure out the to-translation-unit depth for this function declaration for
+ // the purpose of seeing if they differ by constraints. This isn't the same as
+ // getTemplateDepth, because it includes already instantiated parents.
+-static unsigned CalculateTemplateDepthForConstraints(Sema &S,
+-                                                     const NamedDecl *ND) {
+-  // FIXME: This is a very expensive way to calculate this.
+-  MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(ND);
++static unsigned
++CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND,
++                                     bool SkipForSpecialization = false) {
++  MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
++      ND, ND->getLexicalDeclContext(), /*Final=*/false,
++      /*Innermost=*/std::nullopt,
++      /*RelativeToPrimary=*/true,
++      /*Pattern=*/nullptr,
++      /*ForConstraintInstantiation=*/true, SkipForSpecialization);
+   return MLTAL.getNumLevels();
+ }
+ 
+@@ -1330,8 +1335,10 @@
+   return false;
+ }
+ 
+-static ExprResult SubstituteConceptsInConstraintExpression(
+-    Sema &S, const ConceptSpecializationExpr *CSE, UnsignedOrNone SubstIndex) {
++static ExprResult
++SubstituteConceptsInConstraintExpression(Sema &S, const NamedDecl *D,
++                                         const ConceptSpecializationExpr *CSE,
++                                         UnsignedOrNone SubstIndex) {
+   Sema::SFINAETrap Trap(S);
+   // [C++2c] [temp.constr.normal]
+   // Otherwise, to form CE, any non-dependent concept template argument Ai
+@@ -1339,8 +1346,7 @@
+   // If any such substitution results in an invalid concept-id,
+   // the program is ill-formed; no diagnostic is required.
+ 
+-  Expr *ConstraintExpr =
+-      CSE->getNamedConcept()->getCanonicalDecl()->getConstraintExpr();
++  ConceptDecl *Concept = CSE->getNamedConcept()->getCanonicalDecl();
+   Sema::ArgPackSubstIndexRAII _(S, SubstIndex);
+ 
+   const ASTTemplateArgumentListInfo *ArgsAsWritten =
+@@ -1350,17 +1356,23 @@
+             return !ArgLoc.getArgument().isDependent() &&
+                    ArgLoc.getArgument().isConceptOrConceptTemplateParameter();
+           })) {
+-    return ConstraintExpr;
++    return Concept->getConstraintExpr();
+   }
+ 
+-  return S.SubstConceptTemplateArguments(
+-      CSE, ConstraintExpr,
+-      S.getTemplateInstantiationArgs(CSE->getSpecializationDecl()));
++  MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
++      Concept, Concept->getLexicalDeclContext(),
++      /*Final=*/false, CSE->getTemplateArguments(),
++      /*RelativeToPrimary=*/true,
++      /*Pattern=*/nullptr,
++      /*ForConstraintInstantiation=*/true);
++  return S.SubstConceptTemplateArguments(CSE, Concept->getConstraintExpr(),
++                                         MLTAL);
+ }
+ 
+-bool Sema::SetupConstraintScope(FunctionDecl *FD,
+-                                const MultiLevelTemplateArgumentList &MLTAL,
+-                                LocalInstantiationScope &Scope) {
++bool Sema::SetupConstraintScope(
++    FunctionDecl *FD, std::optional> TemplateArgs,
++    const MultiLevelTemplateArgumentList &MLTAL,
++    LocalInstantiationScope &Scope) {
+   assert(!isLambdaCallOperator(FD) &&
+          "Use LambdaScopeForCallOperatorInstantiationRAII to handle lambda "
+          "instantiations");
+@@ -1369,7 +1381,8 @@
+     InstantiatingTemplate Inst(
+         *this, FD->getPointOfInstantiation(),
+         Sema::InstantiatingTemplate::ConstraintsCheck{}, PrimaryTemplate,
+-        FD->getTemplateSpecializationArgs()->asArray(), SourceRange());
++        TemplateArgs ? *TemplateArgs : ArrayRef{},
++        SourceRange());
+     if (Inst.isInvalid())
+       return true;
+ 
+@@ -1405,10 +1418,11 @@
+             ? FD->getInstantiatedFromMemberFunction()
+             : FD->getInstantiatedFromDecl();
+ 
+-    InstantiatingTemplate Inst(*this, FD->getPointOfInstantiation(),
+-                               Sema::InstantiatingTemplate::ConstraintsCheck{},
+-                               InstantiatedFrom, ArrayRef(),
+-                               SourceRange());
++    InstantiatingTemplate Inst(
++        *this, FD->getPointOfInstantiation(),
++        Sema::InstantiatingTemplate::ConstraintsCheck{}, InstantiatedFrom,
++        TemplateArgs ? *TemplateArgs : ArrayRef{},
++        SourceRange());
+     if (Inst.isInvalid())
+       return true;
+ 
+@@ -1425,12 +1439,23 @@
+ // constraint-instantiation and checking.
+ std::optional
+ Sema::SetupConstraintCheckingTemplateArgumentsAndScope(
+-    FunctionDecl *FD, LocalInstantiationScope &Scope) {
+-  MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs(FD);
++    FunctionDecl *FD, std::optional> TemplateArgs,
++    LocalInstantiationScope &Scope) {
++  MultiLevelTemplateArgumentList MLTAL;
++
++  // Collect the list of template arguments relative to the 'primary' template.
++  // We need the entire list, since the constraint is completely uninstantiated
++  // at this point.
++  MLTAL =
++      getTemplateInstantiationArgs(FD, FD->getLexicalDeclContext(),
++                                   /*Final=*/false, /*Innermost=*/std::nullopt,
++                                   /*RelativeToPrimary=*/true,
++                                   /*Pattern=*/nullptr,
++                                   /*ForConstraintInstantiation=*/true);
+   // Lambdas are handled by LambdaScopeForCallOperatorInstantiationRAII.
+   if (isLambdaCallOperator(FD))
+     return MLTAL;
+-  if (SetupConstraintScope(FD, MLTAL, Scope))
++  if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope))
+     return std::nullopt;
+ 
+   return MLTAL;
+@@ -1477,7 +1502,7 @@
+   LocalInstantiationScope Scope(*this, !ForOverloadResolution);
+   std::optional MLTAL =
+       SetupConstraintCheckingTemplateArgumentsAndScope(
+-          const_cast(FD), Scope);
++          const_cast(FD), {}, Scope);
+ 
+   if (!MLTAL)
+     return true;
+@@ -1500,10 +1525,15 @@
+       Satisfaction);
+ }
+ 
+-static const Expr *
+-SubstituteConstraintExpressionWithoutSatisfaction(Sema &S, const Decl *ND,
+-                                                  const Expr *ConstrExpr) {
+-  MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(ND);
++static const Expr *SubstituteConstraintExpressionWithoutSatisfaction(
++    Sema &S, const Sema::TemplateCompareNewDeclInfo &DeclInfo,
++    const Expr *ConstrExpr) {
++  MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
++      DeclInfo.getDecl(), DeclInfo.getDeclContext(), /*Final=*/false,
++      /*Innermost=*/std::nullopt,
++      /*RelativeToPrimary=*/true,
++      /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true,
++      /*SkipForSpecialization*/ false);
+ 
+   if (MLTAL.getNumSubstitutedLevels() == 0)
+     return ConstrExpr;
+@@ -1513,7 +1543,8 @@
+   // this may happen while we're comparing two templates' constraint
+   // equivalence.
+   std::optional ScopeForParameters;
+-  if (ND->isFunctionOrFunctionTemplate()) {
++  if (const NamedDecl *ND = DeclInfo.getDecl();
++      ND && ND->isFunctionOrFunctionTemplate()) {
+     ScopeForParameters.emplace(S, /*CombineWithOuterScope=*/true);
+     const FunctionDecl *FD = ND->getAsFunction();
+     if (FunctionTemplateDecl *Template = FD->getDescribedFunctionTemplate();
+@@ -1557,9 +1588,13 @@
+   // possible that e.g. constraints involving C> and C are
+   // perceived identical.
+   std::optional ContextScope;
+-  const DeclContext *DC = ND->getFriendObjectKind()
+-                              ? ND->getLexicalDeclContext()
+-                              : ND->getDeclContext();
++  const DeclContext *DC = [&] {
++    if (!DeclInfo.getDecl())
++      return DeclInfo.getDeclContext();
++    return DeclInfo.getDecl()->getFriendObjectKind()
++               ? DeclInfo.getLexicalDeclContext()
++               : DeclInfo.getDeclContext();
++  }();
+   if (auto *RD = dyn_cast(DC)) {
+     ThisScope.emplace(S, const_cast(RD), Qualifiers());
+     ContextScope.emplace(S, const_cast(cast(RD)),
+@@ -1575,14 +1610,15 @@
+   return SubstConstr.get();
+ }
+ 
+-bool Sema::AreConstraintExpressionsEqual(const Decl *Old, const Expr *OldConstr,
+-                                         const Decl *New,
++bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old,
++                                         const Expr *OldConstr,
++                                         const TemplateCompareNewDeclInfo &New,
+                                          const Expr *NewConstr) {
+   if (OldConstr == NewConstr)
+     return true;
+   // C++ [temp.constr.decl]p4
+-  if (Old != New &&
+-      Old->getLexicalDeclContext() != New->getLexicalDeclContext()) {
++  if (Old && !New.isInvalid() && !New.ContainsDecl(Old) &&
++      Old->getLexicalDeclContext() != New.getLexicalDeclContext()) {
+     Sema::SFINAETrap _(*this);
+     if (const Expr *SubstConstr =
+             SubstituteConstraintExpressionWithoutSatisfaction(*this, Old,
+@@ -1604,15 +1640,19 @@
+   return ID1 == ID2;
+ }
+ 
+-bool Sema::FriendConstraintsDependOnEnclosingTemplate(
+-    const FunctionTemplateDecl *FTD) {
+-  assert(FTD->getFriendObjectKind() && "Must be a friend!");
++bool Sema::FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD) {
++  assert(FD->getFriendObjectKind() && "Must be a friend!");
++
++  // The logic for non-templates is handled in ASTContext::isSameEntity, so we
++  // don't have to bother checking 'DependsOnEnclosingTemplate' for a
++  // non-function-template.
++  assert(FD->getDescribedFunctionTemplate() &&
++         "Non-function templates don't need to be checked");
+ 
+   SmallVector ACs;
+-  FTD->getAssociatedConstraints(ACs);
++  FD->getDescribedFunctionTemplate()->getAssociatedConstraints(ACs);
+ 
+-  const FunctionDecl *FD = FTD->getTemplatedDecl();
+-  unsigned OldTemplateDepth = FTD->getTemplateDepth();
++  unsigned OldTemplateDepth = CalculateTemplateDepthForConstraints(*this, FD);
+   for (const AssociatedConstraint &AC : ACs)
+     if (ConstraintExpressionDependsOnEnclosingTemplate(FD, OldTemplateDepth,
+                                                        AC.ConstraintExpr))
+@@ -1648,9 +1688,9 @@
+   return false;
+ }
+ 
+-bool Sema::CheckFunctionTemplateConstraints(
+-    SourceLocation PointOfInstantiation, FunctionTemplateDecl *Template,
+-    ArrayRef TemplateArgs,
++static bool CheckFunctionConstraintsWithoutInstantiation(
++    Sema &SemaRef, SourceLocation PointOfInstantiation,
++    FunctionTemplateDecl *Template, ArrayRef TemplateArgs,
+     ConstraintSatisfaction &Satisfaction) {
+   SmallVector TemplateAC;
+   Template->getAssociatedConstraints(TemplateAC);
+@@ -1659,22 +1699,47 @@
+     return false;
+   }
+ 
+-  LocalInstantiationScope Scope(*this);
++  LocalInstantiationScope Scope(SemaRef);
+ 
+-  MultiLevelTemplateArgumentList MLTAL =
+-      getTemplateInstantiationArgs(Template, TemplateArgs);
++  FunctionDecl *FD = Template->getTemplatedDecl();
++  // Collect the list of template arguments relative to the 'primary'
++  // template. We need the entire list, since the constraint is completely
++  // uninstantiated at this point.
+ 
+-  Sema::ContextRAII SavedContext(*this, Template->getTemplatedDecl());
+-  return CheckConstraintSatisfaction(Template, TemplateAC, MLTAL,
+-                                     PointOfInstantiation, Satisfaction);
++  MultiLevelTemplateArgumentList MLTAL;
++  {
++    // getTemplateInstantiationArgs uses this instantiation context to find out
++    // template arguments for uninstantiated functions.
++    // We don't want this RAII object to persist, because there would be
++    // otherwise duplicate diagnostic notes.
++    Sema::InstantiatingTemplate Inst(
++        SemaRef, PointOfInstantiation,
++        Sema::InstantiatingTemplate::ConstraintsCheck{}, Template, TemplateArgs,
++        PointOfInstantiation);
++    if (Inst.isInvalid())
++      return true;
++    MLTAL = SemaRef.getTemplateInstantiationArgs(
++        /*D=*/FD, FD,
++        /*Final=*/false, /*Innermost=*/{}, /*RelativeToPrimary=*/true,
++        /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true);
++  }
++
++  Sema::ContextRAII SavedContext(SemaRef, FD);
++  return SemaRef.CheckConstraintSatisfaction(
++      Template, TemplateAC, MLTAL, PointOfInstantiation, Satisfaction);
+ }
+ 
+-bool Sema::CheckFunctionSpecializationConstraints(
++bool Sema::CheckFunctionTemplateConstraints(
+     SourceLocation PointOfInstantiation, FunctionDecl *Decl,
++    ArrayRef TemplateArgs,
+     ConstraintSatisfaction &Satisfaction) {
+   // In most cases we're not going to have constraints, so check for that first.
+   FunctionTemplateDecl *Template = Decl->getPrimaryTemplate();
+-  assert(Template && "Function is not a specialization");
++
++  if (!Template)
++    return ::CheckFunctionConstraintsWithoutInstantiation(
++        *this, PointOfInstantiation, Decl->getDescribedFunctionTemplate(),
++        TemplateArgs, Satisfaction);
+ 
+   // Note - code synthesis context for the constraints check is created
+   // inside CheckConstraintsSatisfaction.
+@@ -1691,7 +1756,8 @@
+   LocalInstantiationScope Scope(*this);
+ 
+   std::optional MLTAL =
+-      SetupConstraintCheckingTemplateArgumentsAndScope(Decl, Scope);
++      SetupConstraintCheckingTemplateArgumentsAndScope(Decl, TemplateArgs,
++                                                       Scope);
+ 
+   if (!MLTAL)
+     return true;
+@@ -2275,8 +2341,12 @@
+       InnerArgs = std::move(CTAI.SugaredConverted);
+     }
+ 
+-    MultiLevelTemplateArgumentList MLTAL =
+-        SemaRef.getTemplateInstantiationArgs(Concept, InnerArgs);
++    MultiLevelTemplateArgumentList MLTAL = SemaRef.getTemplateInstantiationArgs(
++        Concept, Concept->getLexicalDeclContext(),
++        /*Final=*/true, InnerArgs,
++        /*RelativeToPrimary=*/true,
++        /*Pattern=*/nullptr,
++        /*ForConstraintInstantiation=*/true);
+ 
+     return SubstituteParameterMappings(SemaRef, &MLTAL,
+                                        CSE->getTemplateArgsAsWritten(),
+@@ -2357,7 +2427,7 @@
+     // [...]
+     NormalizedConstraint *SubNF;
+     if (ExprResult Res =
+-            SubstituteConceptsInConstraintExpression(S, CSE, SubstIndex);
++            SubstituteConceptsInConstraintExpression(S, D, CSE, SubstIndex);
+         Res.isUsable())
+       // Use canonical declarations to merge ConceptDecls across different
+       // modules.
+@@ -2507,8 +2577,8 @@
+     return false;
+   }
+ 
+-  unsigned Depth1 = CalculateTemplateDepthForConstraints(*this, D1);
+-  unsigned Depth2 = CalculateTemplateDepthForConstraints(*this, D2);
++  unsigned Depth1 = CalculateTemplateDepthForConstraints(*this, D1, true);
++  unsigned Depth2 = CalculateTemplateDepthForConstraints(*this, D2, true);
+ 
+   for (size_t I = 0; I != AC1.size() && I != AC2.size(); ++I) {
+     if (Depth2 > Depth1) {
+diff -ruN --strip-trailing-cr a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
+--- a/clang/lib/Sema/SemaDecl.cpp
++++ b/clang/lib/Sema/SemaDecl.cpp
+@@ -4750,10 +4750,10 @@
+   adjustDeclContextForDeclaratorDecl(New, Old);
+ 
+   // Ensure the template parameters are compatible.
+-  if (NewTemplate && !TemplateParameterListsAreEqual(
+-                         NewTemplate, NewTemplate->getTemplateParameters(),
+-                         OldTemplate, OldTemplate->getTemplateParameters(),
+-                         /*Complain=*/true, TPL_TemplateMatch))
++  if (NewTemplate &&
++      !TemplateParameterListsAreEqual(NewTemplate->getTemplateParameters(),
++                                      OldTemplate->getTemplateParameters(),
++                                      /*Complain=*/true, TPL_TemplateMatch))
+     return New->setInvalidDecl();
+ 
+   // C++ [class.mem]p1:
+@@ -6414,7 +6414,20 @@
+       return false;
+ 
+     // Cannot qualify members within a class.
+-    Diag(Loc, diag::err_member_qualification) << Name << SS.getRange();
++    Diag(Loc, diag::err_member_qualification)
++      << Name << SS.getRange();
++    SS.clear();
++
++    // C++ constructors and destructors with incorrect scopes can break
++    // our AST invariants by having the wrong underlying types. If
++    // that's the case, then drop this declaration entirely.
++    if ((Name.getNameKind() == DeclarationName::CXXConstructorName ||
++         Name.getNameKind() == DeclarationName::CXXDestructorName) &&
++        !Context.hasSameType(
++            Name.getCXXNameType(),
++            Context.getCanonicalTagType(cast(Cur))))
++      return true;
++
+     return false;
+   }
+ 
+@@ -8016,8 +8029,6 @@
+     }
+ 
+     if (IsVariableTemplateSpecialization) {
+-      // FIXME: This should be the template keyword location for the last
+-      // template parameter list.
+       SourceLocation TemplateKWLoc =
+           TemplateParamLists.size() > 0
+               ? TemplateParamLists[0]->getTemplateLoc()
+@@ -8060,8 +8071,10 @@
+ 
+     // If we have any template parameter lists that don't directly belong to
+     // the variable (matching the scope specifier), store them.
++    // An explicit variable template specialization does not own any template
++    // parameter lists.
+     unsigned VDTemplateParamLists =
+-        IsVariableTemplate || IsVariableTemplateSpecialization ? 1 : 0;
++        (TemplateParams && !IsExplicitSpecialization) ? 1 : 0;
+     if (TemplateParamLists.size() > VDTemplateParamLists)
+       NewVD->setTemplateParameterListsInfo(
+           Context, TemplateParamLists.drop_back(VDTemplateParamLists));
+@@ -10112,7 +10125,6 @@
+   bool isMemberSpecialization = false;
+   bool isFunctionTemplateSpecialization = false;
+ 
+-  TemplateParameterList *TemplateParams = nullptr;
+   bool HasExplicitTemplateArgs = false;
+   TemplateArgumentListInfo TemplateArgs;
+ 
+@@ -10197,14 +10209,11 @@
+         D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId
+             ? D.getName().TemplateId
+             : nullptr;
+-    TemplateParamListsRef = TemplateParamLists;
+-    TemplateParams = MatchTemplateParametersToScopeSpecifier(
+-        D.getDeclSpec().getBeginLoc(), D.getIdentifierLoc(),
+-        D.getCXXScopeSpec(), TemplateId, TemplateParamListsRef, isFriend,
+-        isMemberSpecialization, Invalid);
+-    // Some template parameter lists may have been dropped because they were
+-    // extraneous.
+-    TemplateParamLists.resize(TemplateParamListsRef.size());
++    TemplateParameterList *TemplateParams =
++        MatchTemplateParametersToScopeSpecifier(
++            D.getDeclSpec().getBeginLoc(), D.getIdentifierLoc(),
++            D.getCXXScopeSpec(), TemplateId, TemplateParamLists, isFriend,
++            isMemberSpecialization, Invalid);
+     if (TemplateParams) {
+       // Check that we can declare a template here.
+       if (CheckTemplateDeclScope(S, TemplateParams))
+@@ -10239,10 +10248,19 @@
+                                                         NewFD);
+         FunctionTemplate->setLexicalDeclContext(CurContext);
+         NewFD->setDescribedFunctionTemplate(FunctionTemplate);
++
++        // For source fidelity, store the other template param lists.
++        if (TemplateParamLists.size() > 1) {
++          NewFD->setTemplateParameterListsInfo(Context,
++              ArrayRef(TemplateParamLists)
++                  .drop_back(1));
++        }
+       } else {
+         // This is a function template specialization.
+         isFunctionTemplateSpecialization = true;
+-        NewFD->setInnerLocStart(TemplateParams->getTemplateLoc());
++        // For source fidelity, store all the template param lists.
++        if (TemplateParamLists.size() > 0)
++          NewFD->setTemplateParameterListsInfo(Context, TemplateParamLists);
+ 
+         // C++0x [temp.expl.spec]p20 forbids "template<> friend void foo(int);".
+         if (isFriend) {
+@@ -10272,12 +10290,6 @@
+           TemplateArgs.setRAngleLoc(InsertLoc);
+         }
+       }
+-      // For source fidelity, store the other template param lists.
+-      if (TemplateParamLists.size() > 1) {
+-        NewFD->setTemplateParameterListsInfo(
+-            Context,
+-            ArrayRef(TemplateParamLists).drop_back(1));
+-      }
+     } else {
+       // Check that we can declare a template here.
+       if (!TemplateParamLists.empty() && isMemberSpecialization &&
+@@ -10879,11 +10891,11 @@
+         // template is instantiated. In such cases, we store the declarations
+         // found by name lookup and defer resolution until instantiation.
+         if (CheckDependentFunctionTemplateSpecialization(
+-                NewFD, TemplateParams, ExplicitTemplateArgs, Previous))
++                NewFD, ExplicitTemplateArgs, Previous))
+           NewFD->setInvalidDecl();
+       } else if (!NewFD->isInvalidDecl()) {
+-        if (CheckFunctionTemplateSpecialization(NewFD, TemplateParams,
+-                                                ExplicitTemplateArgs, Previous))
++        if (CheckFunctionTemplateSpecialization(NewFD, ExplicitTemplateArgs,
++                                                Previous))
+           NewFD->setInvalidDecl();
+       }
+     } else if (isMemberSpecialization && !FunctionTemplate) {
+@@ -11187,7 +11199,7 @@
+     // Precalculate whether this is a friend function template with a constraint
+     // that depends on an enclosing template, per [temp.friend]p9.
+     if (isFriend && FunctionTemplate &&
+-        FriendConstraintsDependOnEnclosingTemplate(FunctionTemplate)) {
++        FriendConstraintsDependOnEnclosingTemplate(NewFD)) {
+       NewFD->setFriendConstraintRefersToEnclosingTemplate(true);
+ 
+       // C++ [temp.friend]p9:
+diff -ruN --strip-trailing-cr a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
+--- a/clang/lib/Sema/SemaDeclCXX.cpp
++++ b/clang/lib/Sema/SemaDeclCXX.cpp
+@@ -13938,13 +13938,10 @@
+       }
+ 
+       if (!Invalid && OldDecl && !OldDecl->isInvalidDecl()) {
+-        // It's ok that we don't pass the declarations corresponding to the
+-        // template parameter lists here, because type alias templates cannot be
+-        // declared out-of-line.
+-        if (TemplateParameterListsAreEqual(
+-                /*NewInstFrom=*/nullptr, TemplateParams,
+-                /*OldInstFrom=*/nullptr, OldDecl->getTemplateParameters(),
+-                /*Complain=*/true, TPL_TemplateMatch))
++        if (TemplateParameterListsAreEqual(TemplateParams,
++                                           OldDecl->getTemplateParameters(),
++                                           /*Complain=*/true,
++                                           TPL_TemplateMatch))
+           OldTemplateParams =
+               OldDecl->getMostRecentDecl()->getTemplateParameters();
+         else
+diff -ruN --strip-trailing-cr a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
+--- a/clang/lib/Sema/SemaExprMember.cpp
++++ b/clang/lib/Sema/SemaExprMember.cpp
+@@ -1129,8 +1129,9 @@
+       return ExprError();
+     }
+ 
+-    DeclResult VDecl = CheckVarTemplateId(
+-        VarTempl, TemplateKWLoc, MemberNameInfo.getLoc(), *TemplateArgs);
++    DeclResult VDecl =
++        CheckVarTemplateId(VarTempl, TemplateKWLoc, MemberNameInfo.getLoc(),
++                           *TemplateArgs, /*SetWrittenArgs=*/false);
+     if (VDecl.isInvalid())
+       return ExprError();
+ 
+diff -ruN --strip-trailing-cr a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
+--- a/clang/lib/Sema/SemaOverload.cpp
++++ b/clang/lib/Sema/SemaOverload.cpp
+@@ -1325,10 +1325,8 @@
+       !New->getType()->isDependentType()) {
+     LookupResult TemplateSpecResult(LookupResult::Temporary, Old);
+     TemplateSpecResult.addAllDecls(Old);
+-    if (CheckFunctionTemplateSpecialization(New, /*TemplateParams=*/nullptr,
+-                                            /*ExplicitTemplateArgs=*/nullptr,
+-                                            TemplateSpecResult,
+-                                            /*QualifiedFriend*/ true)) {
++    if (CheckFunctionTemplateSpecialization(New, nullptr, TemplateSpecResult,
++                                            /*QualifiedFriend*/true)) {
+       New->setInvalidDecl();
+       return OverloadKind::Overload;
+     }
+diff -ruN --strip-trailing-cr a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
+--- a/clang/lib/Sema/SemaTemplate.cpp
++++ b/clang/lib/Sema/SemaTemplate.cpp
+@@ -2113,71 +2113,19 @@
+     }
+   }
+ 
+-  if (!PrevClassTemplate && PrevDecl) {
+-    // C++ [temp]p5:
+-    //   A class template shall not have the same name as any other
+-    //   template, class, function, object, enumeration, enumerator,
+-    //   namespace, or type in the same scope (3.3), except as specified
+-    //   in (14.5.4).
+-    Diag(NameLoc, diag::err_redefinition_different_kind) << Name;
+-    Diag(PrevDecl->getLocation(), diag::note_previous_definition);
+-    return true;
+-  }
+-
+-  // If this is a templated friend in a dependent context we should not put it
+-  // on the redecl chain. In some cases, the templated friend can be the most
+-  // recent declaration tricking the template instantiator to make substitutions
+-  // there.
+-  // FIXME: Figure out how to combine with shouldLinkDependentDeclWithPrevious
+-  bool ShouldAddRedecl =
+-      !(TUK == TagUseKind::Friend && CurContext->isDependentContext());
+-
+-  CXXRecordDecl *NewClass = CXXRecordDecl::Create(
+-      Context, Kind, SemanticContext, KWLoc, NameLoc, Name,
+-      /*PrevDecl=*/PrevClassTemplate && ShouldAddRedecl
+-          ? PrevClassTemplate->getTemplatedDecl()
+-          : nullptr);
+-  SetNestedNameSpecifier(*this, NewClass, SS);
+-  if (NumOuterTemplateParamLists > 0)
+-    NewClass->setTemplateParameterListsInfo(
+-        Context,
+-        llvm::ArrayRef(OuterTemplateParamLists, NumOuterTemplateParamLists));
+-
+-  // Add alignment attributes if necessary; these attributes are checked when
+-  // the ASTContext lays out the structure.
+-  if (TUK == TagUseKind::Definition && (!SkipBody || !SkipBody->ShouldSkip)) {
+-    if (LangOpts.HLSL)
+-      NewClass->addAttr(PackedAttr::CreateImplicit(Context));
+-    AddAlignmentAttributesForRecord(NewClass);
+-    AddMsStructLayoutForRecord(NewClass);
+-  }
+-
+-  ClassTemplateDecl *NewTemplate = ClassTemplateDecl::Create(
+-      Context, SemanticContext, NameLoc, DeclarationName(Name), TemplateParams,
+-      NewClass);
+-
+-  NewClass->setDescribedClassTemplate(NewTemplate);
+-
+-  if (ModulePrivateLoc.isValid())
+-    NewTemplate->setModulePrivate();
+-
+-  // Set the lexical context of these templates
+-  NewClass->setLexicalDeclContext(CurContext);
+-  NewTemplate->setLexicalDeclContext(CurContext);
+-
+   if (PrevClassTemplate) {
+     // Ensure that the template parameter lists are compatible. Skip this check
+     // for a friend in a dependent context: the template parameter list itself
+     // could be dependent.
+     if (!(TUK == TagUseKind::Friend && CurContext->isDependentContext()) &&
+         !TemplateParameterListsAreEqual(
+-            NewTemplate, TemplateParams, PrevClassTemplate,
++            TemplateCompareNewDeclInfo(SemanticContext ? SemanticContext
++                                                       : CurContext,
++                                       CurContext, KWLoc),
++            TemplateParams, PrevClassTemplate,
+             PrevClassTemplate->getTemplateParameters(), /*Complain=*/true,
+-            TPL_TemplateMatch)) {
+-      NewTemplate->setInvalidDecl();
+-      NewClass->setInvalidDecl();
++            TPL_TemplateMatch))
+       return true;
+-    }
+ 
+     // C++ [temp.class]p4:
+     //   In a redeclaration, partial specialization, explicit
+@@ -2222,6 +2170,15 @@
+         }
+       }
+     }
++  } else if (PrevDecl) {
++    // C++ [temp]p5:
++    //   A class template shall not have the same name as any other
++    //   template, class, function, object, enumeration, enumerator,
++    //   namespace, or type in the same scope (3.3), except as specified
++    //   in (14.5.4).
++    Diag(NameLoc, diag::err_redefinition_different_kind) << Name;
++    Diag(PrevDecl->getLocation(), diag::note_previous_definition);
++    return true;
+   }
+ 
+   // Check the template parameter list of this declaration, possibly
+@@ -2252,20 +2209,62 @@
+              << SS.getRange();
+   }
+ 
+-  // Set the access specifier.
+-  if (!Invalid && TUK != TagUseKind::Friend &&
+-      NewTemplate->getDeclContext()->isRecord())
+-    SetMemberAccessSpecifier(NewTemplate, PrevClassTemplate, AS);
++  // If this is a templated friend in a dependent context we should not put it
++  // on the redecl chain. In some cases, the templated friend can be the most
++  // recent declaration tricking the template instantiator to make substitutions
++  // there.
++  // FIXME: Figure out how to combine with shouldLinkDependentDeclWithPrevious
++  bool ShouldAddRedecl =
++      !(TUK == TagUseKind::Friend && CurContext->isDependentContext());
+ 
+-  if (ShouldAddRedecl && PrevClassTemplate)
++  CXXRecordDecl *NewClass = CXXRecordDecl::Create(
++      Context, Kind, SemanticContext, KWLoc, NameLoc, Name,
++      PrevClassTemplate && ShouldAddRedecl
++          ? PrevClassTemplate->getTemplatedDecl()
++          : nullptr);
++  SetNestedNameSpecifier(*this, NewClass, SS);
++  if (NumOuterTemplateParamLists > 0)
++    NewClass->setTemplateParameterListsInfo(
++        Context,
++        llvm::ArrayRef(OuterTemplateParamLists, NumOuterTemplateParamLists));
++
++  // Add alignment attributes if necessary; these attributes are checked when
++  // the ASTContext lays out the structure.
++  if (TUK == TagUseKind::Definition && (!SkipBody || !SkipBody->ShouldSkip)) {
++    if (LangOpts.HLSL)
++      NewClass->addAttr(PackedAttr::CreateImplicit(Context));
++    AddAlignmentAttributesForRecord(NewClass);
++    AddMsStructLayoutForRecord(NewClass);
++  }
++
++  ClassTemplateDecl *NewTemplate
++    = ClassTemplateDecl::Create(Context, SemanticContext, NameLoc,
++                                DeclarationName(Name), TemplateParams,
++                                NewClass);
++
++  if (ShouldAddRedecl)
+     NewTemplate->setPreviousDecl(PrevClassTemplate);
+ 
++  NewClass->setDescribedClassTemplate(NewTemplate);
++
++  if (ModulePrivateLoc.isValid())
++    NewTemplate->setModulePrivate();
++
+   if (IsMemberSpecialization) {
+     assert(PrevClassTemplate &&
+            "Member specialization without a primary template?");
+     NewTemplate->setMemberSpecialization();
+   }
+ 
++  // Set the access specifier.
++  if (!Invalid && TUK != TagUseKind::Friend &&
++      NewTemplate->getDeclContext()->isRecord())
++    SetMemberAccessSpecifier(NewTemplate, PrevClassTemplate, AS);
++
++  // Set the lexical context of these templates
++  NewClass->setLexicalDeclContext(CurContext);
++  NewTemplate->setLexicalDeclContext(CurContext);
++
+   if (TUK == TagUseKind::Definition && (!SkipBody || !SkipBody->ShouldSkip))
+     NewClass->startDefinition();
+ 
+@@ -2838,7 +2837,7 @@
+ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier(
+     SourceLocation DeclStartLoc, SourceLocation DeclLoc, const CXXScopeSpec &SS,
+     TemplateIdAnnotation *TemplateId,
+-    ArrayRef &ParamLists, bool IsFriend,
++    ArrayRef ParamLists, bool IsFriend,
+     bool &IsMemberSpecialization, bool &Invalid, bool SuppressDiagnostic) {
+   IsMemberSpecialization = false;
+   Invalid = false;
+@@ -3104,12 +3103,10 @@
+ 
+       if (ParamIdx < ParamLists.size()) {
+         // Check the template parameter list, if we can.
+-        // FIXME: Should pass the declaration corresponding to each list.
+         if (ExpectedTemplateParams &&
+-            !TemplateParameterListsAreEqual(
+-                /*NewInstFrom=*/nullptr, ParamLists[ParamIdx],
+-                /*OldInstFrom=*/nullptr, ExpectedTemplateParams,
+-                /*Complain=*/!SuppressDiagnostic, TPL_TemplateMatch))
++            !TemplateParameterListsAreEqual(ParamLists[ParamIdx],
++                                            ExpectedTemplateParams,
++                                            !SuppressDiagnostic, TPL_TemplateMatch))
+           Invalid = true;
+ 
+         if (!Invalid &&
+@@ -3176,9 +3173,6 @@
+            diag::note_explicit_template_spec_does_not_need_header)
+         << NestedTypes.back();
+ 
+-    // Recover as if those extra template parameter lists weren't there.
+-    ParamLists = ParamLists.take_back(ParamIdx + 1);
+-
+     // We have a template parameter list with no corresponding scope, which
+     // means that the resulting template declaration can't be instantiated
+     // properly (we'll end up with dependent nodes when we shouldn't).
+@@ -4486,9 +4480,9 @@
+     VarTemplatePartialSpecializationDecl *Partial =
+         VarTemplatePartialSpecializationDecl::Create(
+             Context, VarTemplate->getDeclContext(), TemplateKWLoc,
+-            TemplateNameLoc, TemplateParams,
+-            ASTTemplateArgumentListInfo::Create(Context, TemplateArgs),
+-            VarTemplate, TSI->getType(), TSI, SC, CTAI.CanonicalConverted);
++            TemplateNameLoc, TemplateParams, VarTemplate, TSI->getType(), TSI,
++            SC, CTAI.CanonicalConverted);
++    Partial->setTemplateArgsAsWritten(TemplateArgs);
+ 
+     if (!PrevPartial)
+       VarTemplate->AddPartialSpecialization(Partial, InsertPos);
+@@ -4501,9 +4495,7 @@
+     Specialization = VarTemplateSpecializationDecl::Create(
+         Context, VarTemplate->getDeclContext(), TemplateKWLoc, TemplateNameLoc,
+         VarTemplate, TSI->getType(), TSI, SC, CTAI.CanonicalConverted);
+-    Specialization->setExplicitSpecializationInfo(
+-        TemplateParams,
+-        ASTTemplateArgumentListInfo::Create(Context, TemplateArgs));
++    Specialization->setTemplateArgsAsWritten(TemplateArgs);
+ 
+     if (!PrevDecl)
+       VarTemplate->AddSpecialization(Specialization, InsertPos);
+@@ -4589,7 +4581,8 @@
+ DeclResult
+ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc,
+                          SourceLocation TemplateNameLoc,
+-                         const TemplateArgumentListInfo &TemplateArgs) {
++                         const TemplateArgumentListInfo &TemplateArgs,
++                         bool SetWrittenArgs) {
+   assert(Template && "A variable template id without template?");
+ 
+   // Check that the template argument list is well-formed for this template.
+@@ -4788,6 +4781,8 @@
+       TemplateNameLoc /*, LateAttrs, StartingScope*/);
+   if (!Decl)
+     return true;
++  if (SetWrittenArgs)
++    Decl->setTemplateArgsAsWritten(TemplateArgs);
+ 
+   if (VarTemplatePartialSpecializationDecl *D =
+           dyn_cast(InstantiationPattern))
+@@ -4805,7 +4800,7 @@
+     const TemplateArgumentListInfo *TemplateArgs) {
+ 
+   DeclResult Decl = CheckVarTemplateId(Template, TemplateLoc, NameInfo.getLoc(),
+-                                       *TemplateArgs);
++                                       *TemplateArgs, /*SetWrittenArgs=*/false);
+   if (Decl.isInvalid())
+     return ExprError();
+ 
+@@ -6197,8 +6192,7 @@
+   if (UpdateArgsWithConversions)
+     TemplateArgs = std::move(NewArgs);
+ 
+-  // Template template parameters can never have their constraints checked.
+-  if (!PartialTemplateArgs && !isa(Template)) {
++  if (!PartialTemplateArgs) {
+     // Setup the context/ThisScope for the case where we are needing to
+     // re-instantiate constraints outside of normal instantiation.
+     DeclContext *NewContext = Template->getDeclContext();
+@@ -6217,8 +6211,11 @@
+     ContextRAII Context(*this, NewContext);
+     CXXThisScopeRAII Scope(*this, RD, ThisQuals, RD != nullptr);
+ 
+-    MultiLevelTemplateArgumentList MLTAL =
+-        getTemplateInstantiationArgs(Template, CTAI.SugaredConverted);
++    MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs(
++        Template, NewContext, /*Final=*/true, CTAI.SugaredConverted,
++        /*RelativeToPrimary=*/true,
++        /*Pattern=*/nullptr,
++        /*ForConceptInstantiation=*/true);
+     if (!isa(Template) &&
+         EnsureTemplateArgumentListConstraints(
+             Template, MLTAL,
+@@ -8209,8 +8206,9 @@
+ 
+ /// Match two template parameters within template parameter lists.
+ static bool MatchTemplateParameterKind(
+-    Sema &S, NamedDecl *New, const Decl *NewInstFrom, NamedDecl *Old,
+-    const Decl *OldInstFrom, bool Complain,
++    Sema &S, NamedDecl *New,
++    const Sema::TemplateCompareNewDeclInfo &NewInstFrom, NamedDecl *Old,
++    const NamedDecl *OldInstFrom, bool Complain,
+     Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) {
+   // Check the actual kind (type, non-type, template).
+   if (Old->getKind() != New->getKind()) {
+@@ -8298,7 +8296,7 @@
+     if (OldTTP->templateParameterKind() != NewTTP->templateParameterKind())
+       return false;
+     if (!S.TemplateParameterListsAreEqual(
+-            NewTTP, NewTTP->getTemplateParameters(), OldTTP,
++            NewInstFrom, NewTTP->getTemplateParameters(), OldInstFrom,
+             OldTTP->getTemplateParameters(), Complain,
+             (Kind == Sema::TPL_TemplateMatch
+                  ? Sema::TPL_TemplateTemplateParmMatch
+@@ -8376,8 +8374,8 @@
+ }
+ 
+ bool Sema::TemplateParameterListsAreEqual(
+-    const Decl *NewInstFrom, TemplateParameterList *New,
+-    const Decl *OldInstFrom, TemplateParameterList *Old, bool Complain,
++    const TemplateCompareNewDeclInfo &NewInstFrom, TemplateParameterList *New,
++    const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain,
+     TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) {
+   if (Old->size() != New->size()) {
+     if (Complain)
+@@ -8759,7 +8757,7 @@
+     Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
+     SourceLocation ModulePrivateLoc, CXXScopeSpec &SS,
+     TemplateIdAnnotation &TemplateId, const ParsedAttributesView &Attr,
+-    MultiTemplateParamsArg &TemplateParameterLists, SkipBodyInfo *SkipBody) {
++    MultiTemplateParamsArg TemplateParameterLists, SkipBodyInfo *SkipBody) {
+   assert(TUK != TagUseKind::Reference && "References are not specializations");
+ 
+   SourceLocation TemplateNameLoc = TemplateId.TemplateNameLoc;
+@@ -8957,13 +8955,15 @@
+     Specialization = ClassTemplateSpecializationDecl::Create(
+         Context, Kind, ClassTemplate->getDeclContext(), KWLoc, TemplateNameLoc,
+         ClassTemplate, CTAI.CanonicalConverted, CTAI.StrictPackMatch, PrevDecl);
++    Specialization->setTemplateArgsAsWritten(TemplateArgs);
++    SetNestedNameSpecifier(*this, Specialization, SS);
++    if (TemplateParameterLists.size() > 0) {
++      Specialization->setTemplateParameterListsInfo(Context,
++                                                    TemplateParameterLists);
++    }
+ 
+     if (!PrevDecl)
+       ClassTemplate->AddSpecialization(Specialization, InsertPos);
+-
+-    Specialization->setExplicitSpecializationInfo(
+-        TemplateParams,
+-        ASTTemplateArgumentListInfo::Create(Context, TemplateArgs));
+   } else {
+     CanQualType CanonType = CanQualType::CreateUnsafe(
+         Context.getCanonicalTemplateSpecializationType(
+@@ -8999,8 +8999,13 @@
+     ClassTemplatePartialSpecializationDecl *Partial =
+         ClassTemplatePartialSpecializationDecl::Create(
+             Context, Kind, DC, KWLoc, TemplateNameLoc, TemplateParams,
+-            ASTTemplateArgumentListInfo::Create(Context, TemplateArgs),
+             ClassTemplate, CTAI.CanonicalConverted, CanonType, PrevPartial);
++    Partial->setTemplateArgsAsWritten(TemplateArgs);
++    SetNestedNameSpecifier(*this, Partial, SS);
++    if (TemplateParameterLists.size() > 1 && SS.isSet()) {
++      Partial->setTemplateParameterListsInfo(
++          Context, TemplateParameterLists.drop_back(1));
++    }
+ 
+     if (!PrevPartial)
+       ClassTemplate->AddPartialSpecialization(Partial, InsertPos);
+@@ -9010,18 +9015,10 @@
+     // template specialization, make a note of that.
+     if (isMemberSpecialization)
+       Partial->setMemberSpecialization();
+-  }
+ 
+-  SetNestedNameSpecifier(*this, Specialization, SS);
+-  if (TemplateParameterLists.size() > 1 && SS.isSet()) {
+-    Specialization->setTemplateParameterListsInfo(
+-        Context, TemplateParameterLists.drop_back(1));
++    CheckTemplatePartialSpecialization(Partial);
+   }
+ 
+-  if (isPartialSpecialization)
+-    CheckTemplatePartialSpecialization(
+-        cast(Specialization));
+-
+   // C++ [temp.expl.spec]p6:
+   //   If a template, a member template or the member of a class template is
+   //   explicitly specialized then that specialization shall be declared
+@@ -9381,7 +9378,6 @@
+   switch (NewTSK) {
+   case TSK_Undeclared:
+   case TSK_ImplicitInstantiation:
+-  case TSK_FriendDeclaration:
+     assert(
+         (PrevTSK == TSK_Undeclared || PrevTSK == TSK_ImplicitInstantiation) &&
+         "previous declaration must be implicit!");
+@@ -9396,7 +9392,6 @@
+       // instantiation.
+       return false;
+ 
+-    case TSK_FriendDeclaration:
+     case TSK_ImplicitInstantiation:
+       if (PrevPointOfInstantiation.isInvalid()) {
+         // The declaration itself has not actually been instantiated, so it is
+@@ -9443,7 +9438,6 @@
+       return false;
+ 
+     case TSK_Undeclared:
+-    case TSK_FriendDeclaration:
+     case TSK_ImplicitInstantiation:
+       // We're explicitly instantiating something that may have already been
+       // implicitly instantiated; that's fine.
+@@ -9479,7 +9473,6 @@
+   case TSK_ExplicitInstantiationDefinition:
+     switch (PrevTSK) {
+     case TSK_Undeclared:
+-    case TSK_FriendDeclaration:
+     case TSK_ImplicitInstantiation:
+       // We're explicitly instantiating something that may have already been
+       // implicitly instantiated; that's fine.
+@@ -9539,8 +9532,7 @@
+ }
+ 
+ bool Sema::CheckDependentFunctionTemplateSpecialization(
+-    FunctionDecl *FD, const TemplateParameterList *TemplateParams,
+-    const TemplateArgumentListInfo *ExplicitTemplateArgs,
++    FunctionDecl *FD, const TemplateArgumentListInfo *ExplicitTemplateArgs,
+     LookupResult &Previous) {
+   // Remove anything from Previous that isn't a function template in
+   // the correct context.
+@@ -9577,14 +9569,13 @@
+   }
+ 
+   FD->setDependentTemplateSpecialization(Context, Previous.asUnresolvedSet(),
+-                                         TemplateParams, ExplicitTemplateArgs);
++                                         ExplicitTemplateArgs);
+   return false;
+ }
+ 
+ bool Sema::CheckFunctionTemplateSpecialization(
+-    FunctionDecl *FD, const TemplateParameterList *TemplateParams,
+-    TemplateArgumentListInfo *ExplicitTemplateArgs, LookupResult &Previous,
+-    bool QualifiedFriend) {
++    FunctionDecl *FD, TemplateArgumentListInfo *ExplicitTemplateArgs,
++    LookupResult &Previous, bool QualifiedFriend) {
+   // The set of function template specializations that could match this
+   // explicit function template specialization.
+   UnresolvedSet<8> Candidates;
+@@ -9834,11 +9825,9 @@
+   TemplateArgumentList *TemplArgs = TemplateArgumentList::CreateCopy(
+       Context, Specialization->getTemplateSpecializationArgs()->asArray());
+   FD->setFunctionTemplateSpecialization(
+-      Context, Specialization->getPrimaryTemplate(), TemplArgs,
+-      /*InsertPos=*/nullptr, SpecInfo->getTemplateSpecializationKind(),
+-      TemplateParams,
+-      ExplicitTemplateArgs ? &ConvertedTemplateArgs[Specialization] : nullptr,
+-      /*PointOfInstantiation=*/SourceLocation(), /*AddSpecialization=*/true);
++      Specialization->getPrimaryTemplate(), TemplArgs, /*InsertPos=*/nullptr,
++      SpecInfo->getTemplateSpecializationKind(),
++      ExplicitTemplateArgs ? &ConvertedTemplateArgs[Specialization] : nullptr);
+ 
+   // A function template specialization inherits the target attributes
+   // of its template.  (We require the attributes explicitly in the
+@@ -9988,13 +9977,14 @@
+   // FIXME: Is this really valid? Other compilers reject.
+   if (Member->getFriendObjectKind() != Decl::FOK_None) {
+     // Preserve instantiation information.
+-    if (InstantiatedFrom) {
+-      if (auto *MD = dyn_cast(Member))
+-        MD->setInstantiationOfMemberFunction(
+-            cast(InstantiatedFrom), TSK_FriendDeclaration);
+-      else if (auto *RD = dyn_cast(Member))
+-        RD->setInstantiationOfMemberClass(cast(InstantiatedFrom),
+-                                          TSK_FriendDeclaration);
++    if (InstantiatedFrom && isa(Member)) {
++      cast(Member)->setInstantiationOfMemberFunction(
++                                      cast(InstantiatedFrom),
++        cast(Instantiation)->getTemplateSpecializationKind());
++    } else if (InstantiatedFrom && isa(Member)) {
++      cast(Member)->setInstantiationOfMemberClass(
++                                      cast(InstantiatedFrom),
++        cast(Instantiation)->getTemplateSpecializationKind());
+     }
+ 
+     Previous.clear();
+@@ -10081,8 +10071,7 @@
+ template
+ static void completeMemberSpecializationImpl(Sema &S, DeclT *OrigD,
+                                              SourceLocation Loc) {
+-  if (auto Kind = OrigD->getTemplateSpecializationKind();
+-      Kind != TSK_ImplicitInstantiation && Kind != TSK_FriendDeclaration)
++  if (OrigD->getTemplateSpecializationKind() != TSK_ImplicitInstantiation)
+     return;
+ 
+   // FIXME: Inform AST mutation listeners of this AST mutation.
+@@ -10412,6 +10401,7 @@
+     Specialization = ClassTemplateSpecializationDecl::Create(
+         Context, Kind, ClassTemplate->getDeclContext(), KWLoc, TemplateNameLoc,
+         ClassTemplate, CTAI.CanonicalConverted, CTAI.StrictPackMatch, PrevDecl);
++    SetNestedNameSpecifier(*this, Specialization, SS);
+ 
+     // A MSInheritanceAttr attached to the previous declaration must be
+     // propagated to the new node prior to instantiation.
+@@ -10430,19 +10420,22 @@
+     }
+   }
+ 
+-  Specialization->setExplicitInstantiationInfo(
+-      ExternLoc, TemplateLoc,
+-      ASTTemplateArgumentListInfo::Create(Context, TemplateArgs));
++  Specialization->setTemplateArgsAsWritten(TemplateArgs);
++
++  // Set source locations for keywords.
++  Specialization->setExternKeywordLoc(ExternLoc);
++  Specialization->setTemplateKeywordLoc(TemplateLoc);
+   Specialization->setBraceRange(SourceRange());
+ 
+   bool PreviouslyDLLExported = Specialization->hasAttr();
+   ProcessDeclAttributeList(S, Specialization, Attr);
+   ProcessAPINotes(Specialization);
+ 
+-  // Add the explicit instantiation into its lexical context.
+-  auto *LexicalDC = ClassTemplate->getLexicalDeclContext();
+-  Specialization->setLexicalDeclContext(LexicalDC);
+-  LexicalDC->addDecl(Specialization);
++  // Add the explicit instantiation into its lexical context. However,
++  // since explicit instantiations are never found by name lookup, we
++  // just put it into the declaration context directly.
++  Specialization->setLexicalDeclContext(CurContext);
++  CurContext->addDecl(Specialization);
+ 
+   // Syntax is now OK, so return if it has no other effect on semantics.
+   if (HasNoEffect) {
+@@ -10771,15 +10764,6 @@
+   LookupParsedName(Previous, S, &D.getCXXScopeSpec(),
+                    /*ObjectType=*/QualType());
+ 
+-  // If the declarator is a template-id, translate the parser's template
+-  // argument list into our AST format.
+-  bool HasExplicitTemplateArgs = false;
+-  TemplateArgumentListInfo TemplateArgs;
+-  if (D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId) {
+-    TemplateArgs = makeTemplateArgumentListInfo(*this, *D.getName().TemplateId);
+-    HasExplicitTemplateArgs = true;
+-  }
+-
+   if (!R->isFunctionType()) {
+     // C++ [temp.explicit]p1:
+     //   A [...] static data member of a class template can be explicitly
+@@ -10829,7 +10813,7 @@
+         return true;
+       }
+ 
+-      if (!HasExplicitTemplateArgs) {
++      if (D.getName().getKind() != UnqualifiedIdKind::IK_TemplateId) {
+         // C++1y [temp.explicit]p3:
+         //   If the explicit instantiation is for a variable, the unqualified-id
+         //   in the declaration shall be a template-id.
+@@ -10841,8 +10825,13 @@
+         return true;
+       }
+ 
+-      DeclResult Res = CheckVarTemplateId(PrevTemplate, TemplateLoc,
+-                                          D.getIdentifierLoc(), TemplateArgs);
++      // Translate the parser's template argument list into our AST format.
++      TemplateArgumentListInfo TemplateArgs =
++          makeTemplateArgumentListInfo(*this, *D.getName().TemplateId);
++
++      DeclResult Res =
++          CheckVarTemplateId(PrevTemplate, TemplateLoc, D.getIdentifierLoc(),
++                             TemplateArgs, /*SetWrittenArgs=*/true);
+       if (Res.isInvalid())
+         return true;
+ 
+@@ -10890,10 +10879,8 @@
+       // Instantiate static data member or variable template.
+       Prev->setTemplateSpecializationKind(TSK, D.getIdentifierLoc());
+       if (auto *VTSD = dyn_cast(Prev)) {
+-        assert(HasExplicitTemplateArgs);
+-        VTSD->setExplicitInstantiationInfo(
+-            ExternLoc, TemplateLoc,
+-            ASTTemplateArgumentListInfo::Create(Context, TemplateArgs));
++        VTSD->setExternKeywordLoc(ExternLoc);
++        VTSD->setTemplateKeywordLoc(TemplateLoc);
+       }
+ 
+       // Merge attributes.
+@@ -10922,6 +10909,15 @@
+     return (Decl *)nullptr;
+   }
+ 
++  // If the declarator is a template-id, translate the parser's template
++  // argument list into our AST format.
++  bool HasExplicitTemplateArgs = false;
++  TemplateArgumentListInfo TemplateArgs;
++  if (D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId) {
++    TemplateArgs = makeTemplateArgumentListInfo(*this, *D.getName().TemplateId);
++    HasExplicitTemplateArgs = true;
++  }
++
+   // C++ [temp.explicit]p1:
+   //   A [...] function [...] can be explicitly instantiated from its template.
+   //   A member function [...] of a class template can be explicitly
+diff -ruN --strip-trailing-cr a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
+--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
++++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
+@@ -3182,16 +3182,36 @@
+                                 ArrayRef CanonicalDeducedArgs,
+                                 TemplateDeductionInfo &Info) {
+   llvm::SmallVector AssociatedConstraints;
+-  if (auto *TD = dyn_cast(Template))
++  bool DeducedArgsNeedReplacement = false;
++  if (auto *TD = dyn_cast(Template)) {
+     TD->getAssociatedConstraints(AssociatedConstraints);
+-  else if (auto *TD = dyn_cast(Template))
++    DeducedArgsNeedReplacement = !TD->isClassScopeExplicitSpecialization();
++  } else if (auto *TD =
++                 dyn_cast(Template)) {
+     TD->getAssociatedConstraints(AssociatedConstraints);
+-  else
++    DeducedArgsNeedReplacement = !TD->isClassScopeExplicitSpecialization();
++  } else {
+     cast(Template)->getAssociatedConstraints(
+         AssociatedConstraints);
++  }
+ 
+-  MultiLevelTemplateArgumentList MLTAL =
+-      S.getTemplateInstantiationArgs(Template, SugaredDeducedArgs);
++  std::optional> Innermost;
++  // If we don't need to replace the deduced template arguments,
++  // we can add them immediately as the inner-most argument list.
++  if (!DeducedArgsNeedReplacement)
++    Innermost = SugaredDeducedArgs;
++
++  MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
++      Template, Template->getDeclContext(), /*Final=*/false, Innermost,
++      /*RelativeToPrimary=*/true, /*Pattern=*/
++      nullptr, /*ForConstraintInstantiation=*/true);
++
++  // getTemplateInstantiationArgs picks up the non-deduced version of the
++  // template args when this is a variable template partial specialization and
++  // not class-scope explicit specialization, so replace with Deduced Args
++  // instead of adding to inner-most.
++  if (!Innermost)
++    MLTAL.replaceInnermostTemplateArguments(Template, SugaredDeducedArgs);
+ 
+   if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL,
+                                     Info.getLocation(),
+@@ -3954,8 +3974,9 @@
+   bool IsLambda = isLambdaCallOperator(FD) || isLambdaConversionOperator(FD);
+   if (!IsLambda && !IsIncomplete) {
+     if (CheckFunctionTemplateConstraints(
+-            Info.getLocation(), FunctionTemplate, CTAI.CanonicalConverted,
+-            Info.AssociatedConstraintsSatisfaction))
++            Info.getLocation(),
++            FunctionTemplate->getCanonicalDecl()->getTemplatedDecl(),
++            CTAI.CanonicalConverted, Info.AssociatedConstraintsSatisfaction))
+       return TemplateDeductionResult::MiscellaneousDeductionFailure;
+     if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
+       Info.reset(Info.takeSugared(), TemplateArgumentList::CreateCopy(
+@@ -4001,8 +4022,8 @@
+   //   ([temp.constr.constr]). If the constraints are not satisfied, type
+   //   deduction fails.
+   if (IsLambda && !IsIncomplete) {
+-    if (CheckFunctionSpecializationConstraints(
+-            Info.getLocation(), Specialization,
++    if (CheckFunctionTemplateConstraints(
++            Info.getLocation(), Specialization, CTAI.CanonicalConverted,
+             Info.AssociatedConstraintsSatisfaction))
+       return TemplateDeductionResult::MiscellaneousDeductionFailure;
+ 
+@@ -6028,7 +6049,7 @@
+   //   function parameters that positionally correspond between the two
+   //   templates are not of the same type, neither template is more specialized
+   //   than the other.
+-  if (!TemplateParameterListsAreEqual(FT1, TPL1, FT2, TPL2, false,
++  if (!TemplateParameterListsAreEqual(TPL1, TPL2, false,
+                                       Sema::TPL_TemplateParamsEquivalent))
+     return nullptr;
+ 
+@@ -6383,7 +6404,7 @@
+   // function parameters that positionally correspond between the two
+   // templates are not of the same type, neither template is more specialized
+   // than the other.
+-  if (!S.TemplateParameterListsAreEqual(P1, TPL1, P2, TPL2, false,
++  if (!S.TemplateParameterListsAreEqual(TPL1, TPL2, false,
+                                         Sema::TPL_TemplateParamsEquivalent))
+     return nullptr;
+ 
+diff -ruN --strip-trailing-cr a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
++++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+@@ -65,9 +65,10 @@
+     return true;
+ 
+   // General case: pairwise compare each associated constraint expression.
++  Sema::TemplateCompareNewDeclInfo NewInfo(New);
+   for (size_t I = 0, E = OldACs.size(); I != E; ++I)
+-    if (!SemaRef.AreConstraintExpressionsEqual(Old, OldACs[I].ConstraintExpr,
+-                                               New, NewACs[I].ConstraintExpr))
++    if (!SemaRef.AreConstraintExpressionsEqual(
++            Old, OldACs[I].ConstraintExpr, NewInfo, NewACs[I].ConstraintExpr))
+       return false;
+ 
+   return true;
+@@ -502,7 +503,6 @@
+       : SemaRef(S), Template(Template) {
+     // If the template is nested, then we need to use the original
+     // pattern to iterate over the constructors.
+-    // FIXME: Should this just use getTemplateInstantiationPattern?
+     ClassTemplateDecl *Pattern = Template;
+     while (Pattern->getInstantiatedFromMemberTemplate()) {
+       if (Pattern->isMemberSpecialization())
+@@ -510,9 +510,9 @@
+       Pattern = Pattern->getInstantiatedFromMemberTemplate();
+       NestedPattern = Pattern;
+     }
++
+     if (NestedPattern)
+-      OuterInstantiationArgs = SemaRef.getTemplateInstantiationArgs(
+-          Decl::castFromDeclContext(Template->getDeclContext()));
++      OuterInstantiationArgs = SemaRef.getTemplateInstantiationArgs(Template);
+   }
+ 
+   Sema &SemaRef;
+@@ -1053,9 +1053,52 @@
+     }
+   }
+ 
+-  MultiLevelTemplateArgumentList ArgsForBuildingRC =
+-      SemaRef.getTemplateInstantiationArgs(F, TemplateArgsForBuildingRC);
++  // A list of template arguments for transforming the require-clause of F.
++  // It must contain the entire set of template argument lists.
++  MultiLevelTemplateArgumentList ArgsForBuildingRC;
+   ArgsForBuildingRC.setKind(clang::TemplateSubstitutionKind::Rewrite);
++  ArgsForBuildingRC.addOuterTemplateArguments(TemplateArgsForBuildingRC);
++  // For 2), if the underlying deduction guide F is nested in a class template,
++  // we need the entire template argument list, as the constraint AST in the
++  // require-clause of F remains completely uninstantiated.
++  //
++  // For example:
++  //   template  // depth 0
++  //   struct Outer {
++  //      template 
++  //      struct Foo { Foo(U); };
++  //
++  //      template  // depth 1
++  //      requires C
++  //      Foo(U) -> Foo;
++  //   };
++  //   template 
++  //   using AFoo = Outer::Foo;
++  //
++  // In this scenario, the deduction guide for `Foo` inside `Outer`:
++  //   - The occurrence of U in the require-expression is [depth:1, index:0]
++  //   - The occurrence of U in the function parameter is [depth:0, index:0]
++  //   - The template parameter of U is [depth:0, index:0]
++  //
++  // We add the outer template arguments which is [int] to the multi-level arg
++  // list to ensure that the occurrence U in `C` will be replaced with int
++  // during the substitution.
++  //
++  // NOTE: The underlying deduction guide F is instantiated -- either from an
++  // explicitly-written deduction guide member, or from a constructor.
++  // getInstantiatedFromMemberTemplate() can only handle the former case, so we
++  // check the DeclContext kind.
++  if (F->getLexicalDeclContext()->getDeclKind() ==
++      clang::Decl::ClassTemplateSpecialization) {
++    auto OuterLevelArgs = SemaRef.getTemplateInstantiationArgs(
++        F, F->getLexicalDeclContext(),
++        /*Final=*/false, /*Innermost=*/std::nullopt,
++        /*RelativeToPrimary=*/true,
++        /*Pattern=*/nullptr,
++        /*ForConstraintInstantiation=*/true);
++    for (auto It : OuterLevelArgs)
++      ArgsForBuildingRC.addOuterTemplateArguments(It.Args);
++  }
+ 
+   ExprResult E = SemaRef.SubstExpr(RC, ArgsForBuildingRC);
+   if (E.isInvalid())
+diff -ruN --strip-trailing-cr a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
+--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
++++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
+@@ -49,6 +49,38 @@
+ //===----------------------------------------------------------------------===/
+ 
+ namespace {
++namespace TemplateInstArgsHelpers {
++struct Response {
++  const Decl *NextDecl = nullptr;
++  bool IsDone = false;
++  bool ClearRelativeToPrimary = true;
++  static Response Done() {
++    Response R;
++    R.IsDone = true;
++    return R;
++  }
++  static Response ChangeDecl(const Decl *ND) {
++    Response R;
++    R.NextDecl = ND;
++    return R;
++  }
++  static Response ChangeDecl(const DeclContext *Ctx) {
++    Response R;
++    R.NextDecl = Decl::castFromDeclContext(Ctx);
++    return R;
++  }
++
++  static Response UseNextDecl(const Decl *CurDecl) {
++    return ChangeDecl(CurDecl->getDeclContext());
++  }
++
++  static Response DontClearRelativeToPrimaryNextDecl(const Decl *CurDecl) {
++    Response R = Response::UseNextDecl(CurDecl);
++    R.ClearRelativeToPrimary = false;
++    return R;
++  }
++};
++
+ // Retrieve the primary template for a lambda call operator. It's
+ // unfortunate that we only have the mappings of call operators rather
+ // than lambda classes.
+@@ -136,197 +168,397 @@
+               .TraverseType(Underlying);
+ }
+ 
+-} // namespace
+-
+-MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
+-    const Decl *D, std::optional> Innermost,
+-    UnsignedOrNone NumLevels, bool SkipInnerNonInstantiated) {
+-  MultiLevelTemplateArgumentList TemplateArgs;
+-
+-  // The scope being followed for an out-of-line-definition.
+-  NestedNameSpecifier Qualifier = std::nullopt;
+-  ArrayRef TPLs;
+-
+-  while (!NumLevels || *NumLevels != 0U) {
+-    bool Final = false;
+-    if (const TemplateParameterList *TPL =
+-            isa(D)
+-                ? cast(D)->getTemplateParameters()
+-                : D->getDescribedTemplateParams()) {
+-      // Template-like entities should always produce a final substitution
+-      // level, as they shouldn't be transformed again.
+-      Final = true;
+-      // If Innermost is provided, use that as the template arguments.
+-      if (!Innermost)
+-        Innermost = TPL->getInjectedTemplateArgs(Context);
+-      else
+-        SkipInnerNonInstantiated = false;
+-    } else {
+-      assert(!Innermost &&
+-             "Shouldn't provide innermost for a non-template-like declaration");
+-
+-      if (isa(
+-              D)) {
+-        auto specCase = [&](const auto *ND) {
+-          if (ND->getSpecializationKind() == TSK_ExplicitSpecialization) {
+-            assert(TPLs.empty());
+-            return;
+-          }
+-          SkipInnerNonInstantiated = false;
+-          Innermost = ND->getTemplateInstantiationArgs().asArray();
+-        };
+-        if (isa(D))
+-          specCase(cast(D));
+-        else
+-          specCase(cast(D));
+-      } else if (isa(D)) {
+-        SkipInnerNonInstantiated = false;
+-        Innermost =
+-            cast(D)->getTemplateArguments();
+-        Final = true;
+-      } else if (const auto *ND = dyn_cast(D)) {
+-        const FunctionTemplateSpecializationInfo *Info =
+-            ND->getTemplateSpecializationInfo();
+-        if (!Info || Info->getTemplateSpecializationKind() ==
+-                         TSK_ExplicitSpecialization) {
+-          assert(TPLs.empty());
+-        } else {
+-          SkipInnerNonInstantiated = false;
+-          Innermost = Info->TemplateArguments->asArray();
+-        }
+-      } else if (const auto *ND = dyn_cast(D);
+-                 ND && ND->isLambda()) {
+-        assert(TPLs.empty() &&
+-               "lambdas can't be part of an out-of-line definition");
+-        // FIXME: If this lambda is enclosed by a type alias template, look
+-        // into the current CodeSynthesisContexts to find the template
+-        // arguments used to specialize it.
+-        if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef);
+-            TypeAlias &&
+-            isLambdaEnclosedByTypeAliasDecl(ND->getLambdaCallOperator(),
+-                                            TypeAlias.PrimaryTypeAliasDecl)) {
+-          Innermost = TypeAlias.AssociatedTemplateArguments;
+-          Final = true;
+-          D = TypeAlias.Template;
+-          continue;
+-        }
+-        if (const Decl *LCD = ND->getLambdaContextDecl()) {
+-          D = LCD;
+-          continue;
+-        }
+-      } else if (isa(D)) {
+-        // The end of the line, there can't be any template arguments further.
+-        assert(TPLs.empty());
+-        assert(!NumLevels && "Declaration had less levels than expected");
+-        return TemplateArgs;
+-      }
+-    }
+-
+-    const Decl *CurDecl = D;
+-
+-    // There is either a current qualifier for an out-of-line definition being
+-    // followed, in which case it should be advanced, or the next qualifier
+-    // and template parameter listss should be retrieved from the current
+-    // declaration, if any.
+-    bool InOutOfLineDefinition = Qualifier != std::nullopt;
+-    switch (Qualifier.getKind()) {
+-    case NestedNameSpecifier::Kind::Null: {
+-      // Get the current qualifier and template parameter lists.
+-      assert(TPLs.empty());
+-      if (auto *TD = dyn_cast(D)) {
+-        D = TD->getTemplatedDecl();
+-        // One of the template entities which doesn't have a templated decl.
+-        if (!D)
++// Add template arguments from a variable template instantiation.
++Response
++HandleVarTemplateSpec(const VarTemplateSpecializationDecl *VarTemplSpec,
++                      MultiLevelTemplateArgumentList &Result,
++                      bool SkipForSpecialization) {
++  // For a class-scope explicit specialization, there are no template arguments
++  // at this level, but there may be enclosing template arguments.
++  if (VarTemplSpec->isClassScopeExplicitSpecialization())
++    return Response::DontClearRelativeToPrimaryNextDecl(VarTemplSpec);
++
++  // We're done when we hit an explicit specialization.
++  if (VarTemplSpec->getSpecializationKind() == TSK_ExplicitSpecialization &&
++      !isa(VarTemplSpec))
++    return Response::Done();
++
++  // If this variable template specialization was instantiated from a
++  // specialized member that is a variable template, we're done.
++  assert(VarTemplSpec->getSpecializedTemplate() && "No variable template?");
++  llvm::PointerUnion
++      Specialized = VarTemplSpec->getSpecializedTemplateOrPartial();
++  if (VarTemplatePartialSpecializationDecl *Partial =
++          dyn_cast(Specialized)) {
++    if (!SkipForSpecialization)
++      Result.addOuterTemplateArguments(
++          Partial, VarTemplSpec->getTemplateInstantiationArgs().asArray(),
++          /*Final=*/false);
++    if (Partial->isMemberSpecialization())
++      return Response::Done();
++  } else {
++    VarTemplateDecl *Tmpl = cast(Specialized);
++    if (!SkipForSpecialization)
++      Result.addOuterTemplateArguments(
++          Tmpl, VarTemplSpec->getTemplateInstantiationArgs().asArray(),
++          /*Final=*/false);
++    if (Tmpl->isMemberSpecialization())
++      return Response::Done();
++  }
++  return Response::DontClearRelativeToPrimaryNextDecl(VarTemplSpec);
++}
++
++// If we have a template template parameter with translation unit context,
++// then we're performing substitution into a default template argument of
++// this template template parameter before we've constructed the template
++// that will own this template template parameter. In this case, we
++// use empty template parameter lists for all of the outer templates
++// to avoid performing any substitutions.
++Response
++HandleDefaultTempArgIntoTempTempParam(const TemplateTemplateParmDecl *TTP,
++                                      MultiLevelTemplateArgumentList &Result) {
++  for (unsigned I = 0, N = TTP->getDepth() + 1; I != N; ++I)
++    Result.addOuterTemplateArguments(std::nullopt);
++  return Response::Done();
++}
++
++Response HandlePartialClassTemplateSpec(
++    const ClassTemplatePartialSpecializationDecl *PartialClassTemplSpec,
++    MultiLevelTemplateArgumentList &Result, bool SkipForSpecialization) {
++  if (!SkipForSpecialization)
++    Result.addOuterRetainedLevels(PartialClassTemplSpec->getTemplateDepth());
++  return Response::Done();
++}
++
++// Add template arguments from a class template instantiation.
++Response
++HandleClassTemplateSpec(const ClassTemplateSpecializationDecl *ClassTemplSpec,
++                        MultiLevelTemplateArgumentList &Result,
++                        bool SkipForSpecialization) {
++  if (!ClassTemplSpec->isClassScopeExplicitSpecialization()) {
++    // We're done when we hit an explicit specialization.
++    if (ClassTemplSpec->getSpecializationKind() == TSK_ExplicitSpecialization &&
++        !isa(ClassTemplSpec))
++      return Response::Done();
++
++    if (!SkipForSpecialization)
++      Result.addOuterTemplateArguments(
++          const_cast(ClassTemplSpec),
++          ClassTemplSpec->getTemplateInstantiationArgs().asArray(),
++          /*Final=*/false);
++
++    // If this class template specialization was instantiated from a
++    // specialized member that is a class template, we're done.
++    assert(ClassTemplSpec->getSpecializedTemplate() && "No class template?");
++    if (ClassTemplSpec->getSpecializedTemplate()->isMemberSpecialization())
++      return Response::Done();
++
++    // If this was instantiated from a partial template specialization, we need
++    // to get the next level of declaration context from the partial
++    // specialization, as the ClassTemplateSpecializationDecl's
++    // DeclContext/LexicalDeclContext will be for the primary template.
++    if (auto *InstFromPartialTempl =
++            ClassTemplSpec->getSpecializedTemplateOrPartial()
++                .dyn_cast())
++      return Response::ChangeDecl(
++          InstFromPartialTempl->getLexicalDeclContext());
++  }
++  return Response::UseNextDecl(ClassTemplSpec);
++}
++
++Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function,
++                        MultiLevelTemplateArgumentList &Result,
++                        const FunctionDecl *Pattern, bool RelativeToPrimary,
++                        bool ForConstraintInstantiation,
++                        bool ForDefaultArgumentSubstitution) {
++  // Add template arguments from a function template specialization.
++  if (!RelativeToPrimary &&
++      Function->getTemplateSpecializationKindForInstantiation() ==
++          TSK_ExplicitSpecialization)
++    return Response::Done();
++
++  if (!RelativeToPrimary &&
++      Function->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) {
++    // This is an implicit instantiation of an explicit specialization. We
++    // don't get any template arguments from this function but might get
++    // some from an enclosing template.
++    return Response::UseNextDecl(Function);
++  } else if (const TemplateArgumentList *TemplateArgs =
++                 Function->getTemplateSpecializationArgs()) {
++    // Add the template arguments for this specialization.
++    Result.addOuterTemplateArguments(const_cast(Function),
++                                     TemplateArgs->asArray(),
++                                     /*Final=*/false);
++
++    if (RelativeToPrimary &&
++        (Function->getTemplateSpecializationKind() ==
++             TSK_ExplicitSpecialization ||
++         (Function->getFriendObjectKind() &&
++          !Function->getPrimaryTemplate()->getFriendObjectKind())))
++      return Response::UseNextDecl(Function);
++
++    // If this function was instantiated from a specialized member that is
++    // a function template, we're done.
++    assert(Function->getPrimaryTemplate() && "No function template?");
++    if (!ForDefaultArgumentSubstitution &&
++        Function->getPrimaryTemplate()->isMemberSpecialization())
++      return Response::Done();
++
++    // If this function is a generic lambda specialization, we are done.
++    if (!ForConstraintInstantiation &&
++        isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function))
++      return Response::Done();
++
++  } else if (auto *Template = Function->getDescribedFunctionTemplate()) {
++    assert(
++        (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) &&
++        "Outer template not instantiated?");
++    if (ForConstraintInstantiation) {
++      for (auto &Inst : llvm::reverse(SemaRef.CodeSynthesisContexts)) {
++        if (Inst.Kind == Sema::CodeSynthesisContext::ConstraintsCheck &&
++            Inst.Entity == Template) {
++          // After CWG2369, the outer templates are not instantiated when
++          // checking its associated constraints. So add them back through the
++          // synthesis context; this is useful for e.g. nested constraints
++          // involving lambdas.
++          Result.addOuterTemplateArguments(Template, Inst.template_arguments(),
++                                           /*Final=*/false);
+           break;
++        }
+       }
+-      if (auto *DD = dyn_cast(D)) {
+-        TPLs = DD->getTemplateParameterLists();
+-        Qualifier = DD->getQualifier();
+-      } else if (const auto *TD = dyn_cast(D)) {
+-        TPLs = TD->getTemplateParameterLists();
+-        Qualifier = TD->getQualifier();
+-      }
+-      assert((Qualifier || TPLs.empty()) &&
+-             "template parameter lists without qualifier");
+-      break;
+-    }
+-    case NestedNameSpecifier::Kind::Namespace:
+-    case NestedNameSpecifier::Kind::Global:
+-      llvm_unreachable("handled above");
+-    case NestedNameSpecifier::Kind::Type:
+-      Qualifier = Qualifier.getAsType()->getPrefix();
+-      break;
+-    case NestedNameSpecifier::Kind::MicrosoftSuper:
+-      Qualifier = std::nullopt;
+-      break;
+     }
++  }
++  // If this is a friend or local declaration and it declares an entity at
++  // namespace scope, take arguments from its lexical parent
++  // instead of its semantic parent, unless of course the pattern we're
++  // instantiating actually comes from the file's context!
++  if ((Function->getFriendObjectKind() || Function->isLocalExternDecl()) &&
++      Function->getNonTransparentDeclContext()->isFileContext() &&
++      (!Pattern || !Pattern->getLexicalDeclContext()->isFileContext())) {
++    return Response::ChangeDecl(Function->getLexicalDeclContext());
++  }
++
++  if (ForConstraintInstantiation && Function->getFriendObjectKind())
++    return Response::ChangeDecl(Function->getLexicalDeclContext());
++  return Response::UseNextDecl(Function);
++}
++
++Response HandleFunctionTemplateDecl(Sema &SemaRef,
++                                    const FunctionTemplateDecl *FTD,
++                                    MultiLevelTemplateArgumentList &Result) {
++  if (!isa(FTD->getDeclContext())) {
++    Result.addOuterTemplateArguments(
++        const_cast(FTD),
++        const_cast(FTD)->getInjectedTemplateArgs(
++            SemaRef.Context),
++        /*Final=*/false);
++
++    NestedNameSpecifier NNS = FTD->getTemplatedDecl()->getQualifier();
++
++    for (const Type *Ty = NNS.getKind() == NestedNameSpecifier::Kind::Type
++                              ? NNS.getAsType()
++                              : nullptr,
++                    *NextTy = nullptr;
++         Ty && Ty->isInstantiationDependentType();
++         Ty = std::exchange(NextTy, nullptr)) {
++      if (NestedNameSpecifier P = Ty->getPrefix();
++          P.getKind() == NestedNameSpecifier::Kind::Type)
++        NextTy = P.getAsType();
++      const auto *TSTy = dyn_cast(Ty);
++      if (!TSTy)
++        continue;
+ 
+-    // Get the next declaration from the current qualifier.
+-    switch (Qualifier.getKind()) {
+-    case NestedNameSpecifier::Kind::Null:
+-      // If there is no qualifier, the template context always follows
+-      // lexically.
+-      D = Decl::castFromDeclContext(CurDecl->getLexicalDeclContext());
+-      break;
+-    case NestedNameSpecifier::Kind::Namespace:
+-    case NestedNameSpecifier::Kind::Global:
+-      // Skip namespace, as they are never templated.
+-      D = Context.getTranslationUnitDecl();
+-      break;
+-    case NestedNameSpecifier::Kind::Type:
+-      if (const Type *T = Qualifier.getAsType();
+-          const auto *TT = T->getAs())
+-        D = TT->getDecl();
+-      else if (const auto *TST = T->getAsNonAliasTemplateSpecializationType())
+-        D = TST->getTemplateName().getAsTemplateDecl(/*IgnoreDeduced=*/true);
+-      else
+-        llvm_unreachable("unexpected type kind");
+-      break;
+-    case NestedNameSpecifier::Kind::MicrosoftSuper:
+-      D = Qualifier.getAsMicrosoftSuper();
+-      break;
+-    }
+-
+-    if (!Innermost)
+-      continue;
+-
+-    auto DeclArgs = *std::exchange(Innermost, std::nullopt);
+-    assert(!DeclArgs.empty());
++      ArrayRef Arguments = TSTy->template_arguments();
++      // Prefer template arguments from the injected-class-type if possible.
++      // For example,
++      // ```cpp
++      // template  struct S {
++      //   template  void foo();
++      // };
++      // template  template 
++      //           ^^^^^^^^^^^^^ InjectedTemplateArgs
++      //           They're of kind TemplateArgument::Pack, not of
++      //           TemplateArgument::Type.
++      // void S::foo() {}
++      //        ^^^^^^^
++      //        TSTy->template_arguments() (which are of PackExpansionType)
++      // ```
++      // This meets the contract in
++      // TreeTransform::TryExpandParameterPacks that the template arguments
++      // for unexpanded parameters should be of a Pack kind.
++      if (TSTy->isCurrentInstantiation()) {
++        auto *RD = TSTy->getCanonicalTypeInternal()->getAsCXXRecordDecl();
++        if (ClassTemplateDecl *CTD = RD->getDescribedClassTemplate())
++          Arguments = CTD->getInjectedTemplateArgs(SemaRef.Context);
++        else if (auto *Specialization =
++                     dyn_cast(RD))
++          Arguments = Specialization->getTemplateInstantiationArgs().asArray();
++      }
++      Result.addOuterTemplateArguments(
++          TSTy->getTemplateName().getAsTemplateDecl(), Arguments,
++          /*Final=*/false);
++    }
++  }
++
++  return Response::ChangeDecl(FTD->getLexicalDeclContext());
++}
++
++Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec,
++                          MultiLevelTemplateArgumentList &Result,
++                          ASTContext &Context,
++                          bool ForConstraintInstantiation) {
++  if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) {
++    assert(
++        (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) &&
++        "Outer template not instantiated?");
++    if (ClassTemplate->isMemberSpecialization())
++      return Response::Done();
++    if (ForConstraintInstantiation)
++      Result.addOuterTemplateArguments(
++          const_cast(Rec),
++          ClassTemplate->getInjectedTemplateArgs(SemaRef.Context),
++          /*Final=*/false);
++  }
++
++  if (const MemberSpecializationInfo *MSInfo =
++          Rec->getMemberSpecializationInfo())
++    if (MSInfo->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
++      return Response::Done();
++
++  bool IsFriend = Rec->getFriendObjectKind() ||
++                  (Rec->getDescribedClassTemplate() &&
++                   Rec->getDescribedClassTemplate()->getFriendObjectKind());
++  if (ForConstraintInstantiation && IsFriend &&
++      Rec->getNonTransparentDeclContext()->isFileContext()) {
++    return Response::ChangeDecl(Rec->getLexicalDeclContext());
++  }
++
++  // This is to make sure we pick up the VarTemplateSpecializationDecl or the
++  // TypeAliasTemplateDecl that this lambda is defined inside of.
++  if (Rec->isLambda()) {
++    if (const Decl *LCD = Rec->getLambdaContextDecl())
++      return Response::ChangeDecl(LCD);
++    // Retrieve the template arguments for a using alias declaration.
++    // This is necessary for constraint checking, since we always keep
++    // constraints relative to the primary template.
++    if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef);
++        ForConstraintInstantiation && TypeAlias) {
++      if (isLambdaEnclosedByTypeAliasDecl(Rec->getLambdaCallOperator(),
++                                          TypeAlias.PrimaryTypeAliasDecl)) {
++        Result.addOuterTemplateArguments(TypeAlias.Template,
++                                         TypeAlias.AssociatedTemplateArguments,
++                                         /*Final=*/false);
++        // Visit the parent of the current type alias declaration rather than
++        // the lambda thereof.
++        // E.g., in the following example:
++        // struct S {
++        //  template  using T = decltype([] {} ());
++        // };
++        // void foo() {
++        //   S::T var;
++        // }
++        // The instantiated lambda expression (which we're visiting at 'var')
++        // has a function DeclContext 'foo' rather than the Record DeclContext
++        // S. This seems to be an oversight to me that we may want to set a
++        // Sema Context from the CXXScopeSpec before substituting into T.
++        return Response::ChangeDecl(TypeAlias.Template->getDeclContext());
++      }
++    }
++  }
++
++  return Response::UseNextDecl(Rec);
++}
++
++Response HandleImplicitConceptSpecializationDecl(
++    const ImplicitConceptSpecializationDecl *CSD,
++    MultiLevelTemplateArgumentList &Result) {
++  Result.addOuterTemplateArguments(
++      const_cast(CSD),
++      CSD->getTemplateArguments(),
++      /*Final=*/false);
++  return Response::UseNextDecl(CSD);
++}
+ 
+-    if (InOutOfLineDefinition) {
+-      // There either may or may not be a template parameter list corresponding
+-      // to the current qualifier chunk.
+-      // A template level only exists where there is a non-empty template
+-      // parameter list. Explicit specializations, ie `template <>` are never a
+-      // template level.
+-      TemplateParameterList *TPL =
+-          !TPLs.empty() ? TPLs.consume_back() : nullptr;
+-      if (!TPL || TPL->empty())
+-        continue;
+-    }
++Response HandleGenericDeclContext(const Decl *CurDecl) {
++  return Response::UseNextDecl(CurDecl);
++}
++} // namespace TemplateInstArgsHelpers
++} // namespace
+ 
+-    if (!SkipInnerNonInstantiated)
+-      TemplateArgs.addOuterTemplateArguments(const_cast(CurDecl),
+-                                             DeclArgs, Final);
+-
+-    // A new level was just added. So if there is a level limit, decrement it.
+-    if (NumLevels)
+-      NumLevels = *NumLevels - 1U;
+-
+-    // FIXME: Workaround for template template parameters on other template
+-    // template parameters and on type alias templates.
+-    if (const auto *TTP = dyn_cast(CurDecl)) {
+-      unsigned ExtraLevels = TTP->getDepth() - D->getTemplateDepth() + 1;
+-      if (NumLevels) {
+-        ExtraLevels = std::min(ExtraLevels, *NumLevels);
+-        NumLevels = *NumLevels - ExtraLevels;
++MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
++    const NamedDecl *ND, const DeclContext *DC, bool Final,
++    std::optional> Innermost, bool RelativeToPrimary,
++    const FunctionDecl *Pattern, bool ForConstraintInstantiation,
++    bool SkipForSpecialization, bool ForDefaultArgumentSubstitution) {
++  assert((ND || DC) && "Can't find arguments for a decl if one isn't provided");
++  // Accumulate the set of template argument lists in this structure.
++  MultiLevelTemplateArgumentList Result;
++
++  using namespace TemplateInstArgsHelpers;
++  const Decl *CurDecl = ND;
++
++  if (Innermost) {
++    Result.addOuterTemplateArguments(const_cast(ND), *Innermost,
++                                     Final);
++    // Populate placeholder template arguments for TemplateTemplateParmDecls.
++    // This is essential for the case e.g.
++    //
++    // template  concept Concept = false;
++    // template