Some of the existing structs had primimtive fields that were
not explicitly initialized on construction.
After this commit every struct consistently sets a defined value for
every field when default-initialized.
Details
Diff Detail
- Repository
- rCTE Clang Tools Extra
- Build Status
Buildable 14965 Build 14965: arc lint + arc unit
Event Timeline
This commit makes potential UB less likely. Logical errors (i.e. forgot to initialize some fields) will still be there.
Sending this for review to make sure everyone agrees this is a good thing.
I think it's useful to explicit initialize non-trivial types like FileChangeType. But I think it's safe and probably easier to rely on default values of primitive types like int, bool etc. Explicitly setting default values is probably fine (so this change LGTM), but do we really want to make this a requirement for future changes or even in our coding style?
It's not always safe, as primitive types are sometimes left uninitialized (e.g. when constructed on the stack) and reading an uninitialized value is UB.
but do we really want to make this a requirement for future changes or even in our coding style?
I think we should, default values are less surprising than UB. Other people may disagree, though.
@sammccall , @hokein , WDYT? Should we always initialize primitive types in our code?
Oh, you are absolutely right! I I think l had protos get into my mind...
but do we really want to make this a requirement for future changes or even in our coding style?
I think we should, default values are less surprising than UB. Other people may disagree, though.
@sammccall , @hokein , WDYT? Should we always initialize primitive types in our code?
I think it would probably depend on whether we could find a sensible default value for a field (e.g. is 0 a better default value than UINT_MAX for line number?) and whether we expect users to always initialize a field (e.g. would it be better to pollute the field instead of providing a default value so that uninitialized values would be caught more easily).
Yup, I got bitten recently from some of our plain-c-style structs with no default initializers (in Index).
Definitely a fan of this change. Main downside is you can't use aggregate initialization, but the field-by-field initialization is often more readable anyway.
And +1 to avoiding explicit = None for optionals, etc.
clangd/Protocol.h | ||
---|---|---|
80 | I'd lean to making callers initialize field-by-field here rather than provide constructors. |
- Removed initializers for Optional<> fields, they are not problematic
- Remove ctor from Position, initialize on per-field basis instead.
Yeah, it arguable whether 0 is the right default, but at least it's consistent with what compiler uses for zero-initialization, i.e. when you default-initialize builtin types explicitly (e.g. int b = int(); or int b = *(new int)).
For things like FileChangeType we choose 1, because it's the lowest acceptable value, but I'm not sure that's the right default value, merely a random valid enum value.
clangd/Protocol.h | ||
---|---|---|
80 | As discussed offline, removed the ctor from Position and initialized everything on per-field basis. It strikes me that this should be decided on per-struct basis. For things like position per-field init seems preferable to avoid accidentally confusing lines and chars. |
I'd lean to making callers initialize field-by-field here rather than provide constructors.
It's not terrible here (you can get it wrong, but only one way), but it's a pattern that doesn't scale so well I think.