Iterator debugging asserts in mixed managed/native code
Describe the bug
Iterator debugging fires an assert in xmemory when the sample is built with mixed managed/unmanaged translation units. When everything is compiled as unmanaged the assert does not fire.
The sample involves a std::function passing across managed/unmanaged boundaries with placeholders for the iterators. The assert fires for foo from the managed code, but not bar from the unmanaged code. I would expect it to fire for both or neither.
Command-line test case STL version (git commit or Visual Studio version): 0281a452293b4e9bd2a0b77cdd1a58ac63a9b086
Self contained test case is at:
https://github.com/ngoozeff/iter_test
Open a Visual Studio Command Prompt to the directory containing the checked out code from above, execute the run.cmd command. run2.cmd has some setup helpers for the include paths etc, you would need to adjust to your environment.
Expected behavior Program runs cleanly.
Additional context
Microsoft (R) C/C++ Optimizing Compiler Version 19.23.28105.4 for x64
I've reproduced this bug, thanks for the easy to run (and easy to get) reproduction case.
I reduced the repro a bit:
// main.cc, native code
#include <vector>
void apply(std::vector<int>::iterator) {}
extern void foo();
int main() {
foo();
}
// mgd.cc, managed
#include <vector>
extern void apply(std::vector<int>::iterator b);
static_assert(std::_Is_invocable_r<void, void (*)(std::vector<int>::iterator), std::vector<int>::iterator>::value, "");
void foo() {
std::vector<int> v;
apply(v.begin());
}
If we comment out the static assert from mgd.cc then the program will run cleanly.
I tested on VS 2022 17.1 Preview 2
I have looked in the assembly.
if we comment out the static assert then the assembly has 2 more functions:
std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int> > >::<MarshalDestroy>
and
std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int> > >::<MarshalCopy>
I reduced a little bit more:
// main.cc, native code
#include <vector>
void apply(std::vector<int>::iterator) {}
extern void foo();
int main() {
foo();
}
// mgd.cc, managed
#include <vector>
extern void apply(std::vector<int>::iterator b);
auto test() -> decltype(apply(std::vector<int>::iterator{}))
{}
void foo() {
std::vector<int> v;
apply(v.begin());
}
if "test" function is defined then we get the same assert error. If we comment out the "test" function then the program works fine.
one more time:
// main.cc, native code
struct dummy {
int* a = nullptr;
dummy() {
a = new int[100];
}
dummy(const dummy&) {
delete[] a;
a = new int[100];
}
~dummy() {
delete[] a;
}
};
void apply(dummy a) {}
extern void foo();
int main() {
foo();
}
// mgd.cc, managed
struct dummy {
int* a = nullptr;
dummy() {
a = new int[100];
}
dummy(const dummy&) {
delete[] a;
a = new int[100];
}
~dummy() {
delete[] a;
}
};
extern void apply(dummy a);
auto test() -> decltype(apply(dummy{})) {}
void foo() {
dummy v;
apply(v);
}
if we comment out auto test() -> decltype(apply(dummy{})) {} then managed code uses dummy::<MarshalCopy>
I might be wrong but I think it is a compiler issue.
I think we should use dummy::<MarshalCopy> always.
I will inform the compiler developers.
I created DevCom-1632617