D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 22239 - Can't migrate from postblits if they are used without frame pointer
Summary: Can't migrate from postblits if they are used without frame pointer
Status: NEW
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P3 normal
Assignee: No Owner
URL:
Keywords:
: 22499 (view as issue list)
Depends on:
Blocks:
 
Reported: 2021-08-25 13:08 UTC by Ate Eskola
Modified: 2024-12-13 19:18 UTC (History)
4 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Ate Eskola 2021-08-25 13:08:25 UTC
So, postblits are considered a legacy construct that should be migrated to copy constructors. However, there is an instance where that cannot be done:

---
struct PostBlitted(alias al){
  this(this){}
  void x(){} //a member function so that an instance contains a frame pointer
}

struct CopyConstructed(alias al){
  this(ref typeof(this)){};
  void x(){} //a member function so that an instance contains a frame pointer
}

void main(){
  PostBlitted!(x => x) a;
  CopyConstructed!(x => x) b;

  a.f; //compiles
  b.f; //does not
}

void f(T)(T x){
  T y = x;
}
---

There should be a way to define a copy constructor so that it works even when the caller does not have a frame pointer for the new struct instance.
Comment 1 RazvanN 2021-08-26 10:01:08 UTC
This affects normal constructors as well (copy constructors are just a particularization of a normal constructor):

struct Normal(alias al) 
{
    this(int a) {}
    void x() {}
}
  
void main()
{
    Normal!(x => x) a;
  
    a.f;
}
  
void f(T)(T x)
{ 
    T y = 6;                                                                                                                         
}
Comment 2 RazvanN 2021-08-26 10:17:34 UTC
Hmmm, this code fails also:

struct PostBlitted(alias al) 
{                                                                                                                                  
  this(this){}
  void x(){} //a member function so that an instance contains a frame pointer
}
  
void main()
{
  PostBlitted!(x => x) a;
  a.f;
}
  
void f(T)(T x)
{
    T y;
}
Comment 3 Ate Eskola 2021-08-26 12:21:28 UTC
That last example is supposed to fail. `PostBlitted!(x => x)` contains a hidden context pointer to `main` to construct, so they can be default-initialized only at `main`. Not at `f`.

(It could be argued that no frame pointer should be needed because `x => x` does not use any variables from `main`, but that's a different issue)

Default blittings, and thus postblits, work, because the hidden context pointer gets copied from the original instance.

But I can't come up with a way to do the same with a copy constructor. Well, I suppose I could devise a workaround with `static opCall`, but we're supposed to replace postblits with copy constructors, not with hackish workarounds.
Comment 4 RazvanN 2021-11-30 10:05:33 UTC
*** Issue 22499 has been marked as a duplicate of this issue. ***
Comment 5 Jonathan M Davis 2024-03-28 20:43:29 UTC
When trying to add a copy constructor to a type in an existing code base, I've been seeing a lot of errors about not having a frame pointer (particularly from std.algorithm code), which has been incredibly annoying to deal with, and it has made no sense to me that a frame pointer would even be needed, since the only difference is that a copy constructor has been added (though honestly, the whole frame pointer stuff is incredibly confusing in general). So, I'm _guessing_ that this is the issue that I've been hitting, but man, it would be nice if the compiler didn't decide that it needed a frame pointer that it can't have just because a type that's involved got a copy constructor added to it.
Comment 6 Walter Bright 2024-04-02 05:00:41 UTC
The reason for the frame pointer is:

void test(int i)
{
    struct S
    {
        int x;
        int f() { return x + i; } // reference to `i`
    }
    pragma(msg, S.sizeof); // prints 16
}

so member functions can access variables in the function the struct is nested in. To avoid this, use `static`:

void test(int i)
{
    static struct S
    {
        int x;
        int f() { return x + i; }
    }
    pragma(msg, S.sizeof); // prints 4
}

test2.d(7): Error: function `test2.test.S.f` cannot access variable `i` in frame of function `test2.test`
test2.d(2):        `i` declared here

The compiler does a conservative guess as to whether a frame pointer is needed. A non-static member function will trip that. The compiler does not look at the body of the function to determine this, because the body may be defined elsewhere.

The alias parameter also trips the "needs a frame pointer" for similar reasons.

To resolve the problem, declare the template as `static`:

static struct PostBlitted(alias al){
  this(this){}
  void x(){} //a member function so that an instance contains a frame pointer
}

static struct CopyConstructed(alias al){
  this(ref typeof(this)){};
  void x(){} //a member function so that an instance contains a frame pointer
}

The fix for this bug should be to point this out in the spec.
Comment 7 dlangBugzillaToGithub 2024-12-13 19:18:08 UTC
THIS ISSUE HAS BEEN MOVED TO GITHUB

https://github.com/dlang/dmd/issues/18053

DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB