[torque] Protect against printing Type* pointers
I've noticed a frequent mistake within Torque is to use Type* pointers with ostream's operator<<, which causes it to print a hex pointer rather than a descriptive string. This can cause confusing error messages for users of the Torque compiler. This change is an idea to prevent future incidences of that problem by adding a template overload that will cause a compilation failure if anybody tries to use Type* in this way. It found two incorrect uses of Type*, which I've corrected. Bug: v8:7793 Change-Id: I85fafb333a89f8a3fed4346bdd154d70846a63d1 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2748936 Reviewed-by: Clemens Backes <clemensb@chromium.org> Reviewed-by: Nico Hartmann <nicohartmann@chromium.org> Commit-Queue: Seth Brenith <seth.brenith@microsoft.com> Cr-Commit-Position: refs/heads/master@{#73574}
This commit is contained in:
parent
321b1f8280
commit
ef808d3ba5
@ -51,6 +51,8 @@ V8_BASE_EXPORT V8_NOINLINE void V8_Dcheck(const char* file, int line,
|
|||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
|
class CheckMessageStream : public std::ostringstream {};
|
||||||
|
|
||||||
// Overwrite the default function that prints a stack trace.
|
// Overwrite the default function that prints a stack trace.
|
||||||
V8_BASE_EXPORT void SetPrintStackTrace(void (*print_stack_trace_)());
|
V8_BASE_EXPORT void SetPrintStackTrace(void (*print_stack_trace_)());
|
||||||
|
|
||||||
@ -141,7 +143,7 @@ V8_BASE_EXPORT void SetDcheckFunction(void (*dcheck_Function)(const char*, int,
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
template <typename... Ts>
|
template <typename... Ts>
|
||||||
std::string PrintToString(Ts&&... ts) {
|
std::string PrintToString(Ts&&... ts) {
|
||||||
std::ostringstream oss;
|
CheckMessageStream oss;
|
||||||
int unused_results[]{((oss << std::forward<Ts>(ts)), 0)...};
|
int unused_results[]{((oss << std::forward<Ts>(ts)), 0)...};
|
||||||
(void)unused_results; // Avoid "unused variable" warning.
|
(void)unused_results; // Avoid "unused variable" warning.
|
||||||
return oss.str();
|
return oss.str();
|
||||||
@ -164,7 +166,8 @@ auto GetUnderlyingEnumTypeForPrinting(T val) {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
!std::is_function<typename std::remove_pointer<T>::type>::value &&
|
!std::is_function<typename std::remove_pointer<T>::type>::value &&
|
||||||
!std::is_enum<T>::value && has_output_operator<T>::value,
|
!std::is_enum<T>::value &&
|
||||||
|
has_output_operator<T, CheckMessageStream>::value,
|
||||||
std::string>::type
|
std::string>::type
|
||||||
PrintCheckOperand(T val) {
|
PrintCheckOperand(T val) {
|
||||||
return detail::PrintToString(std::forward<T>(val));
|
return detail::PrintToString(std::forward<T>(val));
|
||||||
@ -185,7 +188,8 @@ PrintCheckOperand(T val) {
|
|||||||
|
|
||||||
// Define PrintCheckOperand<T> for enums with an output operator.
|
// Define PrintCheckOperand<T> for enums with an output operator.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
typename std::enable_if<std::is_enum<T>::value && has_output_operator<T>::value,
|
typename std::enable_if<std::is_enum<T>::value &&
|
||||||
|
has_output_operator<T, CheckMessageStream>::value,
|
||||||
std::string>::type
|
std::string>::type
|
||||||
PrintCheckOperand(T val) {
|
PrintCheckOperand(T val) {
|
||||||
std::string val_str = detail::PrintToString(val);
|
std::string val_str = detail::PrintToString(val);
|
||||||
@ -205,15 +209,16 @@ PrintCheckOperand(T val) {
|
|||||||
|
|
||||||
// Define PrintCheckOperand<T> for enums without an output operator.
|
// Define PrintCheckOperand<T> for enums without an output operator.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
typename std::enable_if<
|
typename std::enable_if<std::is_enum<T>::value &&
|
||||||
std::is_enum<T>::value && !has_output_operator<T>::value, std::string>::type
|
!has_output_operator<T, CheckMessageStream>::value,
|
||||||
|
std::string>::type
|
||||||
PrintCheckOperand(T val) {
|
PrintCheckOperand(T val) {
|
||||||
return detail::PrintToString(detail::GetUnderlyingEnumTypeForPrinting(val));
|
return detail::PrintToString(detail::GetUnderlyingEnumTypeForPrinting(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define default PrintCheckOperand<T> for non-printable types.
|
// Define default PrintCheckOperand<T> for non-printable types.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
typename std::enable_if<!has_output_operator<T>::value &&
|
typename std::enable_if<!has_output_operator<T, CheckMessageStream>::value &&
|
||||||
!std::is_enum<T>::value,
|
!std::is_enum<T>::value,
|
||||||
std::string>::type
|
std::string>::type
|
||||||
PrintCheckOperand(T val) {
|
PrintCheckOperand(T val) {
|
||||||
@ -242,7 +247,7 @@ template <typename Lhs, typename Rhs>
|
|||||||
V8_NOINLINE std::string* MakeCheckOpString(Lhs lhs, Rhs rhs, char const* msg) {
|
V8_NOINLINE std::string* MakeCheckOpString(Lhs lhs, Rhs rhs, char const* msg) {
|
||||||
std::string lhs_str = PrintCheckOperand<Lhs>(lhs);
|
std::string lhs_str = PrintCheckOperand<Lhs>(lhs);
|
||||||
std::string rhs_str = PrintCheckOperand<Rhs>(rhs);
|
std::string rhs_str = PrintCheckOperand<Rhs>(rhs);
|
||||||
std::ostringstream ss;
|
CheckMessageStream ss;
|
||||||
ss << msg;
|
ss << msg;
|
||||||
constexpr size_t kMaxInlineLength = 50;
|
constexpr size_t kMaxInlineLength = 50;
|
||||||
if (lhs_str.size() <= kMaxInlineLength &&
|
if (lhs_str.size() <= kMaxInlineLength &&
|
||||||
|
@ -53,11 +53,11 @@ struct pass_value_or_ref {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Uses expression SFINAE to detect whether using operator<< would work.
|
// Uses expression SFINAE to detect whether using operator<< would work.
|
||||||
template <typename T, typename = void>
|
template <typename T, typename TStream = std::ostream, typename = void>
|
||||||
struct has_output_operator : std::false_type {};
|
struct has_output_operator : std::false_type {};
|
||||||
template <typename T>
|
template <typename T, typename TStream>
|
||||||
struct has_output_operator<T, decltype(void(std::declval<std::ostream&>()
|
struct has_output_operator<
|
||||||
<< std::declval<T>()))>
|
T, TStream, decltype(void(std::declval<TStream&>() << std::declval<T>()))>
|
||||||
: std::true_type {};
|
: std::true_type {};
|
||||||
|
|
||||||
// Fold all arguments from left to right with a given function.
|
// Fold all arguments from left to right with a given function.
|
||||||
|
@ -141,7 +141,7 @@ void DeclarationVisitor::Visit(ExternalRuntimeDeclaration* decl) {
|
|||||||
ReportError(
|
ReportError(
|
||||||
"runtime functions can only return strong tagged values, but "
|
"runtime functions can only return strong tagged values, but "
|
||||||
"found type ",
|
"found type ",
|
||||||
signature.return_type);
|
*signature.return_type);
|
||||||
}
|
}
|
||||||
for (const Type* parameter_type : signature.parameter_types.types) {
|
for (const Type* parameter_type : signature.parameter_types.types) {
|
||||||
if (!parameter_type->IsSubtypeOf(TypeOracle::GetStrongTaggedType())) {
|
if (!parameter_type->IsSubtypeOf(TypeOracle::GetStrongTaggedType())) {
|
||||||
|
@ -2687,7 +2687,7 @@ VisitResult ImplementationVisitor::GenerateCall(
|
|||||||
if (!this_value.type()->IsSubtypeOf(method->aggregate_type())) {
|
if (!this_value.type()->IsSubtypeOf(method->aggregate_type())) {
|
||||||
ReportError("this parameter must be a subtype of ",
|
ReportError("this parameter must be a subtype of ",
|
||||||
*method->aggregate_type(), " but it is of type ",
|
*method->aggregate_type(), " but it is of type ",
|
||||||
this_value.type());
|
*this_value.type());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
AddCallParameter(callable, this_value, method->aggregate_type(),
|
AddCallParameter(callable, this_value, method->aggregate_type(),
|
||||||
|
@ -785,6 +785,20 @@ inline std::ostream& operator<<(std::ostream& os, const Type& t) {
|
|||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <bool success = false>
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Type* t) {
|
||||||
|
static_assert(success,
|
||||||
|
"Using Type* with an ostream is usually a mistake. Did you "
|
||||||
|
"mean to use Type& instead? If you actually intended to print "
|
||||||
|
"a pointer, use static_cast<const void*>.");
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't emit an error if a Type* is printed due to CHECK macros.
|
||||||
|
inline std::ostream& operator<<(base::CheckMessageStream& os, const Type* t) {
|
||||||
|
return os << static_cast<const void*>(t);
|
||||||
|
}
|
||||||
|
|
||||||
class VisitResult {
|
class VisitResult {
|
||||||
public:
|
public:
|
||||||
VisitResult() = default;
|
VisitResult() = default;
|
||||||
|
Loading…
Reference in New Issue
Block a user