[cfe-dev] Implementing DR 712 (odr-use, potential results of an expression)?

Richard Smith richard at metafoo.co.uk
Sat May 18 11:42:09 PDT 2013


On Sat, May 18, 2013 at 9:22 AM, Faisal Vali <faisalv at gmail.com> wrote:

> Before I return to working on generic lambdas, I thought it might be
> useful to nail down DR 712 which reworded the definition of odr-use in
> terms of the potential results of an expression (in order to avoid
> odr-using a variable that represents a constant expression whenever we can
> get away with it).  This is relevant to lambdas, because it affects which
> variables get captured and which don't.
>
>
> Let me review how I think clang currently handles this (pre-712
> resolution  - please correct me where I err). While parsing and
> semanalyzing expressions, all variables that are usable in constant
> expressions (and so might not need to be odr-used) are stored in
> MaybeODRUseExprs (a set container) as DeclRefExprs (DRE).  Then, if an
> Lvalue-to-Rvalue conversion is performed on the DRE, it is removed from the
> set above (In UpdateMarkingForLValueToRValue).  Anything left in that set
> is marked as odr-used by ActOnFinishFullExpr.
>
>
> OK, with the above in mind, these are my initial thoughts on how to
> implement it so that not only do we handle those constant expressions that
> are lvalue-to-rvalue converted, but also those that are discarded, and also
> handle the conditional expressions and member-access expressions.
>
> The relevant hooks/callbacks/functions within clang are:
>   - UpdateMarkingForLValueToRValue
>   - IgnoredValueConversions
>
> A function i intend to write is GetPotentialResultsOfExpression: when
> passed an Expr* E, returns a vector of its potential results as
> DeclRefExpr's (and MemberExpr's??); this function should follow from  3.2
> para 2.
>
> Now in each hook, GetPotentialResults shall be called, and those
> expressions shall be removed from the MaybeODRUseExpr.
>

This seems like the right approach, although you presumably don't actually
need to build the vector and could just remove the odr-use marker directly.


> What are your thoughts on the above preliminary strategy?  I have this
> sense that Member expressions are going to require some more work, but
> we'll see...
>
> Also before I proceed any further, I was hoping to get some more clarity
> on 3.2 para 2 and para 3 - please see my queries below:
>
>
> Consider the following code:
>
>
> struct S {
>   const int mi;
>   constexpr S(int i) : mi(i) { }
> };
>
> int f(const int& );
> void g(int);
> int main() {
>   constexpr S r{5};
>   constexpr S s{r};
>   [](char c) {
>       c = s.mi;  // #1 does this odr-use 's'
>

No. Here, e = 's.mi', ex = 's' (a member of the set of potential results of
e), and e is subject to an lvalue-to-rvalue conversion.


>       g(s.mi);   //  #2 what about this?
>

Also no, because an lvalue-to-rvalue conversion is applied to s.mi.


>       s.mi;        // #3 and this?
>

Also no, because this is a discarded-value expression.


>       c ? s.mi : r.mi; // #4 and this?
>

Also no.


>       g( c ? s.mi : r.mi ); // #5
>

Also no.


>   };
> }
>
> My intial suspicion was that neither s nor r were odr-used in the lambda
> (because the compiler can figure out the value of 'mi' at its point of use,
> and use it or discard it) - but then based on Richard's analysis of the
> example below where he states that s.operator int() is an odr-use of 's', i
> am now unsure.
>

Note that member function calls aren't listed in the rules of 3.2/2. 's' is
not in the set of potential results of 's.operator int()'.


> Here is my attempt at trying to figure this out (e refers to expression,
> ex as a potentially-evaluated expression, using the language of 3.2 para 2
> and 3)
>
> In #1,
>   the variable 's' appears as a potentially evaluated expression and
> satisfies the requirements for appearing in a constant expression, and it
> is an object, and it is an element of the set of potential results of an
> expression 'e' (s.mi) and the lvalue-to-rvalue conversion is applied to
> that expression so it is not odr-used?  But what if 'e' is deemed to be 's'
> in 's.mi' - no lvalue-to-rvalue conversion is applied so it should be
> captured?
>

The rule says: "x [...] is odr-used unless [...] ex is is an element of the
set of potential results of an expression e, where [...]"

That is, if there *exists* such an 'e', then 'x' is not odr-used.

Hope that helps!


> OK, I'm going to give up on analyzing the rest, because I feel I'm missing
> something and am unable to think about this clearly.
>
> Any help or guidance will be appreciated!
>
>
>
> On Wed, May 15, 2013 at 3:21 PM, Richard Smith <richard at metafoo.co.uk>wrote:
>
>> On Tue, May 14, 2013 at 9:48 PM, Faisal Vali <faisalv at gmail.com> wrote:
>>
>>>
>>>
>>> While we're on the topic, can i ask you to clarify a few capture and
>>> constexpr
>>> questions below:
>>>
>>
>> OK, the relevant context is [basic.def.odr]p2 and p3.
>>
>>
>>> struct S {
>>>   constexpr S() {}
>>>   constexpr operator int() const { return 0; }
>>> };
>>> void fooS(S s) { }
>>>
>>> void fooInt(int i) { }
>>>
>>> void f() {
>>>   constexpr S s {};
>>>   auto L = [] (int x) {
>>>       (void) s;  // 1
>>>
>>
>> Here, the 's' expression is a discarded-value expression, and the 's'
>> variable is in the set of potential results of the 's' expression, and 's'
>> satisfies the constraints for appearing in a constant expression, so 's' is
>> not odr-used, so is not captured. We should not require a capture-default
>> here.
>>
>>
>>>       fooS(s);  // 2
>>>
>>
>> Here, 's' is implicitly passed to the implicit S::S(const S&)
>> constructor. This is neither an lvalue-to-rvalue conversion nor a
>> discarded-value expression, so 's' is odr-used and we have an error due to
>> the missing capture-default.
>>
>>
>>>       fooInt(s); // 3
>>>
>>
>> This is equivalent to 's.operator int()', which again odr-uses 's', so
>> requires a capture-default.
>>
>>
>>>   };
>>> }
>>>
>>> Should the above be ok in spite of L not having a default capture?
>>> Currently clang errors on all of them individually - is that the right
>>> behavior?
>>>
>>> Also, how would you want me to start submitting patches for commit - do
>>> you want
>>> me to break up the patch into smaller patches? - and if so, do you have
>>> any thoughts
>>> on how I might want to break up the functionality per patch?
>>>
>>
>> Smaller patches are definitely better, if you can decompose the
>> functionality into coherent smaller chunks. There are some hints on how to
>> decompose the functionality here (but this division may or may not work for
>> you):
>>
>>
>> http://clang.llvm.org/docs/InternalsManual.html#how-to-add-an-expression-or-statement
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20130518/a6ff0fc2/attachment.html>


More information about the cfe-dev mailing list