[Bug] std::variant in containers triggers UBSan error in Xcode 16.x

I've discovered a bug in Xcode 16.0/16.1 where using std::variant in containers across compilation units triggers UBSan errors. This is a regression as it works correctly in Xcode 15.4.

Here's a minimal reproduction case:

[base.h]

#pragma once
#include <string>
#include <variant>
#include <forward_list>

class Item {
public:
    std::variant<std::monostate, std::string> value;
};

typedef std::forward_list<Item> ItemList;

class Test {
public:
    void addItem(const Item& item);
    ItemList items;
};

[base.cpp]

#include "base.h"

void Test::addItem(const Item& item) {
    items.push_front(item);
}

[main.cpp]

#include "base.h"

int main() {
    Test t;
    Item item;
    t.addItem(item);
    return 0;
}

To reproduce:

  1. Compile with UBSan enabled (-fsanitize=undefined)
  2. Occurs on both arm64 and x86_64
  3. Occurs in both Xcode 16.0 and 16.1
  4. Works correctly in Xcode 15.4

I've filed a Feedback Assistant report: FB15710420

Workaround: The issue can be avoided by implementing the addItem method in the header file instead of a separate compilation unit.

Has anyone else encountered this issue? Are there other workarounds besides moving the implementation to the header?

What exactly does UBSan say?

@endecotp :

/Applications/Xcode-16.0/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/include/c++/v1/variant:495:12: runtime error: call to function decltype(auto) std::__1::__variant_detail::__visitation::__base::__dispatcher<0ul, 0ul>::__dispatch[abi:de180100]<void std::__1::__variant_detail::__ctor<std::__1::__variant_detail::__traits<std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>>::__generic_construct[abi:de180100]<std::__1::__variant_detail::__copy_constructor<std::__1::__variant_detail::__traits<std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, (std::__1::__variant_detail::_Trait)1> const&>(std::__1::__variant_detail::__ctor<std::__1::__variant_detail::__traits<std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>>&, std::__1::__variant_detail::__copy_constructor<std::__1::__variant_detail::__traits<std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, (std::__1::__variant_detail::_Trait)1> const&)::'lambda'(std::__1::__variant_detail::__copy_constructor<std::__1::__variant_detail::__traits<std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, (std::__1::__variant_detail::_Trait)1> const&, auto&&)&&, std::__1::__variant_detail::__base<(std::__1::__variant_detail::_Trait)1, std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>&, std::__1::__variant_detail::__base<(std::__1::__variant_detail::_Trait)1, std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>> const&>(std::__1::__variant_detail::__copy_constructor<std::__1::__variant_detail::__traits<std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, (std::__1::__variant_detail::_Trait)1> const&, std::__1::__variant_detail::__base<(std::__1::__variant_detail::_Trait)1, std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>&, std::__1::__variant_detail::__base<(std::__1::__variant_detail::_Trait)1, std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>> const&) through pointer to incorrect function type 'void (*)((lambda at /Applications/Xcode-16.0/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/include/c++/v1/variant:814:11) &&, std::__variant_detail::__base<std::__variant_detail::_Trait::_Available, std::monostate, std::string> &, const std::__variant_detail::__base<std::__variant_detail::_Trait::_Available, std::monostate, std::string> &)'
variant:532: note: decltype(auto) std::__1::__variant_detail::__visitation::__base::__dispatcher<0ul, 0ul>::__dispatch[abi:de180100]<void std::__1::__variant_detail::__ctor<std::__1::__variant_detail::__traits<std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>>::__generic_construct[abi:de180100]<std::__1::__variant_detail::__copy_constructor<std::__1::__variant_detail::__traits<std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, (std::__1::__variant_detail::_Trait)1> const&>(std::__1::__variant_detail::__ctor<std::__1::__variant_detail::__traits<std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>>&, std::__1::__variant_detail::__copy_constructor<std::__1::__variant_detail::__traits<std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, (std::__1::__variant_detail::_Trait)1> const&)::'lambda'(std::__1::__variant_detail::__copy_constructor<std::__1::__variant_detail::__traits<std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, (std::__1::__variant_detail::_Trait)1> const&, auto&&)&&, std::__1::__variant_detail::__base<(std::__1::__variant_detail::_Trait)1, std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>&, std::__1::__variant_detail::__base<(std::__1::__variant_detail::_Trait)1, std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>> const&>(std::__1::__variant_detail::__copy_constructor<std::__1::__variant_detail::__traits<std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, (std::__1::__variant_detail::_Trait)1> const&, std::__1::__variant_detail::__base<(std::__1::__variant_detail::_Trait)1, std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>&, std::__1::__variant_detail::__base<(std::__1::__variant_detail::_Trait)1, std::__1::monostate, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>> const&) defined here
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /Applications/Xcode-16.0/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.0.sdk/usr/include/c++/v1/variant:495:12

This error appears only on Xcode 16*

Stripping that down a bit:

runtime error: 
call to function
through pointer to incorrect function type

Which is interesting. Maybe it's a bug in libc++. I would be interested to see if it is specific to having monostate in the variant. Try changing that to int.

I've tried to search to see if it's a known/resolved clang / ubsan / libc++ bug but I've not found anything obvious.

Thanks for looking into this. I've done more testing and found that the issue is broader than originally shown. The problem occurs with complex types in the variant, regardless of monostate.

This minimal case triggers the error:

class Item {
public:
    std::variant<std::string> value;
};

typedef std::forward_list<Item> ItemList;

class Test {
public:
    void addItem(const Item& item);
    ItemList items;
};

While the remaining code and compilation steps are the same.

[Bug] std::variant in containers triggers UBSan error in Xcode 16.x
 
 
Q