Begin forwarded message:

>> 
>> Hmm.  We’ve had a number of similar bugs in the past, and usually the fix is 
>> to enter a new expression evaluation context in the right place.  The right 
>> place here would be around the emission of the copy-initialization 
>> expression, not around the array/dictionary elements.  Doing that should be 
>> good enough to fix this in general, I think, but I’d like Doug and Richard 
>> to weigh in because there’s weirdness afoot.
>> 
>> So, the interesting code here is in BuildBlockForLambdaConversion, at the 
>> bottom of SemaLambda.cpp.  This function is used to implement the conversion 
>> of a lambda to a block pointer, which occurs (1) within the implementation 
>> of the non-standard conversion operator to block-pointer type that we add to 
>> all lambda types and (2) as a special case of applying that conversion when 
>> the source is a LiteralExpr.  Let’s call case (2) the Literal Hack.  The 
>> Literal Hack solves some problems with block memory management which would 
>> otherwise bite us, but it’s also the code path that’s causing this bug.
>> 
>> ExprResult Sema::BuildBlockForLambdaConversion(SourceLocation 
>> CurrentLocation,
>>                                                SourceLocation ConvLocation,
>>                                                CXXConversionDecl *Conv,
>>                                                Expr *Src) {
>>   …
>>    ExprResult Init = PerformCopyInitialization(
>>                       InitializedEntity::InitializeBlock(ConvLocation, 
>>                                                          Src->getType(), 
>>                                                          /*NRVO=*/false),
>>                       CurrentLocation, Src);
>>   if (!Init.isInvalid())
>>     Init = ActOnFinishFullExpr(Init.get());
>>   ...
>> 
>> This Init expression will become the copy-init expression in the block's 
>> capture of the lambda (its own capture), which is actually used in two 
>> different places.  The first is that it’s emitted during the IR-generation 
>> of the block literal expression in order to initialize the corresponding 
>> capture field; the second is that it's emitted within the block’s 
>> copy-helper function in order to initialize the destination capture field.  
>> In this latter situation, IRGen first binds the address of all the captured 
>> local variables to the corresponding field in the block.  It’s because of 
>> this second use that we generally want to enter a new expression evaluation 
>> context around emitting the copy-init expression of a capture, because an 
>> arbitrary class’s copy constructor might require arbitrary stuff in default 
>> arguments, including the killer cases that absolutely require proper 
>> tracking of cleanups.  (Ordinary C++ temporaries themselves are not 
>> problematic because we can conservatively say that any expression might have 
>> C++ cleanups without causing any lasting harm.)
>> 
>> Note that the Src expression parameter to BuildBlockForLambdaConversion 
>> becomes a part of this Init expression, and it’s already been emitted;  in 
>> the normal case, we could just pass in a std::function to generate it, but 
>> in the Literal Hack case we can’t, because we don’t know that we’re doing 
>> the Literal Hack until after we’ve fully parsed that expression and the 
>> enclosing expression and decided that we need to use the conversion 
>> operator.  So if Src could be an arbitrary expression, we’d be in deep 
>> trouble, and I think we’d need to split the two use cases and maintain two 
>> separate copy-init expressions.
>> 
>> Fortunately, it can’t.  In the normal case it’ll be a DeclRefExpr, and in 
>> the Literal Hack case it’ll be a LambdaExpr, which has some arbitrarily 
>> complex sub-expressions to do capturing but which wraps them all up as 
>> full-expressions.  So I think we can just wrap the construction in an 
>> EnterExpressionEvaluationContext, flag that there are cleanups in the new 
>> context if the lambda type has a non-trivial destructor, and then bind that 
>> up as its own full-expression.
>> 
>> This is slightly wrong from a modeling perspective because it will put the 
>> LambdaExpr inside an ExprWithCleanups, which in principle limits the 
>> lifetime of that temporary too much, but the generated code has the correct 
>> semantics because we’ll elide the lambda temporary directly into the block’s 
>> capture field, and the block's lifetime is at least as long as the lambda 
>> temporary’s.
>> 
>> So I think the right fix is basically:
>> 
>> ExprResult Init;
>> {
>>   EnterExpressionEvaluationContext evalContext(…);
>>   ExprHasCleanups = !Lambda->hasTrivialDestructor();
>>   Init = PerformCopyInitialization(…);
>>   if (!Init.isInvalid())
>>     Init = ActOnFinishFullExpr(Init.get());
>> }
>> 
>> But I’d like to know if I’m missing some way in which arbitrary 
>> subexpression structure can show up in Src.
> 
> Yeh. This change (with minor correction as attached) has the desired effect 
> of postponing cleanup code for the Literal hack until the outer-most 
> expression is 
> seen. But, I wait if there are more comments.
> 
>       
Index: lib/Sema/SemaLambda.cpp
===================================================================
--- lib/Sema/SemaLambda.cpp     (revision 217960)
+++ lib/Sema/SemaLambda.cpp     (working copy)
@@ -1572,14 +1572,20 @@
   CallOperator->setReferenced();
   CallOperator->markUsed(Context);
 
-  ExprResult Init = PerformCopyInitialization(
-                      InitializedEntity::InitializeBlock(ConvLocation, 
-                                                         Src->getType(), 
-                                                         /*NRVO=*/false),
-                      CurrentLocation, Src);
-  if (!Init.isInvalid())
-    Init = ActOnFinishFullExpr(Init.get());
+  ExprResult Init;
+  {
+    EnterExpressionEvaluationContext evalContext(*this, 
Sema::PotentiallyEvaluated);
+    ExprNeedsCleanups = !Lambda->hasTrivialDestructor();
   
+    Init = PerformCopyInitialization(
+             InitializedEntity::InitializeBlock(ConvLocation,
+                                                Src->getType(),
+                                                /*NRVO=*/false),
+                                     CurrentLocation, Src);
+    if (!Init.isInvalid())
+      Init = ActOnFinishFullExpr(Init.get());
+  }
+  
   if (Init.isInvalid())
     return ExprError();
   
Index: test/CodeGenObjCXX/lambda-in-objc-literals.mm
===================================================================
--- test/CodeGenObjCXX/lambda-in-objc-literals.mm       (revision 0)
+++ test/CodeGenObjCXX/lambda-in-objc-literals.mm       (working copy)
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10.0.0 -emit-llvm -o - %s 
-fexceptions -std=c++11 -fblocks
+// rdar://16879958
+
+@protocol NSCopying @end
+typedef unsigned long NSUInteger;
+typedef long NSInteger;
+
+@interface NSDictionary
++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id <NSCopying> 
[])keys count:(NSUInteger)cnt;
+- (void)setObject:(id)object forKeyedSubscript:(id)key;
+- (id)objectForKeyedSubscript:(id)key;
++ (id) alloc;
+- (id)initWithObjects:(const id [])objects forKeys:(const id [])keys 
count:(unsigned long)cnt;
+@end
+
+@interface NSString<NSCopying>
+@end
+
+
+@class NSFastEnumerationState;
+
+@protocol NSFastEnumeration
+
+- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state 
objects:(id [])buffer count:(NSUInteger)len;
+
+@end
+
+@interface NSNumber
++ (NSNumber *)numberWithInt:(int)value;
+@end
+
+@interface NSArray <NSFastEnumeration>
++ (id) alloc;
++ (id)arrayWithObjects:(const id [])objects count:(NSUInteger)cnt;
+@end
+
+id test_dict()
+{
+        return @{
+                @"a": [](){},
+                @"b": [](){},
+                @"c": [](){}
+        };
+}
+
+id test_arr()
+{
+        return @[
+                [](){},
+                [](){}
+        ];
+}
> 
> 

If there is no additional comment or feedback, I would like to check this in.

> - Thanks, Fariborz
> 


_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to