fut
fut copied to clipboard
Temporary values are not destructed in C, e.g. string interpolation leaks memory
Consider OpAddAssignString.ci
:
s2 += p + $"{p}" + s; //FAIL: cpp - should work with C++20
gets compiled to
CiString_Assign(&s2, CiString_Format("%s%s%s%s", s2, p, p, s));
here, the result of CiString_Format
is a result of malloc
. It's passed to CiString_Assign
and abandoned afterwards, leaking memory. This does not happen in C++ because temporary values are automatically destructed in C++, including std::string
.
I assume there may be other instances where temporary objects are created and not immediately stored into variables.
All tests are run in Travis CI. C, C++ and Swift tests are run with the Address Sanitizer and OpAddAssignString
passes.
There's this line in the C translation of OpAddAssignString
:
free(s2);
Why do you think it leaks memory?
I'm sorry, I misread the example. Here is a simpler one:
public static class Test
{
static void Foo(string x) {
}
public static void Run()
{
int x;
Foo($"{x}");
}
}
gets compiled to
static void Test_Foo(const char *x)
{
}
void Test_Run(void)
{
int x;
Test_Foo(CiString_Format("%d", x));
}
There is no free
in this program at all.
Yes, it's a known issue. I've just added a test and will fix it.
Interpolated strings are even worse when targetting C++, because they use C++20 <format>
. Even though C++20 was ratified some time ago, clang
only now starts implementing <format>
.
I believe the problem also exists with dynamic references in general. E.g. the following code:
class Foo {
}
public static class Test
{
public static void Consume(Foo f) {}
public static Foo# Create() { return new Foo(); }
public static void Run() {
Consume(Create());
}
}
leaks memory and does not induce the Dynamically allocated objects must be assigned to a Foo# reference
error, as Consume(new Foo())
would.
cito
cannot determine all the dangling reference scenarios. If it were easy, C and C++ compilers would do it, given five decades of their development.
Consume(Create())
would be valid code if Create()
stored the dynamic reference somewhere. The Create
method itself is valid if used correctly.
FWIW, I just learned that smart pointers are possible in C with GNU compilers: https://stackoverflow.com/questions/799825/smart-pointers-safe-memory-management-for-c
This issue is still unfixed for string concatination. The following code
string s1 = "1";
string s2 = "2";
string s3 = "3";
string s4 = "4";
string s5 = "5";
string() total = s1+s2+s3+s4+s5;
is transpiling to
const char *s1 = "1";
const char *s2 = "2";
const char *s3 = "3";
const char *s4 = "4";
const char *s5 = "5";
char *futemp0 = FuString_Format("%s%s", s1, s2);
char *futemp1 = FuString_Format("%s%s", FuString_Format("%s%s", s1, s2), s3);
char *futemp2 = FuString_Format("%s%s", FuString_Format("%s%s", FuString_Format("%s%s", s1, s2), s3), s4);
char *total = FuString_Format("%s%s", FuString_Format("%s%s", FuString_Format("%s%s", FuString_Format("%s%s", s1, s2), s3), s4), s5);
There are temporal variables declared correctly (futemp0, futemp1, futemp2), but this variables are not used in the FuString_Format()
calls.
P.S. In the case of interpolation there is no such issue.
string() total = $"{s1}{s2}{s3}{s4}{s5}";
All FuString_Format()
calls are merged in one:
char *total = FuString_Format("%s%s%s%s%s", s1, s2, s3, s4, s5);
@Timerix22 af1f308 fixes the concatenation of multiple strings.
I will work on the remaining cases.
All cases reported here are now fixed.