Skip to content

tuple variant crashing iterating over vector with std::apply (clang 17.0.1 - clang 19.1.0) #126246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wh1t3lord opened this issue Feb 7, 2025 · 4 comments
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" crash Prefer [crash-on-valid] or [crash-on-invalid]

Comments

@wh1t3lord
Copy link

Problem

Here's a function that fills a std::vector<std::variant<Ts...>> from a std::vectorstd::string_view. The function template takes a std::tuple<Ts...> as a type parameter and uses std::apply to iterate over the tuple's types. It attempts to convert each string in the input vector to the corresponding type or keeps it as a string if conversion is not possible.

Code & Proof

#include <iostream>
#include <vector>
#include <variant>
#include <tuple>
#include <string_view>
#include <cstdlib> // for std::atoi, std::atof
#include <type_traits>

template <typename... Ts>
std::vector<std::variant<Ts...>> fill_vector(const std::vector<std::string_view>& input) {
    using VariantType = std::variant<Ts...>;
    std::vector<VariantType> result;
    result.reserve(input.size());

    for (const auto& str : input) {
        bool converted = false;

        // Create a temporary tuple to pass to std::apply
        std::tuple<Ts...> tempTuple;

        std::apply([&](auto&&... args) {
            // Fold expression to try each type
            (([&] {
                using T = std::decay_t<decltype(args)>;
                if (!converted) {
                    if constexpr (std::is_integral_v<T>) {
                        try {
                            result.emplace_back(static_cast<T>(std::atoi(str.data())));
                            converted = true;
                        } catch (...) {}
                    } else if constexpr (std::is_floating_point_v<T>) {
                        try {
                            result.emplace_back(static_cast<T>(std::atof(str.data())));
                            converted = true;
                        } catch (...) {}
                    }
                }
            }()), ...);
        }, tempTuple);

        if (!converted) {
            result.emplace_back(std::string(str)); // Fallback to string
        }
    }

    return result;
}

int main() {
    std::vector<std::string_view> input = {"123", "45.67", "hello", "789"};

    auto output = fill_vector<int, double, std::string>(input);

    for (const auto& v : output) {
        std::visit([](const auto& value) { std::cout << value << " "; }, v);
    }

    return 0;
}

Proof №1: https://godbolt.org/z/TPv4vcGch (clang 17.0.1)

Proof №2: https://godbolt.org/z/178zP4x9v (clang 19.1.0)

So both crashes.

Working sample (Expected behaviour)

Unexpected but it compiles on gcc.

Link: https://godbolt.org/z/W8jqWqzE3 (gcc 12.1)

Link: https://godbolt.org/z/n9GWG4EM8 (gcc 14.2)

Crash backtrace

Proceed to godbolt site with attached link in previous section.

@llvmbot llvmbot added the clang Clang issues not falling into any other category label Feb 7, 2025
@zwuis
Copy link
Contributor

zwuis commented Feb 7, 2025

Proof №2: https://godbolt.org/z/178zP4x9v (clang 19.1.0)

Doesn't reproduce on Clang thunk. Please wait for Clang 20 release.

Unexpected but it compiles on gcc.

You can visit cppreference to see how std::atoi works.

@EugeneZelenko EugeneZelenko added clang:frontend Language frontend issues, e.g. anything involving "Sema" crash Prefer [crash-on-valid] or [crash-on-invalid] and removed clang Clang issues not falling into any other category labels Feb 7, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 7, 2025

@llvm/issue-subscribers-clang-frontend

Author: None (wh1t3lord)

## Problem

Here's a function that fills a std::vector<std::variant<Ts...>> from a std::vector<std::string_view>. The function template takes a std::tuple<Ts...> as a type parameter and uses std::apply to iterate over the tuple's types. It attempts to convert each string in the input vector to the corresponding type or keeps it as a string if conversion is not possible.

Code & Proof

#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;variant&gt;
#include &lt;tuple&gt;
#include &lt;string_view&gt;
#include &lt;cstdlib&gt; // for std::atoi, std::atof
#include &lt;type_traits&gt;

template &lt;typename... Ts&gt;
std::vector&lt;std::variant&lt;Ts...&gt;&gt; fill_vector(const std::vector&lt;std::string_view&gt;&amp; input) {
    using VariantType = std::variant&lt;Ts...&gt;;
    std::vector&lt;VariantType&gt; result;
    result.reserve(input.size());

    for (const auto&amp; str : input) {
        bool converted = false;

        // Create a temporary tuple to pass to std::apply
        std::tuple&lt;Ts...&gt; tempTuple;

        std::apply([&amp;](auto&amp;&amp;... args) {
            // Fold expression to try each type
            (([&amp;] {
                using T = std::decay_t&lt;decltype(args)&gt;;
                if (!converted) {
                    if constexpr (std::is_integral_v&lt;T&gt;) {
                        try {
                            result.emplace_back(static_cast&lt;T&gt;(std::atoi(str.data())));
                            converted = true;
                        } catch (...) {}
                    } else if constexpr (std::is_floating_point_v&lt;T&gt;) {
                        try {
                            result.emplace_back(static_cast&lt;T&gt;(std::atof(str.data())));
                            converted = true;
                        } catch (...) {}
                    }
                }
            }()), ...);
        }, tempTuple);

        if (!converted) {
            result.emplace_back(std::string(str)); // Fallback to string
        }
    }

    return result;
}

int main() {
    std::vector&lt;std::string_view&gt; input = {"123", "45.67", "hello", "789"};

    auto output = fill_vector&lt;int, double, std::string&gt;(input);

    for (const auto&amp; v : output) {
        std::visit([](const auto&amp; value) { std::cout &lt;&lt; value &lt;&lt; " "; }, v);
    }

    return 0;
}

Proof №1: https://godbolt.org/z/TPv4vcGch (clang 17.0.1)

Proof №2: https://godbolt.org/z/178zP4x9v (clang 19.1.0)

So both crashes.

Working sample (Expected behaviour)

Unexpected but it compiles on gcc.

Link: https://godbolt.org/z/W8jqWqzE3 (gcc 12.1)

Link: https://godbolt.org/z/n9GWG4EM8 (gcc 14.2)

Crash backtrace

Proceed to godbolt site with attached link in previous section.

@shafik
Copy link
Collaborator

shafik commented Feb 7, 2025

This looks fixed on trunk, maybe this is related to #122417

CC @zyn0217

@zyn0217
Copy link
Contributor

zyn0217 commented Feb 8, 2025

As a workaround you can inline the type-alias into the template arugments: https://godbolt.org/z/P57q6h7os

@zyn0217 zyn0217 closed this as completed Feb 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" crash Prefer [crash-on-valid] or [crash-on-invalid]
Projects
None yet
Development

No branches or pull requests

6 participants