[PATCH] D130055: Clang extensions yolo, woot & kaboom

Chris Bieneman via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Wed Jul 27 13:02:10 PDT 2022


beanz added a comment.

In D130055#3683173 <https://reviews.llvm.org/D130055#3683173>, @aaron.ballman wrote:

> Are there circumstances where we cannot "simply" infer this from the constructor itself?

I think this gets tricky in cases where objects may have valid "uninitialized" state. Like a tagged union that hasn't been set might set its tag value to "uninitialized" and zero its storage, which would make it look like all the members are initialized to the compiler, they just happen to be initialized to known sentinel values.

> (After instantiation) we know the class hierarchy, we know the data members, and we know the ctor init list/in-class initializers, so it seems like we should be able to recursively mark a constructor as yolo for the user rather than making them write it themselves. It seems like inferring this would reduce false positives and false negatives (in the case the user marks the constructor incorrectly or the code gets out of sync with the marking), and we might only need this attribute in very rare circumstances (or not at all, as a user-facing attribute). Is that an approach you're considering?

I hadn't thought at all about automated propagation. One of the other use cases I was thinking about was `std::optional`, where you could specify that initialization to `std::nullopt` is `yolo`, and then `get` becomes `kaboom`.

With these annotations we can conservatively diagnose cases where an optional's value is accessed when uninitialized.

> Fully? Partially? I'm trying to decide whether this is only useful for functions named `init()` and `clear()` and the like, or whether this is useful for functions like `setWhatever()` where you can have a partially constructed object that is not fully initialized by any one member function.

I had also thought about this too. As implemented in this patch `woot` implies fully initialized, but there could be some interesting complex initializations. I could imagine builder patterns where given code like `Builder::Create().X().Y().Z()`, you might want `Y` to only be callable after `X` and `Z` might be the required final initializer.

> Same question here about fully vs partially initialized. e.g., where you have a partially constructed object and you can only call `getWhatever()` after `setWhatever()` has been called.

I'm wondering if there could be a generalization of the attribute like:

  class TaggedValue {
    enum Kind {
      Uninitialized = 0,
      Integer,
      Float
    };
    Kind VK = Uninitialized;
  
    union {
      int I;
      float F;
    };
  public:
    
    [[clang::yolo]]
    TaggedValue() = default;
    
    TaggedValue(TaggedValue&) = default;
  
    void hasValue() { return VK == Uninitialized; } // always safe
  
    [[clang::woot("FloatSet"]] // Marks as safe for functions with matching kaboom arguments
    void set(float V) {
      VK= Float;
      F = V;
    }
  
    [[clang::woot("IntSet")]] // Marks as safe for functions with matching kaboom arguments
    void set(int V) {
      VK= Integer;
      I = V;
    }
  
      [[clang::woot]] // Marks as safe for all kaboom functions (because I'm sad)
     void zero() {
       VK= Integer;
       I = 0;
     }
  
    [[clang::kaboom("FloatSet"]]
    operator float() {
      return F;
    }
  
    [[clang::kaboom("IntSet")]]
    operator int() {
      return I;
    }
  };

This does get into some more complex questions of whether `woot` would change or append status, and I can see arguments for both so we might need `appending_woot` too...


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D130055/new/

https://reviews.llvm.org/D130055



More information about the cfe-commits mailing list