rjmccall added inline comments.
================ Comment at: include/clang/AST/Type.h:1108 + PCK_ARCStrong, // objc strong pointer. + PCK_Struct // non-trivial C struct. + }; ---------------- These should all be /// documentation comments, and they mostly shouldn't talk about fields since this is a query on QualType, not FieldDecl. I would suggest something like: for Trivial - The type does not fall into any of the following categories. Note that this case is zero-valued so that values of this enum can be used as a boolean condition for non-triviality. for VolatileTrivial - The type would be trivial except that it is volatile-qualified. Types that fall into one of the other non-trivial cases may additionally be volatile-qualified. for ARCStrong - The type is an Objective-C retainable pointer type that is qualified with the ARC __strong qualifier. for Struct - The type is a struct containing a field whose type is not PCK_Trivial. Note that a C++ struct type does not necessarily match this; C++ copying semantics are too complex to express here, in part because they depend on the exact constructor or assignment operator that is chosen by overload resolution to do the copy. ================ Comment at: include/clang/AST/Type.h:1121 + /// after it is moved, as opposed to a truely destructive move in which the + /// source object is placed in an uninitialized state. + PrimitiveCopyKind isNonTrivialToPrimitiveDestructiveMove() const; ---------------- "truly" Hmm. Now that I'm thinking more about it, I'm not sure there's any point in tracking non-triviality of a C++-style destructive move separately from the non-triviality of a copy. It's hard to imagine that there would ever be a non-C++ type that primitively has non-trivial copies but trivial C++-style moves or vice-versa. Type-based destructors imply that the type represents some kind of resource, and a C++-style move will always be non-trivial for resource types because ownership of the resource needs to be given up by the old location. Otherwise, a type might be non-trivial to copy but not destroy because there's something special about how it's stored (like volatility), but it's hard to imagine what could possibly cause it to be non-trivial to destroy but not copy. If we were tracking non-triviality of an *unsafe* destructive move, one that leaves the source in an uninitialized state, that's quite different. I think there are three reasonable options here: - Ignore the argument I just made about the types that we're *likely* to care about modeling and generalize your tracking to also distinguish construction from assignment. In such an environment, I think you can absolutely make an argument that it's still interesting to track C++-style moves separately from copies. - Drop the tracking of destructive moves completely. If you want to keep the method around, find, but it can just call `isNonTrivialToPrimitiveCopy()`. - Change the tracking of *destructive* moves to instead track *deinitializing* moves. The implementation would stop considering `__strong` types to be non-trivial to move. But as things stand today, I do not see any point in separately tracking triviality of C++-style destructive moves. ================ Comment at: lib/AST/Type.cpp:2231 + + Qualifiers::ObjCLifetime Lifetime = getQualifiers().getObjCLifetime(); + if (Lifetime == Qualifiers::OCL_Strong) ---------------- You can re-use this call to getQualifiers() in the check for `volatile` below. ================ Comment at: lib/AST/Type.cpp:3945 + if (RT->getDecl()->isNonTrivialToPrimitiveDestroy()) + return DK_nontrivial_c_struct; + ---------------- Please combine this check with the getAsCXXRecordDecl() check below. That is, (1) check whether it's a record type once, and if so, (2) it's a CXXRecordDecl, ask about its destructor, and (3) if it's not a CXXRecordDecl, ask whether it's non-trivial to primitive destroy. ================ Comment at: lib/CodeGen/CGBlocks.cpp:1779 + break; } ---------------- This entire switch can be within an `#ifndef NDEBUG` if you really feel it's important to assert. ================ Comment at: lib/CodeGen/CGBlocks.cpp:1792 + return std::make_pair(BlockCaptureEntityKind::BlockObject, + getBlockFieldFlagsForObjCObjectPointer(CI, T)); ---------------- I think you should leave the byref case of that function here, but you can make it local to this if-clause. ================ Comment at: lib/CodeGen/CGDecl.cpp:1421 + destroyer = CodeGenFunction::destroyNonTrivialCStruct; + cleanupKind = getARCCleanupKind(); + break; ---------------- rjmccall wrote: > rjmccall wrote: > > ahatanak wrote: > > > rjmccall wrote: > > > > This can only be getARCCleanupKind() if it's non-trivial to destroy > > > > solely because of __strong members. Since the setup here is > > > > significantly more general than ARC, I think you need to default to the > > > > more-correct behavior; if you want to add a special-case check for only > > > > __strong members, you ought to do that explicitly. > > > I added a DestructionKind (DK_c_struct_strong_field) that is used just > > > for structs that have strong fields (but doesn't have weak fields). > > Sure, that's a simple enough way to do it, and I think it's fairly > > warranted. > Okay, since you don't have a DK_c_struct_strong_field anymore, you either > need to stop using getARCCleanupKind() or do the test for just __strong > fields. Ping about this again. ================ Comment at: lib/CodeGen/CGDecl.cpp:1423 + if (capturedByInit) + drillIntoBlockVariable(*this, lvalue, cast<VarDecl>(D)); + ---------------- ahatanak wrote: > rjmccall wrote: > > The "delay" is that we generally handle `__block` captures within > > initializers by delaying the drill-into until we're ready to actually store > > into the variable. Not delaying has the advantage of allowing us to just > > project out the stack storage instead of actually chasing the forwarding > > pointer, but this isn't safe if the forwarding pointer might no longer > > point to the stack storage, which is what happens when a block capturing a > > `__block` variable is copied to the heap. > > > > Delaying the drill-into is easy when the stores can be completely separated > > from the initializer, as is true for scalar and complex types. It's not > > easy for aggregates because we need to emit the initializer in place, but > > it's possible that a block capturing the `__block` variable will be copied > > during the emission of that initializer (e.g. during a later element of a > > braced-init-list or within a C++ constructor call), meaning that we might > > be copying an object that's only partly initialized. This is particularly > > dangerous if there are destructors involved. > > > > Probably the best solution is to forbid capturing `__block` variables of > > aggregate type within their own initializer. It's possible that that might > > break existing code that's not doing the specific dangerous things, though. > > > > The more complex solution is to make IRGen force the `__block` variable to > > the heap before actually initializing it. (It can do this by directly > > calling `_Block_object_assign`.) We would then initialize the heap copy > > in-place, safely knowing that there is no possibility that it will move > > again. Effectively, the stack copy of the variable would never exist as an > > object; only its header would matter, and only enough to allow the runtime > > to "copy" it to the heap. We'd have to hack the __block variable's copy > > helper to not try to call the move-constructor, and we'd need to suppress > > the cleanup that destroys the stack copy of the variable. We already push > > a cleanup to release the `__block` variable when it goes out of scope, and > > that would stay. > Ah I see. I'll see if I can implement the first solution you suggested in a > separate patch. If it turns out that doing so breaks too much code, we can > consider implementing the second solution. > > Is this something users do commonly? > There's an idiom of copying `__block` variables of *block pointer* type in their own initializers, as a way of constructing a recursive block. I don't know of any reason why someone would intentionally do it for some random struct type, but that's always a risk when locking down on something retroactively. Anyway, I agree that we can experiment to see if locking down on aggregate __block captures causes any actual problems. ================ Comment at: lib/CodeGen/CGExprAgg.cpp:255 + Ty.isDestructedType() == QualType::DK_nontrivial_c_struct) + CGF.pushDestroy(Ty.isDestructedType(), src.getAggregateAddress(), Ty); + ---------------- ahatanak wrote: > rjmccall wrote: > > Hrm. Okay, I guess. This is specific to ignored results because otherwise > > we assume that the caller is going to manage the lifetime of the object? > Yes, this is specific to ignored results. This is needed because otherwise > function return values that are ignored won't get destructed by the caller. Okay. I'm just a little worried about what this implies about when we push destructor cleanups in general. I know it's rather ad-hoc for C++ destructors right now. ================ Comment at: lib/CodeGen/CGNonTrivialStruct.cpp:193 + + TrivialFieldIsVolatile |= FT.isVolatileQualified(); + if (Start == End) ---------------- ahatanak wrote: > rjmccall wrote: > > I feel like maybe volatile fields should be individually copied instead of > > being aggregated into a multi-field memcpy. This is a more natural > > interpretation of the C volatile rules than we currently do. In fact, > > arguably we should really add a PrimitiveCopyKind enumerator for volatile > > fields (that are otherwise trivially-copyable) and force all copies of > > structs with volatile fields into this path precisely so that we can make a > > point of copying the volatile fields this way. (Obviously that part is not > > something that's your responsibility to do.) > > > > To get that right with bit-fields, you'll need to propagate the actual > > FieldDecl down. On the plus side, that should let you use > > EmitLValueForField to do the field projection in the common case. > I added method visitVolatileTrivial that copies volatile fields individually. > Please see test case test_copy_constructor_Bitfield1 in > test/CodeGenObjC/strong-in-c-struct.m. Okay, great! I like the name. Does this mean we're now copying all structs that contain volatile fields with one of these helper functions? If so, please add a C test case testing just that. Also, you should retitle this review and stress that we're changing how *all* non-trivial types are copied, and that that includes both volatile and ARC-qualified fields. ================ Comment at: lib/CodeGen/CGNonTrivialStruct.cpp:110 + case QualType::PCK_ARCStrong: + return asDerived().visitStrong(FT, std::forward<Ts>(Args)...); + case QualType::PCK_Struct: ---------------- Please call these methods visitARCStrong to go with the new PCK name. https://reviews.llvm.org/D41228 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits