Skip to content
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

ldc fails when using a struct member address in an associative array #2859

Closed
LaurentTreguier opened this issue Sep 28, 2018 · 8 comments
Closed

Comments

@LaurentTreguier
Copy link

With the following code, LDC seems to just stop and doesn't produce any executable or object file.

import std.stdio;

struct S
{
	string a = "a";
}

void main()
{
	S s;
	string str = "str";
	writeln(["" : &str]); // this is fine :)
	writeln(["" : &s.a]); // but this is not :(
}

I've tried this with LDC 1.8.0 on Ubuntu bash for Windows, LDC 1.11.0 and 1.12.0-beta1 on Windows 10 x64 directly; all with the same result.

Since dmd has no problem with it, I'm enclined to think this code shouldn't pose any problem.
I have no error message or backtrace, the compilation (or linking ?) silently fails with an exit code of 1.
I've tried switching the linker to ld.bfd, ld.gold and lld on Ubuntu bash, to no avail.

Where would you start to debug this ? I'm ready to try and find the cause (although I have no knowledge about compilers), especially if for some reason I'm the only one who can reproduce this.

@kinke
Copy link
Member

kinke commented Sep 28, 2018

Where would you start to debug this ? I'm ready to try and find the cause (although I have no knowledge about compilers), especially if for some reason I'm the only one who can reproduce this.

If LDC crashes (or exits unexpectedly), the most useful thing IMO is to run it with -vv. That'll usually show what it was doing when the erroneous behavior happened.

I can reproduce it; thx for the report. I'll fix it; explaining the cause would probably take as much time as fixing it. ;)

kinke added a commit to kinke/ldc that referenced this issue Sep 28, 2018
… gagged)

It was the only unguarded fatal() call in that file.
@kinke
Copy link
Member

kinke commented Sep 28, 2018

See #2860.

@LaurentTreguier
Copy link
Author

Thanks for the swift response and PR !

kinke added a commit to kinke/ldc that referenced this issue Sep 28, 2018
… gagged)

It was the only unguarded fatal() call in that file.
@PetarKirov
Copy link
Contributor

@LaurentTreguier note that your example triggers undefined behavior as it's prone to memory corruption - by using the -dip1000 dmd will correctly diagnose errors on both lines, see: https://run.dlang.io/is/P4hi2M. Undefined behavior means that compilers are free to do anything they want with such code - the program behavior doesn't even need to make sense, if the code compiles at all.

s and str are local variables living on main's stack frame. Taking the address of such local variable produces a pointer which is valid only for the lifetime of the variable it points to - in this case its valid right until main returns. If you copy a pointer to local variable into an object with greater lifetime (i.e. if you escape the local variable) you allow access to the variable's memory after its lifetime ends, and you may end up with a stack corruption. Associative arrays are GC allocated and as such their lifetime is infinite, in particular they usually outlive the function in which they were created.

@kinke
Copy link
Member

kinke commented Sep 29, 2018

I'd rather argue that -dip1000 erroring out in this case is a bug - it's clear that the AA won't escape (in the sense of being accessible in a meaningful way later, although GC-allocated and outliving the function), but this won't compile:

void main()
{
    S s;
    const aa = ["" : &s.a];
}

@LaurentTreguier
Copy link
Author

Ah, I see. In the case I was using this, both the struct and the associative array were in the same scope, as members of a class.

Using a GC-allocated struct satisfies dmd with -dip1000, but not ldc:

import std.stdio;

struct S
{
	string a = "a";
}

void main()
{
	S* s = new S();
	auto aa = ["" : &s.a];
	writeln(aa);
}

kinke added a commit that referenced this issue Sep 29, 2018
I assume a DotVar expression making it here implies its address is not constant.
@kinke
Copy link
Member

kinke commented Sep 29, 2018

[This is the same LDC bug and has nothing to do with -dip1000.]

@kinke kinke closed this as completed Sep 29, 2018
@PetarKirov
Copy link
Contributor

PetarKirov commented Sep 29, 2018

I'd rather argue that -dip1000 erroring out in this case is a bug [..]

I agree in the sense that your and mine programs exibit what could be classified as a rejects-valid dmd bug, though the frontend bails out when it sees pointer to local memory escaped into a GC-allocated object as it can't prove that it's safe in the general case. In the original example the AAs were passed to another function and if that function was not a template it could store a reference to the AA or its value and pass it to another thread, without the compiler being able to see through the code.

(Of course the LDC bug is unrelated to -dip1000, I was just pointing out there may be issues with code like this.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants