[clang] efe4a54 - [Sema] Clean up ActionResult type a little. NFCI

Sam McCall via cfe-commits cfe-commits at lists.llvm.org
Fri Aug 18 04:30:51 PDT 2023


Author: Sam McCall
Date: 2023-08-18T13:30:14+02:00
New Revision: efe4a54884cb1e5f1d6306f5e831d369c6fd6f54

URL: https://github.com/llvm/llvm-project/commit/efe4a54884cb1e5f1d6306f5e831d369c6fd6f54
DIFF: https://github.com/llvm/llvm-project/commit/efe4a54884cb1e5f1d6306f5e831d369c6fd6f54.diff

LOG: [Sema] Clean up ActionResult type a little. NFCI

* Document the valid states: invalid/unset/pointer
  (Currently both documentation and implementation strongly suggest that
   pointer+invalid is poissible, when it's not)
* Remove unused set() functions, which had different semantics between the
  compressed/uncompressed specialization! (The former allowing escaping the
  tristate into pointer+invalid)
* Make the compressed specialization's internals directly model the tristate,
  rather than pretending pointer + invalid were independent.
* Make members of each version identical where possible, remove repetition
* fix operator= accidentally returning a const reference
* Fix indentation :-)

This was motivated by D157868, in which an experienced clang dev was
confused about the possible states for ExprResult - and I vividly
remember getting very confused about this myself.

Differential Revision: https://reviews.llvm.org/D158093

Added: 
    

Modified: 
    clang/include/clang/Sema/Ownership.h

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Sema/Ownership.h b/clang/include/clang/Sema/Ownership.h
index 5c7b010ed73677..83b9c9a3652a8e 100644
--- a/clang/include/clang/Sema/Ownership.h
+++ b/clang/include/clang/Sema/Ownership.h
@@ -132,7 +132,6 @@ namespace llvm {
 
 namespace clang {
 
-  // Basic
 class StreamingDiagnostic;
 
 // Determines whether the low bit of the result pointer for the
@@ -140,164 +139,147 @@ class StreamingDiagnostic;
 // for it's "invalid" flag.
 template <class Ptr> struct IsResultPtrLowBitFree {
   static const bool value = false;
-  };
-
-  /// ActionResult - This structure is used while parsing/acting on
-  /// expressions, stmts, etc.  It encapsulates both the object returned by
-  /// the action, plus a sense of whether or not it is valid.
-  /// When CompressInvalid is true, the "invalid" flag will be
-  /// stored in the low bit of the Val pointer.
-  template<class PtrTy,
-           bool CompressInvalid = IsResultPtrLowBitFree<PtrTy>::value>
-  class ActionResult {
-    PtrTy Val;
-    bool Invalid;
-
-  public:
-    ActionResult(bool Invalid = false) : Val(PtrTy()), Invalid(Invalid) {}
-    ActionResult(PtrTy val) : Val(val), Invalid(false) {}
-    ActionResult(const DiagnosticBuilder &) : Val(PtrTy()), Invalid(true) {}
-
-    // These two overloads prevent void* -> bool conversions.
-    ActionResult(const void *) = delete;
-    ActionResult(volatile void *) = delete;
-
-    bool isInvalid() const { return Invalid; }
-    bool isUsable() const { return !Invalid && Val; }
-    bool isUnset() const { return !Invalid && !Val; }
-
-    PtrTy get() const { return Val; }
-    template <typename T> T *getAs() { return static_cast<T*>(get()); }
-
-    void set(PtrTy V) { Val = V; }
-
-    const ActionResult &operator=(PtrTy RHS) {
-      Val = RHS;
-      Invalid = false;
-      return *this;
-    }
-  };
-
-  // This ActionResult partial specialization places the "invalid"
-  // flag into the low bit of the pointer.
-  template<typename PtrTy>
-  class ActionResult<PtrTy, true> {
-    // A pointer whose low bit is 1 if this result is invalid, 0
-    // otherwise.
-    uintptr_t PtrWithInvalid;
-
-    using PtrTraits = llvm::PointerLikeTypeTraits<PtrTy>;
-
-  public:
-    ActionResult(bool Invalid = false)
-        : PtrWithInvalid(static_cast<uintptr_t>(Invalid)) {}
-
-    ActionResult(PtrTy V) {
-      void *VP = PtrTraits::getAsVoidPointer(V);
-      PtrWithInvalid = reinterpret_cast<uintptr_t>(VP);
-      assert((PtrWithInvalid & 0x01) == 0 && "Badly aligned pointer");
-    }
-
-    ActionResult(const DiagnosticBuilder &) : PtrWithInvalid(0x01) {}
-
-    // These two overloads prevent void* -> bool conversions.
-    ActionResult(const void *) = delete;
-    ActionResult(volatile void *) = delete;
-
-    bool isInvalid() const { return PtrWithInvalid & 0x01; }
-    bool isUsable() const { return PtrWithInvalid > 0x01; }
-    bool isUnset() const { return PtrWithInvalid == 0; }
-
-    PtrTy get() const {
-      void *VP = reinterpret_cast<void *>(PtrWithInvalid & ~0x01);
-      return PtrTraits::getFromVoidPointer(VP);
-    }
-
-    template <typename T> T *getAs() { return static_cast<T*>(get()); }
-
-    void set(PtrTy V) {
-      void *VP = PtrTraits::getAsVoidPointer(V);
-      PtrWithInvalid = reinterpret_cast<uintptr_t>(VP);
-      assert((PtrWithInvalid & 0x01) == 0 && "Badly aligned pointer");
-    }
-
-    const ActionResult &operator=(PtrTy RHS) {
-      void *VP = PtrTraits::getAsVoidPointer(RHS);
-      PtrWithInvalid = reinterpret_cast<uintptr_t>(VP);
-      assert((PtrWithInvalid & 0x01) == 0 && "Badly aligned pointer");
-      return *this;
-    }
-
-    // For types where we can fit a flag in with the pointer, provide
-    // conversions to/from pointer type.
-    static ActionResult getFromOpaquePointer(void *P) {
-      ActionResult Result;
-      Result.PtrWithInvalid = (uintptr_t)P;
-      return Result;
-    }
-    void *getAsOpaquePointer() const { return (void*)PtrWithInvalid; }
-  };
+};
+
+/// The result of parsing/analyzing an expression, statement etc.
+///
+/// It may be:
+/// - a valid pointer to the result object
+/// - unset (null but valid), for constructs that may legitimately be absent
+///   (for example, the condition of a for loop)
+/// - invalid, indicating an error
+///   (no detail is provided, usually the error has already been diagnosed)
+template <class PtrTy, bool Compress = IsResultPtrLowBitFree<PtrTy>::value>
+class ActionResult {
+  PtrTy Val = {};
+  bool Invalid;
+
+public:
+  ActionResult(bool Invalid = false) : Val(PtrTy()), Invalid(Invalid) {}
+  ActionResult(PtrTy Val) { *this = Val; }
+  ActionResult(const DiagnosticBuilder &) : ActionResult(/*Invalid=*/true) {}
+
+  // These two overloads prevent void* -> bool conversions.
+  ActionResult(const void *) = delete;
+  ActionResult(volatile void *) = delete;
+
+  bool isInvalid() const { return Invalid; }
+  bool isUnset() const { return !Invalid && !Val; }
+  bool isUsable() const { return !isInvalid() && !isUnset(); }
+
+  PtrTy get() const { return Val; }
+  template <typename T> T *getAs() { return static_cast<T *>(get()); }
+
+  ActionResult &operator=(PtrTy RHS) {
+    Val = RHS;
+    Invalid = false;
+    return *this;
+  }
+};
 
-  /// An opaque type for threading parsed type information through the
-  /// parser.
-  using ParsedType = OpaquePtr<QualType>;
-  using UnionParsedType = UnionOpaquePtr<QualType>;
+// If we PtrTy has a free bit, we can represent "invalid" as nullptr|1.
+template <typename PtrTy> class ActionResult<PtrTy, true> {
+  static constexpr uintptr_t UnsetValue = 0x0;
+  static constexpr uintptr_t InvalidValue = 0x1;
 
-  // We can re-use the low bit of expression, statement, base, and
-  // member-initializer pointers for the "invalid" flag of
-  // ActionResult.
-  template<> struct IsResultPtrLowBitFree<Expr*> {
-    static const bool value = true;
-  };
-  template<> struct IsResultPtrLowBitFree<Stmt*> {
-    static const bool value = true;
-  };
-  template<> struct IsResultPtrLowBitFree<CXXBaseSpecifier*> {
-    static const bool value = true;
-  };
-  template<> struct IsResultPtrLowBitFree<CXXCtorInitializer*> {
-    static const bool value = true;
-  };
+  uintptr_t Value = UnsetValue;
 
-  using ExprResult = ActionResult<Expr *>;
-  using StmtResult = ActionResult<Stmt *>;
-  using TypeResult = ActionResult<ParsedType>;
-  using BaseResult = ActionResult<CXXBaseSpecifier *>;
-  using MemInitResult = ActionResult<CXXCtorInitializer *>;
+  using PtrTraits = llvm::PointerLikeTypeTraits<PtrTy>;
 
-  using DeclResult = ActionResult<Decl *>;
-  using ParsedTemplateTy = OpaquePtr<TemplateName>;
-  using UnionParsedTemplateTy = UnionOpaquePtr<TemplateName>;
+public:
+  ActionResult(bool Invalid = false)
+      : Value(Invalid ? InvalidValue : UnsetValue) {}
+  ActionResult(PtrTy V) { *this = V; }
+  ActionResult(const DiagnosticBuilder &) : ActionResult(/*Invalid=*/true) {}
 
-  using MultiExprArg = MutableArrayRef<Expr *>;
-  using MultiStmtArg = MutableArrayRef<Stmt *>;
-  using ASTTemplateArgsPtr = MutableArrayRef<ParsedTemplateArgument>;
-  using MultiTypeArg = MutableArrayRef<ParsedType>;
-  using MultiTemplateParamsArg = MutableArrayRef<TemplateParameterList *>;
+  // These two overloads prevent void* -> bool conversions.
+  ActionResult(const void *) = delete;
+  ActionResult(volatile void *) = delete;
 
-  inline ExprResult ExprError() { return ExprResult(true); }
-  inline StmtResult StmtError() { return StmtResult(true); }
-  inline TypeResult TypeError() { return TypeResult(true); }
+  bool isInvalid() const { return Value == InvalidValue; }
+  bool isUnset() const { return Value == UnsetValue; }
+  bool isUsable() const { return !isInvalid() && !isUnset(); }
 
-  inline ExprResult ExprError(const StreamingDiagnostic &) {
-    return ExprError();
+  PtrTy get() const {
+    void *VP = reinterpret_cast<void *>(Value & ~0x01);
+    return PtrTraits::getFromVoidPointer(VP);
   }
-  inline StmtResult StmtError(const StreamingDiagnostic &) {
-    return StmtError();
-  }
-
-  inline ExprResult ExprEmpty() { return ExprResult(false); }
-  inline StmtResult StmtEmpty() { return StmtResult(false); }
+  template <typename T> T *getAs() { return static_cast<T *>(get()); }
 
-  inline Expr *AssertSuccess(ExprResult R) {
-    assert(!R.isInvalid() && "operation was asserted to never fail!");
-    return R.get();
+  ActionResult &operator=(PtrTy RHS) {
+    void *VP = PtrTraits::getAsVoidPointer(RHS);
+    Value = reinterpret_cast<uintptr_t>(VP);
+    assert((Value & 0x01) == 0 && "Badly aligned pointer");
+    return *this;
   }
 
-  inline Stmt *AssertSuccess(StmtResult R) {
-    assert(!R.isInvalid() && "operation was asserted to never fail!");
-    return R.get();
+  // For types where we can fit a flag in with the pointer, provide
+  // conversions to/from pointer type.
+  static ActionResult getFromOpaquePointer(void *P) {
+    ActionResult Result;
+    Result.Value = (uintptr_t)P;
+    assert(Result.isInvalid() ||
+           PtrTraits::getAsVoidPointer(Result.get()) == P);
+    return Result;
   }
+  void *getAsOpaquePointer() const { return (void *)Value; }
+};
+
+/// An opaque type for threading parsed type information through the parser.
+using ParsedType = OpaquePtr<QualType>;
+using UnionParsedType = UnionOpaquePtr<QualType>;
+
+// We can re-use the low bit of expression, statement, base, and
+// member-initializer pointers for the "invalid" flag of
+// ActionResult.
+template <> struct IsResultPtrLowBitFree<Expr *> {
+  static const bool value = true;
+};
+template <> struct IsResultPtrLowBitFree<Stmt *> {
+  static const bool value = true;
+};
+template <> struct IsResultPtrLowBitFree<CXXBaseSpecifier *> {
+  static const bool value = true;
+};
+template <> struct IsResultPtrLowBitFree<CXXCtorInitializer *> {
+  static const bool value = true;
+};
+
+using ExprResult = ActionResult<Expr *>;
+using StmtResult = ActionResult<Stmt *>;
+using TypeResult = ActionResult<ParsedType>;
+using BaseResult = ActionResult<CXXBaseSpecifier *>;
+using MemInitResult = ActionResult<CXXCtorInitializer *>;
+
+using DeclResult = ActionResult<Decl *>;
+using ParsedTemplateTy = OpaquePtr<TemplateName>;
+using UnionParsedTemplateTy = UnionOpaquePtr<TemplateName>;
+
+using MultiExprArg = MutableArrayRef<Expr *>;
+using MultiStmtArg = MutableArrayRef<Stmt *>;
+using ASTTemplateArgsPtr = MutableArrayRef<ParsedTemplateArgument>;
+using MultiTypeArg = MutableArrayRef<ParsedType>;
+using MultiTemplateParamsArg = MutableArrayRef<TemplateParameterList *>;
+
+inline ExprResult ExprError() { return ExprResult(true); }
+inline StmtResult StmtError() { return StmtResult(true); }
+inline TypeResult TypeError() { return TypeResult(true); }
+
+inline ExprResult ExprError(const StreamingDiagnostic &) { return ExprError(); }
+inline StmtResult StmtError(const StreamingDiagnostic &) { return StmtError(); }
+
+inline ExprResult ExprEmpty() { return ExprResult(false); }
+inline StmtResult StmtEmpty() { return StmtResult(false); }
+
+inline Expr *AssertSuccess(ExprResult R) {
+  assert(!R.isInvalid() && "operation was asserted to never fail!");
+  return R.get();
+}
+
+inline Stmt *AssertSuccess(StmtResult R) {
+  assert(!R.isInvalid() && "operation was asserted to never fail!");
+  return R.get();
+}
 
 } // namespace clang
 


        


More information about the cfe-commits mailing list