I have found what I believe to be a weird bug with std::optional in GCC on my macbook. I am hoping that someone can either show me that I'm wrong, or offer guidance about where to report it.
Background:
I recently decided that I wanted to learn more about monads, so I started following along with a presentation given at CppConn this past year. (Video here for those interested)
My environment is composed of:
Hardware: Apple M2 Macbook Air
OS: Sonoma 14.2.1
GCC: 13.2.0
Initial Problem:
In the following code, I found that I was unable to catch the std::invalid_argument being thrown by std::stoi.
Code:
#include <iostream>
#include <optional>
std::optional<int> convertStrToInt(const std::string& s) {
std::optional<int> ret;
try {
ret = std::stoi(s);
} catch (...) {
std::cout << "Caught bad str to int conversion!\n";
}
return ret;
}
int doubleValue(int v) {
return v+v;
}
std::string convertIntToStr(int v) {
return std::to_string(v);
}
std::optional<std::string> stringDouble(const std::optional<std::string>& opt) {
return opt.and_then(convertStrToInt)
.transform(doubleValue)
.transform(convertIntToStr);
}
int main() {
std::optional<std::string> result;
std::optional<std::string> input_1("123");
result = stringDouble(input_1);
if ( result.has_value() )
std::cout << "stringDouble's result for input_1: " << result.value() << std::endl;
else
std::cout << "stringDouble has no result for input_1\n";
std::optional<std::string> input_2("NaN");
result = stringDouble(input_2);
if ( result.has_value() )
std::cout << "stringDouble's result for input_2: " << result.value() << std::endl;
else
std::cout << "stringDouble has no result for input_2\n";
return 0;
}
Compile:
g++-13 -std=c++23 std_opt.cpp
Output:
stringDouble's result for input_1: 246
terminate called after throwing an instance of 'std::invalid_argument'
what(): stoi
Abort trap: 6
Concise Problem:
Given the unexpected result above, I decided to make a more concise program to see if I could better determine the cause.
Code:
#include <iostream>
#include <optional>
int main() {
try {
throw std::runtime_error("why?!");
} catch (...) {
std::cout << "caught exception\n";
}
std::optional<std::string> some_opt("my val");
return 0;
}
Compile:
g++-13 -std=c++23 exception_test.cpp
Output:
libunwind: _Unwind_GetTextRelBase - _Unwind_GetTextRelBase() not implemented
Abort trap: 6
It is also worth noting, that replacing line 11 above with any other, valid c++ code seems fine. E.g., putting a std::cout seems to work as desired.
Testing in Linux:
To see if this was just a Mac issue, created a container from the gcc:13.2.0 image and copied the above code into it.
As you can see, it works as desired:
# uname -a
Linux 62b6636acd54 6.7.4-200.fc39.aarch64 #1 SMP PREEMPT_DYNAMIC Mon Feb 5 23:05:56 UTC 2024 aarch64 GNU/Linux
# g++ --version
g++ (GCC) 13.2.0
Copyright (C) 2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# g++ -std=c++23 exception_test.cpp
# ./a.out
caught exception
# g++ -std=c++23 std_opt.cpp
# ./a.out
stringDouble's result for input_1: 246
Caught bad str to int conversion!
stringDouble has no result for input_2
What's Next
So... am I missing something? Is this a bug? Is it a bug with GCC on Mac? Is it a bug in libunwind? Are the above two behaviors even part of the same issue? I am at a bit of a loss.
Thanks!