Topic: proposals for "static if" need to carefully attend to two-phase
Author: "Jay Freeman (saurik)"<saurik@saurik.com>
Date: Fri, 17 Feb 2012 08:23:48 -0800 (PST)
Raw View
So, I have spent most of the day sitting around trying to implement "static if" in GCC (based on N3329 and N3322), and actually got quite a bit of its base functionality working.
However, GCC simply has such tight coupling of the parser with the semantics that I haven't been able to get it working inside of a template when its contents includes a declaration.
While deciding how much I cared about doing this today (and thereby whether I was willing to sit around and stubbornly refactor anything that got in my way), I asked myself why it was so coupled, and I quickly remembered some reasons.
template<typename T>
struct C {
int f() {
return q;
}
};
That code is supposed to be denied even before it is instantiated, as it simply cannot be instantiated: "q" is not a dependent name, and thereby must be looked up "now". GCC happens to implement this while parsing (probably so as to make template/typename cases easier).
I then decided to come up with a similar case that used static if in a way that caused the same underlying parser challenge. The question then becomes: is the following code supposed to be allowed (assuming, of course, that C is never instantiated with something where sizeof(T) != 4)?
template<typename T>
struct C {
int f() {
static if (sizeof(T) == 4) {
int q(0);
}
return q;
}
};
In essence, I believe the concept of two-phase name lookup has to be carefully examined and the static if proposals need to be carefully worded to take it into account.
Once you decide that this is now somehow related to dependent names, you can cruft worse examples:
int q(10);
template<typename T>
struct C {
static if (sizeof(T) == 4) {
int q;
C() : q(20) {}
}
int f() {
return q;
}
};
With this code, what should f() return? I do not think it is obvious that it should ever return 20, at least depending on how one wants to interpret dependence in this context.
For comparison, here is another case (without static if) of a bare identifier being dragged into a specific lookup due to the name not being type dependent, and in this case the initial independent parse always wins, giving "4 4".
int q(4);
struct Q {
int q;
Q() : q(10) {}
};
struct M {
};
template<typename T>
struct C : T {
int f() {
return q;
}
};
int main() {
C<Q> q;
C<M> m;
printf("%u %u\n", q.f(), m.f());
return 0;
}
Of course, calculating what names might be considered dependent on the template parameters in this context (which is an option: make any declaration found inside of a static if type-dependent) outright precludes the recommendation to throw away tokens on the unused branch.
In case anyone is curious "how did this work in D, where this supposedly works", it turns out that D doesn't have this semantic issue as it does not have two-phase name lookup. I installed gdc-4.6 and verified the following code, equivalent to the previous example, prints "10 4".
import std.stdio;
int q = 4;
class Q {
int q;
this() { q = 10; }
};
class M {
};
template C(T) {
class C : T {
int f() {
return q;
}
} }
int main() {
C!(Q) q = new C!(Q);
C!(M) m = new C!(M);
printf("%u %u\n", q.f(), m.f());
return 0;
}
So, the
Sincerely,
Jay Freeeman (saurik)
saurik@saurik.com
P.S.: I'm sorry if this e-mail is going to a useless destination. I am only sending e-mail to this list as Andrei seemed to believe that a Stack Overflow question regarding something unrelated should have made it clear to me that questions like this should go to this newsgroup and not to his e-mail address.
--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]