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

Faisal Vali faisalv at gmail.com
Sat May 18 09:22:53 PDT 2013


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.

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'
      g(s.mi);   //  #2 what about this?
      s.mi;        // #3 and this?
      c ? s.mi : r.mi; // #4 and this?
      g( c ? s.mi : r.mi ); // #5
  };
}

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.

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?

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/02fa594d/attachment.html>


More information about the cfe-dev mailing list