-
Notifications
You must be signed in to change notification settings - Fork 547
CXX-2790 Redeclare bsoncxx::v_noabi as a non-inline namespace #1066
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
kevinAlbs
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with suggested developer documentation.
entities within an ABI namespace MUST NOT be defined in terms of root namespace declarations.
Suggest noting this in CONTRIBUTING.md to document this expected pattern for developers. Example:
Referencing symbols
The library root namespaces (
mongocxxandbsoncxx) contain ABI versioned namespaces (e.g.v_noabi).In library interface and implementation (headers and sources), references to library symbols (e.g.
bsoncxx::X) must either exclude the root namespace qualifier entirely (e.g. justX) or be fully qualified with the ABI namespace included (e.g.::bsoncxx::v_noabi::X). This is to ensure that any interface defined in terms of ABI namespacevAwill never be inadvertently affected by the upgrade of a root namespace declarationvA->vB.Examples:
// header.hpp void foo (::bsoncxx::document::view view); // Not OK! Does not include ABI namespace.// header.hpp namespace bsoncxx { namespace v_noabi { namespace document { void foo (view view); // OK. Excludes root namespace qualifier. } // namespace document } // namespace v_noabi } // namespace bsoncxx// header.hpp void foo (::bsoncxx::v_noabi::document::view view); // OK. Includes ABI namespace.
vector-of-bool
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delay, but LGTM. A few open non-blocking questions:
- Is
BSONCXX_INLINEever actually needed? Everything is already "hidden", including function templates and inline class member function definitions, and there is no documentation on it, so I'm not sure what it is supposed to convey. My only experience so far is that it adds visual noise. - I'm curious how this effects auto-complete in editors. i.e. My
clangdwill complete a fragmentview_or_asbsoncxx::{document,array,string}::view_or_value, and omits theinline namespace. Will it now inject the ABI namespace as part of expansion?
The proposed upcoming changes in #1052 documents // Due to `BSONCXX_API`, all class members have non-hidden visibility by default.
class BSONCXX_API value;
class value {
// ...
// Due to `BSONCXX_INLINE`, this member function is excluded from the class'
// default non-hidden visibility and given hidden visibility instead.
BSONCXX_INLINE document::view view() const noexcept;
// ...
};
// Due to `BSONCXX_INLINE`, this member function definition is inline.
BSONCXX_INLINE document::view value::view() const noexcept { ... }There should not be a symbol for Upon further investigation, because we also set However, I am having trouble finding definitive documentation about what constitutes "inline" w.r.t.
If we are comfortable depending on
So long as the editor (or whatever underlying C++ parser it is using) is capable of identifying using-declarations and redeclaration of referenced namespaces/entities in the correct scope, I believe it should work no different to how it already does with inline namespaces. e.g. a quick test in VS Code correctly identifies autocompletion candidates for a partially-written |
Description
This PR partially resolves CXX-2790. Verified by this patch.
Although this PR and description is focused only on the bsoncxx library, many points equally apply to the mongocxx library. A followup PR will apply the same refactor to mongocxx.
This PR SHOULD NOT break source or binary compatibility. Verified by this patch (pending ABI compatibility check scripts); specifically, the source compatibility report uploaded by the
abi-compliance-checktask. Concerning the added/removedbsoncxx::detailsymbols, see the section below regarding type traits.Context
The following distinctions must be defined first:
For example, given the following initial state (assume no other overloads exist):
The following introduces a potentially source-incompatible, binary-incompatible feature without being a source-breaking or binary-breaking change (it is purely additive):
Source code that refers to
bsoncxx::fooorbsoncxx::vA::foocontinue to compile with the new library release. Binaries that expect the symbolbsoncxx::vA::foocontinue to link with the new library release. This type of change allows library maintainers to introduce new features without concern for breaking source or binary compatibility.By contrast, the following is a non-binary-breaking, potentially source-breaking change:
Source code that refers to
bsoncxx::vA::foocontinue to compile with the new library release, and binaries that expect the symbolbsoncxx::vA::foocontinue to link with the new library release. However, the meaning of source code referring tobsoncxx::foohas changed: it now refers tobsoncxx::vB::fooinstead ofbsoncxx::vA::foo. This type of change allows users to benefit from binary-incompatible features without requiring binary-breaking changes.The important takeway from these examples is that potentially source-breaking changes can be introduced as binary-incompatible features without necessarily breaking source or binary compatibility. This allows forward-progress of the library interface and provides users the Quality of Experience (QoE) of being able to benefit from incremental upgrades, while also providing incremental backward and forward compatibility in the case of source-incompatible changes via explicit ABI namespace qualification: a user may opt-into using a potentially source-incompatible feature
bsoncxx::vB::fooearly, or may opt-out of a potentially source-breaking change by continuing to usebsoncxx::vA::fooinstead.Inline ABI Namespaces
The bsoncxx library was originally designed with the intent of controlling binary compatibility via a
BSONCXX_ABI_VERSIONvariable. Both theSOVERSIONand the ABI namespace were defined in terms of this variable, such that when the ABI version number is bumped fromAtoB, the inline ABI namespace within which all bsoncxx entities are declared is also renamed fromvAtovB. This has the effect of migrating the entirety of the bsoncxx library from thevAto thevBnamespace alongside the bumpedSOVERSION.This design was considered undesirable as, in practice, it does not easily allow for the type of changes described above. This is due to the fact that an inline namespace cannot be redeclared as non-inline, and a non-inline namespace cannot be redeclared as inline. This means bsoncxx library maintainers are forced into an all-or-nothing approach to binary compatibility when considering potentially source-breaking changes:
This limitation discourages both backward and forward compatibility in three major ways:
vA::foocannot be selectively "undeclared" in the root namespace,bsoncxx::foocannot refer tovB::foowithout forcing an ABI version bump. This means users are unable to benefit from the addition ofvB::foounless they explicitly qualify their references withvB, which largely limits the utility of providing root namespace declarations (can only be exercised during binary-breaking releases, which are expected to be very infrequent).vA::foocannot be declared within a non-inlinevAnamespace until thevA->vBrename is already in-progress. Therefore, preserving binary-compatibility withvA::foo-> implies preservingvAas the inline ABI namespace -> implies preserving the current ABI version number.vAtovBin the process of an ABI version bump, all entities in thevAnamespace are redeclared in thevBnamespace regardless of their intended ABI version compatibility. This inevitably triggers a conflict between the intendedvB::fooand the formervA::foo(now incorrectly renamed tovB::foo) during the ABI version number bump. Library maintainers must then go through the laborous effort of identifying and resolving these conflicts at the time of the ABI version bump (this cannot be mitigated beforehand other than via "TODO" documentation). The difficulty of this process is proportional to the number of conflictingvBsymbols and the number ofvAsymbols that must be removed or redeclared (furthermore, these two sets of symbols are not 1-to-1!).These three bullet points overlap under a common theme: binary-incompatible features are a major pain to maintain even if they do not immediately break binary-compatibility.
Using-Declarations
To address the issues with inline namespaces above, this PR follows up on #1034 (CXX-2750) by proposing the use of using-declarations as the method to redeclare ABI namespace entities in the root namespace:
This method is believed to address all of the issues described above:
vBhas no immediate effect or conflict with existing symbols invA(it is not all-or-nothing).vAare always declared in the appropriate namespace and can be maintained independently from symbols invB(no namespace renaming shenanigans).This method facilitates both forward and backward compatibility, allows maintainers more freedom of design, and makes upgrades more accessible to users. The combination of all the above allows maintainers to selectively and incrementally migrate entities from
vAtovB, without breaking source or binary compatibility while minimizing code duplication.Furthermore, this refactor is NOT a source or binary breaking change. The identity of symbols remains unchanged, as all symbols are is still declared within their original ABI namespace (
bsoncxx::v_noabi::Xis stillbsoncxx::v_noabi::X). All references (or lack thereof) to root namespace declarations, formerly imported via inline namespaces, are now fulfilled by the explicit using-declarations. Even argument-dependent lookup (ADL) should not be affected by this refactor given correct all the relevant using-declarations are implemented.Changes
The volume of diffs in this PR is large, but the patterns applied are fairly simple in nature.
Replace
inline namespace v_noabiwithnamespace v_noabiThe primary intent of this PR. All instances of
inline namespacein the bsoncxx library are refactored to be non-inline. Due to the all-or-nothing nature of inline namespaces, this PR is forced to apply this refactor to all instances simulatenously.Add Using-Declarations for Public Interfaces
All entities intended to be part of the public API are redeclared in the corresponding root namespace via a using-declaration. For example, for
bsoncxx::document::value, the forward header looks as follows:And the normal header looks as follows:
Note: "hidden friends" do not require a using-declaration in the root namespace, as by-design they can only be found via ADL. This applies to functions and operator overloads such as
bsoncxx::document::view::operator==that do not have an explicit declaration in namespace scope.The completely separate redeclaration of the
bsoncxxnamespace is intentional to ensure the root namespace declarations are easily identifiable and grouped together. It should be plainly obvious at all times that any given entity declared in the root namespace is referring to the entity declared in this specific ABI namespace. It was deemed undesirable for this information to be mixed within ABI-specific declarations and definitions. Thus all headers now follow this approximate pattern:Note, this also has the additional side-effect of providing an interface boundary to better control exposure of public vs. private/implementation interfaces by the selective using-declaration of ABI namespace entities. For example, it is assumed (note: this assumption may be incorrect) that
bsoncxx::v_noabi::types::is_bson_view_compatibleinbsoncxx/v_noabi/bsoncxx/types/bson_value/view.hppis not intended to be part of the public API, thus it is not included in the using-declarations below, preventing its accidental/unintended use (there is nobsoncxx::types::is_bson_view_compatible).Furthermore, the impact of using-declarations and using-directives used within the
v_noabinamespaces in public headers can be somewhat mitigated in the future by being excluded in their root namespace counterparts. Their removal is not done in this PR to ensure source-compatibility, but// Deprecated.comments have been added to discourage their continued dependence/use.ABI Interface Stability
In library interface and implementation (headers and sources), all references to
bsoncxx::Xhave been refactored to either exclude the root namespace qualifier entirely (e.g. justX) or to be fully qualified with the ABI namespace included (e.g.::bsoncxx::v_noabi::X). This is a coding style that should be enforced via code review going forward: entities within an ABI namespace MUST NOT be defined in terms of root namespace declarations. This is to ensure that any interface defined in terms of ABI namespacevAwill never be inadvertently affected by the upgrade of a root namespace declarationvA->vB.For example:
Applying this rule consistently throughout the bsoncxx library contributed most to the volume of diffs in this PR.
Private and Test Interfaces
Private and test interfaces, by-design, should not affect the ABI, thus are not subject to ABI stability requirements or concerns as described above. Therefore, private and test interfaces have (mostly) been redeclared without being associated with an ABI namespace, simply due to it not being necessary (e.g.
bsoncxx/private/helpers.hh). However, if convenient, they may still be declared within an ABI namespace (e.g.bsoncxx/types/private/convert.hh).This PR takes this opportunity to replace instances of
BSONCXX_INLINEin private and test headers with a simpleinlineto avoid conflating the macro's intended usage as documenting "a public API member that is not a member of the stable ABI". This takes advantage of the CMake target propertyVISIBILITY_INLINES_HIDDENbeing set toONin the bsoncxx library CMake configuration.Type Traits
The
type_traits.hppheader is the sole exception to the rules described above. It was observed that the use of type traits are, with one sole exception (arguably a bug; it should be declaredBSONCXX_INLINE), exclusively used in interfaces declaredBSONCXX_INLINE,inline, or private, indicating they are not guaranteed ABI-stability. To both document and encourage this pattern, the type traits defined inbsoncxx/stdx/type_traits.hppare not declared in an ABI namespace, as the interfaces they are used for are not intended to affect the ABI.To further enforce this expectation, note the addition of an
abi-prohibited-symbolstask in this patch (pending ABI compatibility check scripts) which greps for any mention ofbsoncxx::detailin the exported dynamic symbol table, pernm --demangle --dynamic --defined-only --extern-only --just-symbols.Doxygen Documentation
A notable casualty of this PR is that, shockingly, Doxygen does not yet understand how to identify using-declarations (doxygen/doxygen#3760). This means documentation of root namespace declarations are completely gone following this PR (Doxygen effectively behaves as if they do not exist). This presents a documentation problem for how to communicate what entities are declared in the root namespace. The current Doxygen pages in release 3.9.0 have the ideal behavior (root namespace entities redirect to
v_noabientities and inherit their documentation). An acceptable workaround for this issue has not yet been identified.This is not too severe an issue at the moment, as the only ABI namespace is the
v_noabinamespace, thus all root namespace declarations only refer tov_noabinamespace entities, and all current CXX Driver users are familiar with the existence of the root namespace declarations. However, this issue will need to be addressed before anyv1namespace entities are introduced to avoid confusion.