Since interest in and concerns about allowing 'auto' non static data members has been expressed (
) - I thought I'd hack together a quick implementation (based entirely on Richard smith's Idea:
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?)