This is an archive of the discontinued LLVM Phabricator instance.

Implement the likely resolution of core issue 253.
ClosedPublic

Authored by thakis on Jan 25 2016, 12:25 PM.

Details

Reviewers
rsmith
Summary

C++11 requires const objects to have a user-provided constructor, even for classes without any fields. DR 253 relaxes this to say "If the implicit default constructor initializes all subobjects, no initializer should be required."

clang is currently the only compiler that implements this C++11 rule, and e.g. libstdc++ relies on something like DR 253 to compile in newer versions. So this patch makes it possible to build code that says `const vector<int> v;' again when using libstdc++5.2 and _GLIBCXX_DEBUG (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60284).

Fixes PR23381.

Diff Detail

Event Timeline

thakis updated this revision to Diff 45901.Jan 25 2016, 12:25 PM
thakis retitled this revision from to Let clang not error out on `const std::vector<int> empty_vec;` with libstdc++5.3..
thakis updated this object.
thakis added a reviewer: rsmith.
thakis added a subscriber: cfe-commits.
rsmith edited edge metadata.Feb 8 2016, 11:43 AM

Can we implement all of DR253 instead? (That is, don't restrict this change to libstdc++'s types.) We don't have precise wording for DR253 yet, but do have the guidance that "if the implicit default constructor initializes all subobjects, no initializer should be required."

Will do, thanks! In "If the implicit default constructor initializes all subobjects, no initializer should be required", is "initializes all subobjects meant recursively? I.e. is this ok according to that language?

`
struct Inner {
  Inner() = default;
  int i;  // not initialized, but Inner has a default ctor
};

struct S {
  Inner inner;
};

const S s;
`

Will do, thanks! In "If the implicit default constructor initializes all subobjects, no initializer should be required", is "initializes all subobjects meant recursively?

I think so, or more specifically, the implicit initialization would need to be valid if each member were itself const.

struct Inner {
  Inner() = default;
  int i;  // not initialized, but Inner has a default ctor
};

struct S {
  Inner inner;
};

const S s;

This would be ill-formed because S::S() would notionally default-initialize s.inner of type const Inner, which would notionally default-initialize s.inner.i of type const int, which would be ill-formed.

thakis updated this revision to Diff 48107.Feb 16 2016, 2:04 PM
thakis edited edge metadata.

Checkpointing; not production quality. I tried implementing the general DR 253 rule, and this patch mostly does that. It currently walks all fields and all bases for every const record decl; there should probably be a cache for "are all fields obviously initialized"; probably on CXXRecordDecl. I had to change some test cases, but nothing surprising.

However, while this patch does fix const std::unordered_map<int, int> um; with libstdc++5.3, it does _not_ fix std::vector<int> v; or const std::unordered_map<int, int> um; when -D_GLIBCXX_DEBUG=1 is passed in. Investigating that case more now.

thakis updated this revision to Diff 48115.Feb 16 2016, 3:18 PM

Ok, that was just a bug. Now all libstdc++5.3 cases I tried build. Still not production-quality, but the overall direction won't change. If you want to comment on that, please do, else I'll ping this again after I've cleaned it up.

The direction here looks fine to me.

lib/Sema/SemaInit.cpp
369

I wonder if this recursion through non-inline namespaces is really warranted; can we replace the call to this function with R->isInStdNamespace()?

434–436

(Please commit this separately if you still want to make this change.)

thakis updated this revision to Diff 48248.Feb 17 2016, 3:12 PM
thakis retitled this revision from Let clang not error out on `const std::vector<int> empty_vec;` with libstdc++5.3. to Implement the likely resolution of core issue 253..
thakis updated this object.

Ok, now cleaned up and caches the "is this ok?" state on a per-record level.

thakis added inline comments.Feb 18 2016, 9:54 AM
include/clang/AST/DeclCXX.h
410 ↗(On Diff #48248)

This should be :2 of course.

rsmith added inline comments.Feb 18 2016, 11:03 AM
include/clang/AST/DeclCXX.h
405–416 ↗(On Diff #48248)

Is it worth caching this? (If so, the bit-field should occupy at most 2 bits...)

1286 ↗(On Diff #48248)

Declaring a const what?

lib/AST/DeclCXX.cpp
396–425 ↗(On Diff #48248)

For all the other fields of this kind, we compute the flag as bases and members are added to the class. It seems like we can do the same here, and all else being equal, it would be better to not deviate from the existing pattern without good reason.

The most natural way to do this would probably be to store a single flag for HasUninitializedFields, and then compute whether default initialization would leave a field uninitialized as !hasUserProvidedDefaultConstructor() && HasUninitializedFields.

403–413 ↗(On Diff #48248)

I think you should skip mutable members here, as the corresponding subobject of a const object is not itself const.

thakis updated this revision to Diff 48373.Feb 18 2016, 11:24 AM
thakis marked an inline comment as done.

address comments

thanks!

include/clang/AST/DeclCXX.h
405–416 ↗(On Diff #48248)

The motivation is that a chain of

struct A0 {};
struct A1 { const A0 a; }
struct A2 { const A1 a; }
struct A3 { const A2 a; }

needs O(n^2) without caching. Changed the bitfield to 2 bits.

1286 ↗(On Diff #48248)

Done (I think?)

lib/AST/DeclCXX.cpp
396–425 ↗(On Diff #48248)

My reasoning here was that for the vast majority of types, we'll never have a const variable of that type, so computing this for all types seemed like a waste.

thakis updated this revision to Diff 48376.Feb 18 2016, 11:35 AM

update test

lib/AST/DeclCXX.cpp
403–413 ↗(On Diff #48373)

I did this, but the test suite informs me that this contradicts the resolution of DR497: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#497 I guess 253 supersedes 497; I'll update the test.

rsmith added inline comments.Feb 18 2016, 3:25 PM
lib/AST/DeclCXX.cpp
396–425 ↗(On Diff #48376)

I don't expect this to be a measurable cost either way. I would strongly prefer to use the same pattern for all of these flags unless we have a really good reason not to.

lib/Serialization/ASTReaderDecl.cpp
1517–1562 ↗(On Diff #48376)

You should update this block too.

test/CXX/drs/dr4xx.cpp
1200 ↗(On Diff #48376)

Write this as "dr497: sup 253", and rerun www/make_cxx_dr_status to generate a new www/cxx_dr_status.html file. (You'll need to grab a cwg_index.html from the WG21 website.)

thakis updated this revision to Diff 48418.Feb 18 2016, 3:57 PM

Thanks! All done.

rsmith accepted this revision.Feb 18 2016, 4:03 PM
rsmith edited edge metadata.

Thanks, LGTM!

If it's convenient, it'd be nice to check in a new cxx_dr_status.html without your change first, as a separate commit, so it's more obvious which parts of the file are changed by this commit and which are changed by the new cwg_index.html.

This revision is now accepted and ready to land.Feb 18 2016, 4:03 PM
thakis closed this revision.Feb 18 2016, 5:57 PM

Thanks! Landed the cxx_dr_status.html update in r261295 and this change in r261297.