A prototype implementation of 'auto' NSDMI's for exploration purposes (clang-based)

267 views
Skip to first unread message

Faisal Vali

unread,
Nov 27, 2013, 2:13:50 PM11/27/13
to c++st...@accu.org, std-pr...@isocpp.org
Hi,
  Since interest in and concerns about allowing 'auto' non static data members has been expressed (http://accu.org/cgi-bin/wg21/message?wg=core&msg=23223 & http://accu.org/cgi-bin/wg21/message?wg=core&msg=23496) - I thought I'd hack together a quick implementation (based entirely on Richard smith's Idea: http://accu.org/cgi-bin/wg21/message?wg=ext&msg=14418) to get a better sense of some of the challenges.

What I've managed to kludge so far (and I make no claims of correctness or viability of the approach since neither Richard nor Doug have had a chance to review the code) is here:
  - https://github.com/faisalv/clang/commits/auto-nsdmi-llvm-r195138%2811-19-13%29

As Richard had suggested in his post, all name lookup and type deduction for 'auto' is done at the closing brace (that is all class names have been seen).   In the clang patch above, this is done just before some semantic checks are performed on all Field members and the class is marked "complete" (so layout can be calculated).  The auto member's initializer is always parsed  (albeit, delayed until the closing brace - but in templates this could result in a hard error even if the initializer is not used for initialization - but with clang this occurs even for non auto members, right?).

The test file (to get a sense of the constructs that work) is here:
https://github.com/faisalv/clang/blob/auto-nsdmi-llvm-r195138%2811-19-13%29/test/SemaCXX/cxx1z-auto-nsdmi.cpp

Essentially, things you can NOT do in the auto NSDMI
  - use sizeof/alignof on the class being defined
  - create an object of the type being defined
  - refer to any auto members in in-class static member initializers (class is not complete in them)
  - refer to one auto member before its lexical definition in another auto member's initializer, or in areas where the class is not marked-complete (decltype within member function return types).
  - refer to any member functions with deduced return types (although in clang you can not do this in non-auto members currently - is this a bug or do we intend to support:
     struct X { auto f() { return 0; } int i = f(); };

Stuff you can do as long as you don't violate the above:
  - refer to other members/mem_funs defined before or after the auto member 
  - capture 'this' in class-level lambdas.
 
For e.g. the following works:
 struct X { //expected-note{{implicit default constructor}}
    auto a1 = sizeof(this);
    auto& self = *this;
    auto a2 = this->mem; //expected-warning {{uninitialized}}
    auto *a3 = this;
    auto a4 = a3;
    auto a5 = mem_fun(); // double
    auto a6 = const_cast<const X*>(this)->mem_fun(); // char   
    auto L = [this, &r = *this,  p = this] (auto a) {
          return r(a, this->self, p->a2);
    };
    auto a7 = ([=](auto a) { return a->a1 + a1; })(this);
    auto L2 = [=](auto a) { return self(a, *this, 3); };
    int mem = 5;
    double mem_fun() { return 3.14; }
    char mem_fun() const { return 'a'; }
    template<class T> bool operator()(T t, X&, int) { return true; }
  };

I am quite confident there are bugs, and issues I have not thought about - but I just wanted to see where Richard's idea could start to take us.

Any thoughts on whether it is worth spending more time on this? Any concerns about this direction? Any obvious cases that have been overlooked? Is there enough interest to warrant a paper (would anyone be willing to help write one if that is the case?)

Thanks!

Faisal Vali

[I believe this certainly increases the potential for ODR-violations - but last i heard, google was toying around with the idea of a static-analysis tool to detect odr-violations? http://clang-developers.42468.n3.nabble.com/design-for-an-accurate-ODR-checker-with-clang-td4033150.html any updates on this?]. 

Ville Voutilainen

unread,
Nov 27, 2013, 2:31:42 PM11/27/13
to std-pr...@isocpp.org, c++st...@accu.org
On 27 November 2013 21:13, Faisal Vali <fai...@gmail.com> wrote:
> Any thoughts on whether it is worth spending more time on this? Any concerns
> about this direction? Any obvious cases that have been overlooked? Is there
> enough interest to warrant a paper (would anyone be willing to help write
> one if that is the case?)


+1 certainly interested in having this, willing to help with a paper.

Richard Smith

unread,
Nov 27, 2013, 3:54:43 PM11/27/13
to std-pr...@isocpp.org, c++st...@accu.org
On Wed, Nov 27, 2013 at 11:13 AM, Faisal Vali <fai...@gmail.com> wrote:
Hi,
  Since interest in and concerns about allowing 'auto' non static data members has been expressed (http://accu.org/cgi-bin/wg21/message?wg=core&msg=23223 & http://accu.org/cgi-bin/wg21/message?wg=core&msg=23496) - I thought I'd hack together a quick implementation (based entirely on Richard smith's Idea: http://accu.org/cgi-bin/wg21/message?wg=ext&msg=14418) to get a better sense of some of the challenges.

What I've managed to kludge so far (and I make no claims of correctness or viability of the approach since neither Richard nor Doug have had a chance to review the code) is here:
  - https://github.com/faisalv/clang/commits/auto-nsdmi-llvm-r195138%2811-19-13%29

As Richard had suggested in his post, all name lookup and type deduction for 'auto' is done at the closing brace (that is all class names have been seen).   In the clang patch above, this is done just before some semantic checks are performed on all Field members and the class is marked "complete" (so layout can be calculated).  The auto member's initializer is always parsed  (albeit, delayed until the closing brace - but in templates this could result in a hard error even if the initializer is not used for initialization - but with clang this occurs even for non auto members, right?).

It does, but per discussion in core, it should not -- default initializers should only be instantiated when they're needed (see core issue 1396). I don't see a problem with eagerly instantiating them when we complete the class, for each non-static data member whose type contains a placeholder.
 
The test file (to get a sense of the constructs that work) is here:
https://github.com/faisalv/clang/blob/auto-nsdmi-llvm-r195138%2811-19-13%29/test/SemaCXX/cxx1z-auto-nsdmi.cpp

Essentially, things you can NOT do in the auto NSDMI
  - use sizeof/alignof on the class being defined
  - create an object of the type being defined
  - refer to any auto members in in-class static member initializers (class is not complete in them)
  - refer to one auto member before its lexical definition in another auto member's initializer, or in areas where the class is not marked-complete (decltype within member function return types).
  - refer to any member functions with deduced return types (although in clang you can not do this in non-auto members currently - is this a bug or do we intend to support:
     struct X { auto f() { return 0; } int i = f(); };

This is a very messy area; the core language doesn't specify the circumstances under which one delay-parsed entity can refer to another (this shows up in a number of other guises, such as calling a constexpr function in a constant expression in an exception specification, or calling a function with a deduced return type in a default argument). Last time I looked at this, each compiler did something different here. This seems worthy of a core issue.
 
Stuff you can do as long as you don't violate the above:
  - refer to other members/mem_funs defined before or after the auto member 
  - capture 'this' in class-level lambdas.
 
For e.g. the following works:
 struct X { //expected-note{{implicit default constructor}}
    auto a1 = sizeof(this);
    auto& self = *this;
    auto a2 = this->mem; //expected-warning {{uninitialized}}
    auto *a3 = this;
    auto a4 = a3;
    auto a5 = mem_fun(); // double
    auto a6 = const_cast<const X*>(this)->mem_fun(); // char   
    auto L = [this, &r = *this,  p = this] (auto a) {
          return r(a, this->self, p->a2);
    };
    auto a7 = ([=](auto a) { return a->a1 + a1; })(this);
    auto L2 = [=](auto a) { return self(a, *this, 3); };
    int mem = 5;
    double mem_fun() { return 3.14; }
    char mem_fun() const { return 'a'; }
    template<class T> bool operator()(T t, X&, int) { return true; }
  };

I am quite confident there are bugs, and issues I have not thought about - but I just wanted to see where Richard's idea could start to take us.

Is the class regarded as complete when you parse the default initializers? If so, what happens if one of them requires knowledge of the class layout? For instance:

template<int> struct something { typedef int type; };

struct A {
  int x;
  auto y = something<offsetof(A, z)>::type(); // will need layout here
  int z;
};

... or ...

template<int*> struct something { typedef int type; };

template<typename T> auto stuff() {
  static T t;
  constexpr int *p = &t.z; // may need layout here
  return typename something<p>::type();
}

struct A {
  int x;
  auto y = stuff<A>();
  int z;
};

Any thoughts on whether it is worth spending more time on this? Any concerns about this direction? Any obvious cases that have been overlooked? Is there enough interest to warrant a paper (would anyone be willing to help write one if that is the case?)

Thanks!

Faisal Vali

[I believe this certainly increases the potential for ODR-violations - but last i heard, google was toying around with the idea of a static-analysis tool to detect odr-violations? http://clang-developers.42468.n3.nabble.com/design-for-an-accurate-ODR-checker-with-clang-td4033150.html any updates on this?]. 

--
 
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

Faisal Vali

unread,
Nov 27, 2013, 4:22:21 PM11/27/13
to std-pr...@isocpp.org, c++st...@accu.org
It is regarded as complete for purposes of layout when you parse non auto default initializers - but not complete for purposes of layout when you are parsing default initializers for auto members (although the sema::ActOnCXXInClassMemberInitializer hook, which checks the initialization conversions, lval-to-rval etc. is done once the class is marked complete, with the rest of the fields, in the order they were all declared).

 
template<int> struct something { typedef int type; };

struct A {
  int x;
  auto y = something<offsetof(A, z)>::type(); // will need layout here
  int z;
};

... or ...

template<int*> struct something { typedef int type; };

template<typename T> auto stuff() {
  static T t;
  constexpr int *p = &t.z; // may need layout here
  return typename something<p>::type();
}

struct A {
  int x;
  auto y = stuff<A>();
  int z;
};


Your examples generate the following error messages:

template<int*> struct something { typedef int type; };

template<typename T> auto stuff() {
  static T t;  //expected-error{{incomplete type}}

  constexpr int *p = &t.z; // may need layout here
  return typename something<p>::type();
}

struct A { //expected-note{{not complete}}
  int x;
  auto y = stuff<A>(); //expected-note{{instantiation}}
  int z;
};
namespace offsetof_test {

#define offsetof(t,  d) __builtin_offsetof(t, d)

template<int> struct something { typedef int type; };

struct A { //expected-note{{not complete}}
  int x;
  auto y = something<offsetof(A, z)>::type{}; //expected-error{{incomplete type}}\
        //expected-error{{expected ';'}}
  int z;
};

}

thanks!

John Spicer

unread,
Nov 27, 2013, 4:47:13 PM11/27/13
to c++st...@accu.org, std-pr...@isocpp.org
I have to say that the more this is discussed, the more I think we should not make a change to allow this use of auto.

It introduces a significant set of special cases, which require a lot more to understand than the simple prohibition of auto in that context.

It also changes the rules for when NSDMIs are instantiated in class templates, which seems like an unfortunate interaction.

It seems like a lot of added complexity for users for what is, in my opinion, a minimal benefit.

John.
Reply all
Reply to author
Forward
0 new messages