Skip to content

Commit efe4a54

Browse files
committed
[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
1 parent 4cd1c07 commit efe4a54

File tree

1 file changed

+129
-147
lines changed

1 file changed

+129
-147
lines changed

clang/include/clang/Sema/Ownership.h

Lines changed: 129 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -132,172 +132,154 @@ namespace llvm {
132132

133133
namespace clang {
134134

135-
// Basic
136135
class StreamingDiagnostic;
137136

138137
// Determines whether the low bit of the result pointer for the
139138
// given UID is always zero. If so, ActionResult will use that bit
140139
// for it's "invalid" flag.
141140
template <class Ptr> struct IsResultPtrLowBitFree {
142141
static const bool value = false;
143-
};
144-
145-
/// ActionResult - This structure is used while parsing/acting on
146-
/// expressions, stmts, etc. It encapsulates both the object returned by
147-
/// the action, plus a sense of whether or not it is valid.
148-
/// When CompressInvalid is true, the "invalid" flag will be
149-
/// stored in the low bit of the Val pointer.
150-
template<class PtrTy,
151-
bool CompressInvalid = IsResultPtrLowBitFree<PtrTy>::value>
152-
class ActionResult {
153-
PtrTy Val;
154-
bool Invalid;
155-
156-
public:
157-
ActionResult(bool Invalid = false) : Val(PtrTy()), Invalid(Invalid) {}
158-
ActionResult(PtrTy val) : Val(val), Invalid(false) {}
159-
ActionResult(const DiagnosticBuilder &) : Val(PtrTy()), Invalid(true) {}
160-
161-
// These two overloads prevent void* -> bool conversions.
162-
ActionResult(const void *) = delete;
163-
ActionResult(volatile void *) = delete;
164-
165-
bool isInvalid() const { return Invalid; }
166-
bool isUsable() const { return !Invalid && Val; }
167-
bool isUnset() const { return !Invalid && !Val; }
168-
169-
PtrTy get() const { return Val; }
170-
template <typename T> T *getAs() { return static_cast<T*>(get()); }
171-
172-
void set(PtrTy V) { Val = V; }
173-
174-
const ActionResult &operator=(PtrTy RHS) {
175-
Val = RHS;
176-
Invalid = false;
177-
return *this;
178-
}
179-
};
180-
181-
// This ActionResult partial specialization places the "invalid"
182-
// flag into the low bit of the pointer.
183-
template<typename PtrTy>
184-
class ActionResult<PtrTy, true> {
185-
// A pointer whose low bit is 1 if this result is invalid, 0
186-
// otherwise.
187-
uintptr_t PtrWithInvalid;
188-
189-
using PtrTraits = llvm::PointerLikeTypeTraits<PtrTy>;
190-
191-
public:
192-
ActionResult(bool Invalid = false)
193-
: PtrWithInvalid(static_cast<uintptr_t>(Invalid)) {}
194-
195-
ActionResult(PtrTy V) {
196-
void *VP = PtrTraits::getAsVoidPointer(V);
197-
PtrWithInvalid = reinterpret_cast<uintptr_t>(VP);
198-
assert((PtrWithInvalid & 0x01) == 0 && "Badly aligned pointer");
199-
}
200-
201-
ActionResult(const DiagnosticBuilder &) : PtrWithInvalid(0x01) {}
202-
203-
// These two overloads prevent void* -> bool conversions.
204-
ActionResult(const void *) = delete;
205-
ActionResult(volatile void *) = delete;
206-
207-
bool isInvalid() const { return PtrWithInvalid & 0x01; }
208-
bool isUsable() const { return PtrWithInvalid > 0x01; }
209-
bool isUnset() const { return PtrWithInvalid == 0; }
210-
211-
PtrTy get() const {
212-
void *VP = reinterpret_cast<void *>(PtrWithInvalid & ~0x01);
213-
return PtrTraits::getFromVoidPointer(VP);
214-
}
215-
216-
template <typename T> T *getAs() { return static_cast<T*>(get()); }
217-
218-
void set(PtrTy V) {
219-
void *VP = PtrTraits::getAsVoidPointer(V);
220-
PtrWithInvalid = reinterpret_cast<uintptr_t>(VP);
221-
assert((PtrWithInvalid & 0x01) == 0 && "Badly aligned pointer");
222-
}
223-
224-
const ActionResult &operator=(PtrTy RHS) {
225-
void *VP = PtrTraits::getAsVoidPointer(RHS);
226-
PtrWithInvalid = reinterpret_cast<uintptr_t>(VP);
227-
assert((PtrWithInvalid & 0x01) == 0 && "Badly aligned pointer");
228-
return *this;
229-
}
230-
231-
// For types where we can fit a flag in with the pointer, provide
232-
// conversions to/from pointer type.
233-
static ActionResult getFromOpaquePointer(void *P) {
234-
ActionResult Result;
235-
Result.PtrWithInvalid = (uintptr_t)P;
236-
return Result;
237-
}
238-
void *getAsOpaquePointer() const { return (void*)PtrWithInvalid; }
239-
};
142+
};
143+
144+
/// The result of parsing/analyzing an expression, statement etc.
145+
///
146+
/// It may be:
147+
/// - a valid pointer to the result object
148+
/// - unset (null but valid), for constructs that may legitimately be absent
149+
/// (for example, the condition of a for loop)
150+
/// - invalid, indicating an error
151+
/// (no detail is provided, usually the error has already been diagnosed)
152+
template <class PtrTy, bool Compress = IsResultPtrLowBitFree<PtrTy>::value>
153+
class ActionResult {
154+
PtrTy Val = {};
155+
bool Invalid;
156+
157+
public:
158+
ActionResult(bool Invalid = false) : Val(PtrTy()), Invalid(Invalid) {}
159+
ActionResult(PtrTy Val) { *this = Val; }
160+
ActionResult(const DiagnosticBuilder &) : ActionResult(/*Invalid=*/true) {}
161+
162+
// These two overloads prevent void* -> bool conversions.
163+
ActionResult(const void *) = delete;
164+
ActionResult(volatile void *) = delete;
165+
166+
bool isInvalid() const { return Invalid; }
167+
bool isUnset() const { return !Invalid && !Val; }
168+
bool isUsable() const { return !isInvalid() && !isUnset(); }
169+
170+
PtrTy get() const { return Val; }
171+
template <typename T> T *getAs() { return static_cast<T *>(get()); }
172+
173+
ActionResult &operator=(PtrTy RHS) {
174+
Val = RHS;
175+
Invalid = false;
176+
return *this;
177+
}
178+
};
240179

241-
/// An opaque type for threading parsed type information through the
242-
/// parser.
243-
using ParsedType = OpaquePtr<QualType>;
244-
using UnionParsedType = UnionOpaquePtr<QualType>;
180+
// If we PtrTy has a free bit, we can represent "invalid" as nullptr|1.
181+
template <typename PtrTy> class ActionResult<PtrTy, true> {
182+
static constexpr uintptr_t UnsetValue = 0x0;
183+
static constexpr uintptr_t InvalidValue = 0x1;
245184

246-
// We can re-use the low bit of expression, statement, base, and
247-
// member-initializer pointers for the "invalid" flag of
248-
// ActionResult.
249-
template<> struct IsResultPtrLowBitFree<Expr*> {
250-
static const bool value = true;
251-
};
252-
template<> struct IsResultPtrLowBitFree<Stmt*> {
253-
static const bool value = true;
254-
};
255-
template<> struct IsResultPtrLowBitFree<CXXBaseSpecifier*> {
256-
static const bool value = true;
257-
};
258-
template<> struct IsResultPtrLowBitFree<CXXCtorInitializer*> {
259-
static const bool value = true;
260-
};
185+
uintptr_t Value = UnsetValue;
261186

262-
using ExprResult = ActionResult<Expr *>;
263-
using StmtResult = ActionResult<Stmt *>;
264-
using TypeResult = ActionResult<ParsedType>;
265-
using BaseResult = ActionResult<CXXBaseSpecifier *>;
266-
using MemInitResult = ActionResult<CXXCtorInitializer *>;
187+
using PtrTraits = llvm::PointerLikeTypeTraits<PtrTy>;
267188

268-
using DeclResult = ActionResult<Decl *>;
269-
using ParsedTemplateTy = OpaquePtr<TemplateName>;
270-
using UnionParsedTemplateTy = UnionOpaquePtr<TemplateName>;
189+
public:
190+
ActionResult(bool Invalid = false)
191+
: Value(Invalid ? InvalidValue : UnsetValue) {}
192+
ActionResult(PtrTy V) { *this = V; }
193+
ActionResult(const DiagnosticBuilder &) : ActionResult(/*Invalid=*/true) {}
271194

272-
using MultiExprArg = MutableArrayRef<Expr *>;
273-
using MultiStmtArg = MutableArrayRef<Stmt *>;
274-
using ASTTemplateArgsPtr = MutableArrayRef<ParsedTemplateArgument>;
275-
using MultiTypeArg = MutableArrayRef<ParsedType>;
276-
using MultiTemplateParamsArg = MutableArrayRef<TemplateParameterList *>;
195+
// These two overloads prevent void* -> bool conversions.
196+
ActionResult(const void *) = delete;
197+
ActionResult(volatile void *) = delete;
277198

278-
inline ExprResult ExprError() { return ExprResult(true); }
279-
inline StmtResult StmtError() { return StmtResult(true); }
280-
inline TypeResult TypeError() { return TypeResult(true); }
199+
bool isInvalid() const { return Value == InvalidValue; }
200+
bool isUnset() const { return Value == UnsetValue; }
201+
bool isUsable() const { return !isInvalid() && !isUnset(); }
281202

282-
inline ExprResult ExprError(const StreamingDiagnostic &) {
283-
return ExprError();
203+
PtrTy get() const {
204+
void *VP = reinterpret_cast<void *>(Value & ~0x01);
205+
return PtrTraits::getFromVoidPointer(VP);
284206
}
285-
inline StmtResult StmtError(const StreamingDiagnostic &) {
286-
return StmtError();
287-
}
288-
289-
inline ExprResult ExprEmpty() { return ExprResult(false); }
290-
inline StmtResult StmtEmpty() { return StmtResult(false); }
207+
template <typename T> T *getAs() { return static_cast<T *>(get()); }
291208

292-
inline Expr *AssertSuccess(ExprResult R) {
293-
assert(!R.isInvalid() && "operation was asserted to never fail!");
294-
return R.get();
209+
ActionResult &operator=(PtrTy RHS) {
210+
void *VP = PtrTraits::getAsVoidPointer(RHS);
211+
Value = reinterpret_cast<uintptr_t>(VP);
212+
assert((Value & 0x01) == 0 && "Badly aligned pointer");
213+
return *this;
295214
}
296215

297-
inline Stmt *AssertSuccess(StmtResult R) {
298-
assert(!R.isInvalid() && "operation was asserted to never fail!");
299-
return R.get();
216+
// For types where we can fit a flag in with the pointer, provide
217+
// conversions to/from pointer type.
218+
static ActionResult getFromOpaquePointer(void *P) {
219+
ActionResult Result;
220+
Result.Value = (uintptr_t)P;
221+
assert(Result.isInvalid() ||
222+
PtrTraits::getAsVoidPointer(Result.get()) == P);
223+
return Result;
300224
}
225+
void *getAsOpaquePointer() const { return (void *)Value; }
226+
};
227+
228+
/// An opaque type for threading parsed type information through the parser.
229+
using ParsedType = OpaquePtr<QualType>;
230+
using UnionParsedType = UnionOpaquePtr<QualType>;
231+
232+
// We can re-use the low bit of expression, statement, base, and
233+
// member-initializer pointers for the "invalid" flag of
234+
// ActionResult.
235+
template <> struct IsResultPtrLowBitFree<Expr *> {
236+
static const bool value = true;
237+
};
238+
template <> struct IsResultPtrLowBitFree<Stmt *> {
239+
static const bool value = true;
240+
};
241+
template <> struct IsResultPtrLowBitFree<CXXBaseSpecifier *> {
242+
static const bool value = true;
243+
};
244+
template <> struct IsResultPtrLowBitFree<CXXCtorInitializer *> {
245+
static const bool value = true;
246+
};
247+
248+
using ExprResult = ActionResult<Expr *>;
249+
using StmtResult = ActionResult<Stmt *>;
250+
using TypeResult = ActionResult<ParsedType>;
251+
using BaseResult = ActionResult<CXXBaseSpecifier *>;
252+
using MemInitResult = ActionResult<CXXCtorInitializer *>;
253+
254+
using DeclResult = ActionResult<Decl *>;
255+
using ParsedTemplateTy = OpaquePtr<TemplateName>;
256+
using UnionParsedTemplateTy = UnionOpaquePtr<TemplateName>;
257+
258+
using MultiExprArg = MutableArrayRef<Expr *>;
259+
using MultiStmtArg = MutableArrayRef<Stmt *>;
260+
using ASTTemplateArgsPtr = MutableArrayRef<ParsedTemplateArgument>;
261+
using MultiTypeArg = MutableArrayRef<ParsedType>;
262+
using MultiTemplateParamsArg = MutableArrayRef<TemplateParameterList *>;
263+
264+
inline ExprResult ExprError() { return ExprResult(true); }
265+
inline StmtResult StmtError() { return StmtResult(true); }
266+
inline TypeResult TypeError() { return TypeResult(true); }
267+
268+
inline ExprResult ExprError(const StreamingDiagnostic &) { return ExprError(); }
269+
inline StmtResult StmtError(const StreamingDiagnostic &) { return StmtError(); }
270+
271+
inline ExprResult ExprEmpty() { return ExprResult(false); }
272+
inline StmtResult StmtEmpty() { return StmtResult(false); }
273+
274+
inline Expr *AssertSuccess(ExprResult R) {
275+
assert(!R.isInvalid() && "operation was asserted to never fail!");
276+
return R.get();
277+
}
278+
279+
inline Stmt *AssertSuccess(StmtResult R) {
280+
assert(!R.isInvalid() && "operation was asserted to never fail!");
281+
return R.get();
282+
}
301283

302284
} // namespace clang
303285

0 commit comments

Comments
 (0)