[cfe-commits] [PATCH] Implement C++11 in-class member initializers

Douglas Gregor dgregor at apple.com
Mon Jun 13 08:22:58 PDT 2011


On Jun 11, 2011, at 10:26 AM, Richard Smith wrote:

> Hi!
> 
> Thanks for the review. Committed, along with the requested changes and
> some extra tests, in r132878.

Thanks!

> On Fri, June 10, 2011 19:06, Douglas Gregor wrote:
>> On May 23, 2011, at 5:48 PM, Richard Smith wrote:
>> +  if (FD->getType()->isDependentType() || InitExpr->isTypeDependent()) {
>> +    // We can't check dependent initializers until instantiation.
>> +
>> +    // Erase any temporaries within this evaluation context; we're not
>> +    // going to track them in the AST, since we'll be rebuilding the
>> +    // ASTs during template instantiation.
>> +    ExprTemporaries.erase(
>> +      ExprTemporaries.begin() + ExprEvalContexts.back().NumTemporaries,
>> +      ExprTemporaries.end());
>> 
>> 
>> Rather than directly poking at ExprTemporaries, please just move the
>> MaybeCreateExprWithCleanups() call (and subsequent Invalid check) out of
>> the next "else" block, so we perform the corresponding checking (e.g.,
>> for destructors) in all cases. Plus, we want as few places as possible to
>> modify ExprTemporaries.
> 
> Done. This approach was inspired by the corresponding code in
> Sema::BuildBaseInitializer and Sema::BuildMemberInitializer; should we
> apply a similar fix there too?

Yeah, that'd be a good idea.

>> -  // If we're not in an instance method, error out.
>> -  CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(DC);
>> -  if (!method || !method->isInstance())
>> -    return 0;
>> +  QualType ThisTy;
>> +  if (CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(DC)) {
>> +    if (method && method->isInstance())
>> +      ThisTy = method->getThisType(Context);
>> +  } else if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(DC)) {
>> +    // C++0x [expr.prim]p4:
>> +    //   Otherwise, if a member-declarator declares a non-static data
>> member +    // of a class X, the expression this is a prvalue of type
>> "pointer to X"
>> +    // within the optional brace-or-equal-initializer.
>> +    if (CurScope->getFlags() & Scope::ThisScope)
>> +      ThisTy = Context.getPointerType(Context.getRecordType(RD));
>> +  }
>> 
>> 
>> Please don't access CurScope directly... it's parser state that may not
>> represent the current state of Sema (e.g., because Sema could be in the
>> middle of instantiation). Instead, Sema routines driven by the parser
>> should pass a Scope* through to tryCaptureCXXThis(). TreeTransform, on
>> the other hand, will probably need to just instantiate the type of
>> CXXThisExpr rather than calling tryCaptureCXXThis(), since we won't have
>> a useful Scope*.
> 
> I've switched to using getScopeForContext; this also fixes an issue where
> 'this' was unavailable in scopes nested within in-class initializers (for
> instance, within blocks). I hope that's acceptable; if not, I'll fix with
> a subsequent commit.

Yeah, that's fine.

> This also exposed an issue in parsing: I was creating an unnecessary class
> scope nested within the actual class scope when parsing initializers, in
> order to update the flags to include ThisScope. I've modified the code to
> update the flags on the existing class scope.

Ah, much better.

>> // Mark that we're closing on 'this' in all the block scopes, if
>> applicable. -  for (unsigned idx = FunctionScopes.size() - 1;
>> -       isa<BlockScopeInfo>(FunctionScopes[idx]);
>> -       --idx)
>> -    cast<BlockScopeInfo>(FunctionScopes[idx])->CapturesCXXThis = true;
>> +  if (!ThisTy.isNull())
>> +    for (unsigned idx = FunctionScopes.size() - 1;
>> +         isa<BlockScopeInfo>(FunctionScopes[idx]);
>> +         --idx)
>> +      cast<BlockScopeInfo>(FunctionScopes[idx])->CapturesCXXThis = true;
>> 
>> 
>> We should only be doing this when 'this' is captured because we're in a
>> CXXMethodDecl. We might incorrectly think we're capturing the outer
>> 'this' in crazy code such as:
>> 
>> 
>> struct X { void f() { ^ {
>> struct Nested { Nested *ptr = this; }; }
>> };
>> };
> 
> I agree with your observation, but not the proposed fix: we want to mark
> 'this' as captured in cases such as this:
> 
> struct X {
>  X *(^f)() = ^{ return this; }
> };
> 
> I've fixed this by only marking 'this' as captured in the block scopes we
> skipped when scanning outwards for the containing method or class decl.

Yes, that's the right fix. Thanks!

> Testing for that exposed a couple of CodeGen issues with the interaction
> of 'this' in in-class member initializers and blocks, which I've fixed.
> The interesting such issue was that there is (apparently) no defined name
> mangling for blocks in in-class member initializers. Aside from an assert,
> the code was mangling the above block as if the struct were written as
> follows:
> 
> struct X {
>  X() : f(^{ return this; }) {}
>  X *(^f)();
> };
> 
> That doesn't seem particularly unreasonable to me, so I've just updated
> the assert.

That seems fine to me.

One step closer to '0x!

	- Doug





More information about the cfe-commits mailing list