Skip to content

Conversation

@mizvekov
Copy link
Contributor

@mizvekov mizvekov commented Apr 7, 2025

Currently when printing a template argument of expression type, the expression is converted immediately into a string to be sent to the diagnostic engine, unsing a fake LangOpts.

This makes the expression printing look incorrect for the current language, besides being inneficient, as we don't actually need to print the expression if the diagnostic would be ignored.

This fixes a nastiness with the TemplateArgument constructor for expressions being implicit, and all current users just passing an expression to a diagnostic were implicitly going through the template argument path.

The expressions are also being printed unquoted. This will be fixed in a subsequent patch, as the test churn is much larger.

@mizvekov mizvekov self-assigned this Apr 7, 2025
@llvmbot llvmbot added clang Clang issues not falling into any other category clang-tools-extra clang-tidy clang:frontend Language frontend issues, e.g. anything involving "Sema" HLSL HLSL Language Support labels Apr 7, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 7, 2025

@llvm/pr-subscribers-libcxx
@llvm/pr-subscribers-clang-tools-extra
@llvm/pr-subscribers-hlsl
@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-tidy

Author: Matheus Izvekov (mizvekov)

Changes

Currently when printing a template argument of expression type, the expression is converted immediately into a string to be sent to the diagnostic engine, unsing a fake LangOpts.

This makes the expression printing look incorrect for the current language, besides being inneficient, as we don't actually need to print the expression if the diagnostic would be ignored.

This fixes a nastiness with the TemplateArgument constructor for expressions being implicit, and all current users just passing an expression to a diagnostic were implicitly going through the template argument path.

The expressions are also being printed unquoted. This will be fixed in a subsequent patch, as the test churn is much larger.


Full diff: https://github.com/llvm/llvm-project/pull/134693.diff

15 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp (+3)
  • (modified) clang/docs/ReleaseNotes.rst (+3)
  • (modified) clang/include/clang/AST/Expr.h (+8)
  • (modified) clang/include/clang/AST/TemplateBase.h (+1-1)
  • (modified) clang/include/clang/Basic/Diagnostic.h (+4-1)
  • (modified) clang/lib/AST/ASTDiagnostic.cpp (+8)
  • (modified) clang/lib/AST/TemplateBase.cpp (+3-13)
  • (modified) clang/lib/Basic/Diagnostic.cpp (+1)
  • (modified) clang/lib/Sema/SemaTemplateDeduction.cpp (+4-4)
  • (modified) clang/lib/Sema/SemaTemplateVariadic.cpp (+1-1)
  • (modified) clang/lib/Sema/TreeTransform.h (+3-3)
  • (modified) clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl (+5-5)
  • (modified) clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp (+2-2)
  • (modified) clang/test/SemaTemplate/instantiate-requires-expr.cpp (+2-2)
  • (modified) clang/test/SemaTemplate/trailing-return-short-circuit.cpp (+2-2)
diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
index 71e852545203e..abd6d7b4cd60f 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
@@ -22,6 +22,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTDiagnostic.h"
 #include "clang/AST/Attr.h"
+#include "clang/AST/Expr.h"
 #include "clang/Basic/CharInfo.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/DiagnosticOptions.h"
@@ -528,6 +529,8 @@ void ClangTidyDiagnosticConsumer::forwardDiagnostic(const Diagnostic &Info) {
     case clang::DiagnosticsEngine::ak_addrspace:
       Builder << static_cast<LangAS>(Info.getRawArg(Index));
       break;
+    case clang::DiagnosticsEngine::ak_expr:
+      Builder << reinterpret_cast<const Expr *>(Info.getRawArg(Index));
     }
   }
 }
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index f8f4dfbafb4f8..049ba5ef9ea02 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -280,6 +280,9 @@ Improvements to Clang's diagnostics
 - Clang now better preserves the sugared types of pointers to member.
 - Clang now better preserves the presence of the template keyword with dependent
   prefixes.
+- Clang now respects the current language mode when printing expressions in
+  diagnsotics. This fixes a bunch of `bool` being printed as `_Bool`, and also
+  a bunch of HLSL types being printed as their C++ equivalents.
 - When printing types for diagnostics, clang now doesn't suppress the scopes of
   template arguments contained within nested names.
 - The ``-Wshift-bool`` warning has been added to warn about shifting a boolean. (#GH28334)
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index dedbff5944af8..20f70863a05b3 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -7379,6 +7379,14 @@ class RecoveryExpr final : public Expr,
   friend class ASTStmtWriter;
 };
 
+/// Insertion operator for diagnostics.  This allows sending
+/// Expr into a diagnostic with <<.
+inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
+                                             const Expr *E) {
+  DB.AddTaggedVal(reinterpret_cast<uint64_t>(E), DiagnosticsEngine::ak_expr);
+  return DB;
+}
+
 } // end namespace clang
 
 #endif // LLVM_CLANG_AST_EXPR_H
diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h
index a800a16fc3e7a..bea624eb04942 100644
--- a/clang/include/clang/AST/TemplateBase.h
+++ b/clang/include/clang/AST/TemplateBase.h
@@ -262,7 +262,7 @@ class TemplateArgument {
   /// This form of template argument only occurs in template argument
   /// lists used for dependent types and for expression; it will not
   /// occur in a non-dependent, canonical template argument list.
-  TemplateArgument(Expr *E, bool IsDefaulted = false) {
+  explicit TemplateArgument(Expr *E, bool IsDefaulted = false) {
     TypeOrValue.Kind = Expression;
     TypeOrValue.IsDefaulted = IsDefaulted;
     TypeOrValue.V = reinterpret_cast<uintptr_t>(E);
diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h
index 848acce3c4f13..19524856a9bb3 100644
--- a/clang/include/clang/Basic/Diagnostic.h
+++ b/clang/include/clang/Basic/Diagnostic.h
@@ -284,7 +284,10 @@ class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {
     ak_qualtype_pair,
 
     /// Attr *
-    ak_attr
+    ak_attr,
+
+    /// Expr *
+    ak_expr,
   };
 
   /// Represents on argument value, which is a union discriminated
diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp
index b4e7360e126fb..ccfef9c7ae361 100644
--- a/clang/lib/AST/ASTDiagnostic.cpp
+++ b/clang/lib/AST/ASTDiagnostic.cpp
@@ -508,6 +508,14 @@ void clang::FormatASTNodeDiagnosticArgument(
       NeedQuotes = false;
       break;
     }
+    case DiagnosticsEngine::ak_expr: {
+      const Expr *E = reinterpret_cast<Expr *>(Val);
+      assert(E && "Received null Expr!");
+      E->printPretty(OS, /*Helper=*/nullptr, Context.getPrintingPolicy());
+      // FIXME: Include quotes when printing expressions.
+      NeedQuotes = false;
+      break;
+    }
   }
 
   if (NeedQuotes) {
diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp
index 0be0a83b7010d..42da4e6ca9964 100644
--- a/clang/lib/AST/TemplateBase.cpp
+++ b/clang/lib/AST/TemplateBase.cpp
@@ -478,7 +478,7 @@ TemplateArgument TemplateArgument::getPackExpansionPattern() const {
     return getAsType()->castAs<PackExpansionType>()->getPattern();
 
   case Expression:
-    return cast<PackExpansionExpr>(getAsExpr())->getPattern();
+    return TemplateArgument(cast<PackExpansionExpr>(getAsExpr())->getPattern());
 
   case TemplateExpansion:
     return TemplateArgument(getAsTemplateOrTemplatePattern());
@@ -654,18 +654,8 @@ static const T &DiagTemplateArg(const T &DB, const TemplateArgument &Arg) {
   case TemplateArgument::TemplateExpansion:
     return DB << Arg.getAsTemplateOrTemplatePattern() << "...";
 
-  case TemplateArgument::Expression: {
-    // This shouldn't actually ever happen, so it's okay that we're
-    // regurgitating an expression here.
-    // FIXME: We're guessing at LangOptions!
-    SmallString<32> Str;
-    llvm::raw_svector_ostream OS(Str);
-    LangOptions LangOpts;
-    LangOpts.CPlusPlus = true;
-    PrintingPolicy Policy(LangOpts);
-    Arg.getAsExpr()->printPretty(OS, nullptr, Policy);
-    return DB << OS.str();
-  }
+  case TemplateArgument::Expression:
+    return DB << Arg.getAsExpr();
 
   case TemplateArgument::Pack: {
     // FIXME: We're guessing at LangOptions!
diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp
index 9e2f134135647..4b4a85aaccf8b 100644
--- a/clang/lib/Basic/Diagnostic.cpp
+++ b/clang/lib/Basic/Diagnostic.cpp
@@ -1247,6 +1247,7 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd,
     case DiagnosticsEngine::ak_nestednamespec:
     case DiagnosticsEngine::ak_declcontext:
     case DiagnosticsEngine::ak_attr:
+    case DiagnosticsEngine::ak_expr:
       getDiags()->ConvertArgToString(Kind, getRawArg(ArgNo),
                                      StringRef(Modifier, ModifierLen),
                                      StringRef(Argument, ArgumentLen),
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 200168087136b..6236bce743438 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -493,8 +493,8 @@ DeduceNullPtrTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
                                                         : CK_NullToPointer)
                     .get();
   return DeduceNonTypeTemplateArgument(
-      S, TemplateParams, NTTP, DeducedTemplateArgument(Value), Value->getType(),
-      Info, PartialOrdering, Deduced, HasDeducedAnyParam);
+      S, TemplateParams, NTTP, TemplateArgument(Value), Value->getType(), Info,
+      PartialOrdering, Deduced, HasDeducedAnyParam);
 }
 
 /// Deduce the value of the given non-type template parameter
@@ -508,8 +508,8 @@ DeduceNonTypeTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
                               SmallVectorImpl<DeducedTemplateArgument> &Deduced,
                               bool *HasDeducedAnyParam) {
   return DeduceNonTypeTemplateArgument(
-      S, TemplateParams, NTTP, DeducedTemplateArgument(Value), Value->getType(),
-      Info, PartialOrdering, Deduced, HasDeducedAnyParam);
+      S, TemplateParams, NTTP, TemplateArgument(Value), Value->getType(), Info,
+      PartialOrdering, Deduced, HasDeducedAnyParam);
 }
 
 /// Deduce the value of the given non-type template parameter
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 3d4a245eb8bd5..3040a30454b0c 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -1286,7 +1286,7 @@ TemplateArgumentLoc Sema::getTemplateArgumentPackExpansionPattern(
     Expr *Pattern = Expansion->getPattern();
     Ellipsis = Expansion->getEllipsisLoc();
     NumExpansions = Expansion->getNumExpansions();
-    return TemplateArgumentLoc(Pattern, Pattern);
+    return TemplateArgumentLoc(TemplateArgument(Pattern), Pattern);
   }
 
   case TemplateArgument::TemplateExpansion:
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 57fcc4b3b3682..2889a16dd0271 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -3981,7 +3981,7 @@ class TreeTransform {
       if (Result.isInvalid())
         return TemplateArgumentLoc();
 
-      return TemplateArgumentLoc(Result.get(), Result.get());
+      return TemplateArgumentLoc(TemplateArgument(Result.get()), Result.get());
     }
 
     case TemplateArgument::Template:
@@ -16125,8 +16125,8 @@ TreeTransform<Derived>::TransformSizeOfPackExpr(SizeOfPackExpr *E) {
             E->getPackLoc());
         if (DRE.isInvalid())
           return ExprError();
-        ArgStorage = new (getSema().Context)
-            PackExpansionExpr(DRE.get(), E->getPackLoc(), std::nullopt);
+        ArgStorage = TemplateArgument(new (getSema().Context) PackExpansionExpr(
+            DRE.get(), E->getPackLoc(), std::nullopt));
       }
       PackArgs = ArgStorage;
     }
diff --git a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
index 941e0a975d5f4..34930d8963688 100644
--- a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
@@ -60,13 +60,13 @@ RWBuffer<TemplatedVector<int> > r9;
 // arrays not allowed
 // expected-error@+3 {{constraints not satisfied for class template 'RWBuffer'}}
 // expected-note@*:* {{because 'half[4]' does not satisfy '__is_typed_resource_element_compatible'}}
-// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(__fp16[4])' evaluated to false}}
+// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(half[4])' evaluated to false}}
 RWBuffer<half[4]> r10;
 
 typedef vector<int, 8> int8;
 // expected-error@+3 {{constraints not satisfied for class template 'RWBuffer'}}
 // expected-note@*:* {{because 'vector<int, 8>' (vector of 8 'int' values) does not satisfy '__is_typed_resource_element_compatible'}}
-// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(int __attribute__((ext_vector_type(8))))' evaluated to false}}
+// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(vector<int, 8>)' evaluated to false}}
 RWBuffer<int8> r11;
 
 typedef int MyInt;
@@ -74,12 +74,12 @@ RWBuffer<MyInt> r12;
 
 // expected-error@+3 {{constraints not satisfied for class template 'RWBuffer'}}
 // expected-note@*:* {{because 'bool' does not satisfy '__is_typed_resource_element_compatible'}}
-// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(_Bool)' evaluated to false}}
+// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(bool)' evaluated to false}}
 RWBuffer<bool> r13;
 
 // expected-error@+3 {{constraints not satisfied for class template 'RWBuffer'}}
 // expected-note@*:* {{because 'vector<bool, 2>' (vector of 2 'bool' values) does not satisfy '__is_typed_resource_element_compatible'}}
-// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(_Bool __attribute__((ext_vector_type(2))))' evaluated to false}}
+// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(vector<bool, 2>)' evaluated to false}}
 RWBuffer<vector<bool, 2>> r14;
 
 enum numbers { one, two, three };
@@ -91,7 +91,7 @@ RWBuffer<numbers> r15;
 
 // expected-error@+3 {{constraints not satisfied for class template 'RWBuffer'}}
 // expected-note@*:* {{because 'vector<double, 3>' (vector of 3 'double' values) does not satisfy '__is_typed_resource_element_compatible'}}
-// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(double __attribute__((ext_vector_type(3))))' evaluated to false}}
+// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(vector<double, 3>)' evaluated to false}}
 RWBuffer<double3> r16;
 
 
diff --git a/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp b/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp
index 73fef87b97822..3edf243982958 100644
--- a/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp
+++ b/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp
@@ -8,7 +8,7 @@ constexpr bool is_same_v<T, T> = true;
 
 template<typename T, typename U>
 concept same_as = is_same_v<T, U>;
-// expected-note@-1{{because 'is_same_v<int, _Bool>' evaluated to false}}
+// expected-note@-1{{because 'is_same_v<int, bool>' evaluated to false}}
 
 template<typename T, typename... Us>
 concept either = (is_same_v<T, Us> || ...);
@@ -16,7 +16,7 @@ concept either = (is_same_v<T, Us> || ...);
 template<typename... Ts>
 struct T {
     template<same_as<Ts>... Us>
-    // expected-note@-1{{because 'same_as<int, _Bool>' evaluated to false}}
+    // expected-note@-1{{because 'same_as<int, bool>' evaluated to false}}
     static void foo(Us... u, int x) { };
     // expected-note@-1{{candidate template ignored: deduced too few arguments}}
     // expected-note@-2{{candidate template ignored: constraints not satisfied}}
diff --git a/clang/test/SemaTemplate/instantiate-requires-expr.cpp b/clang/test/SemaTemplate/instantiate-requires-expr.cpp
index ab5fac1f9e63e..47689b93db50f 100644
--- a/clang/test/SemaTemplate/instantiate-requires-expr.cpp
+++ b/clang/test/SemaTemplate/instantiate-requires-expr.cpp
@@ -72,8 +72,8 @@ namespace type_requirement {
 
   template<typename T> requires
   false_v<requires { typename T::template temp<T>; }>
-  // expected-note@-1 {{because 'false_v<requires { typename contains_template<int>::template temp<type_requirement::contains_template<int> >; }>' evaluated to false}}
-  // expected-note@-2 {{because 'false_v<requires { typename contains_template<short>::template temp<type_requirement::contains_template<short> >; }>' evaluated to false}}
+  // expected-note@-1 {{because 'false_v<requires { typename contains_template<int>::template temp<type_requirement::contains_template<int>>; }>' evaluated to false}}
+  // expected-note@-2 {{because 'false_v<requires { typename contains_template<short>::template temp<type_requirement::contains_template<short>>; }>' evaluated to false}}
   struct r2 {};
 
   using r2i1 = r2<contains_template<int>>; // expected-error{{constraints not satisfied for class template 'r2' [with T = type_requirement::contains_template<int>]}}
diff --git a/clang/test/SemaTemplate/trailing-return-short-circuit.cpp b/clang/test/SemaTemplate/trailing-return-short-circuit.cpp
index 0d1c9b52b0e85..4ef7888d23dc5 100644
--- a/clang/test/SemaTemplate/trailing-return-short-circuit.cpp
+++ b/clang/test/SemaTemplate/trailing-return-short-circuit.cpp
@@ -39,13 +39,13 @@ void usage() {
   Foo(true);
   // expected-error@-1{{no matching function for call to 'Foo'}}
   // expected-note@#FOO{{candidate template ignored: constraints not satisfied [with T = bool]}}
-  // expected-note@#FOO_REQ{{because 'sizeof(_Bool) > 2' (1 > 2) evaluated to false}}
+  // expected-note@#FOO_REQ{{because 'sizeof(bool) > 2' (1 > 2) evaluated to false}}
   // expected-note@#FOO_REQ{{because substituted constraint expression is ill-formed: type 'bool' cannot be used prior to '::' because it has no members}}
 
   TrailingReturn(true);
   // expected-error@-1{{no matching function for call to 'TrailingReturn'}}
   // expected-note@#TRAILING{{candidate template ignored: constraints not satisfied [with T = bool]}}
-  // expected-note@#TRAILING_REQ{{because 'sizeof(_Bool) > 2' (1 > 2) evaluated to false}}
+  // expected-note@#TRAILING_REQ{{because 'sizeof(bool) > 2' (1 > 2) evaluated to false}}
   // expected-note@#TRAILING_REQ_VAL{{because substituted constraint expression is ill-formed: type 'bool' cannot be used prior to '::' because it has no members}}
 
   // Fails the 1st check, fails 2nd because ::value is false.

@mizvekov mizvekov requested a review from llvm-beanz April 7, 2025 17:06
@mizvekov mizvekov force-pushed the users/mizvekov/diag-expr branch from ab5d841 to 3ccb347 Compare April 7, 2025 17:13
Currently when printing a template argument of expression type,
the expression is converted immediately into a string to be sent
to the diagnostic engine, unsing a fake LangOpts.

This makes the expression printing look incorrect for the current language,
besides being inneficient, as we don't actually need to print
the expression if the diagnostic would be ignored.

This fixes a nastiness with the TemplateArgument constructor for
expressions being implicit, and all current users just passing
an expression to a diagnostic were implicitly going through the
template argument path.

The expressions are also being printed unquoted. This will be fixed
in a subsequent patch, as the test churn is much larger.
@mizvekov mizvekov force-pushed the users/mizvekov/diag-expr branch from 3ccb347 to 302fc36 Compare April 7, 2025 23:46
@mizvekov mizvekov requested a review from a team as a code owner April 7, 2025 23:46
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Apr 7, 2025
@mizvekov mizvekov merged commit d057811 into main Apr 8, 2025
34 of 35 checks passed
@mizvekov mizvekov deleted the users/mizvekov/diag-expr branch April 8, 2025 02:19
/// Expr into a diagnostic with <<.
inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
const Expr *E) {
DB.AddTaggedVal(reinterpret_cast<uint64_t>(E), DiagnosticsEngine::ak_expr);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So is this correct, to cast a pointer to uint64_t? It looks like we do this and in about half the other place we use intptr_t, which I would be much less suspicious of.

CC @AaronBallman @erichkeane

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

intptr_t /uintptr_t I think avoids any 'round trip' errors by standard, but I think uint64_t is going to be the same type ANYWAY so this is probably harmless at least. I think the suggestion to use intptr_t/uintptr_t is a good one that should happen.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we care that uint64_t might not round trip a pointer in theory, even though it should always in practice, then do we care uintptr_t is not guaranteed to be provided in theory, even though it should always be in practice?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we care that uint64_t might not round trip a pointer in theory, even though it should always in practice, then do we care uintptr_t is not guaranteed to be provided in theory, even though it should always be in practice?

THAT we don't care about because : 1- it is a compile-time diagnosed missing 'thing', and 2- We know all our supported host platforms have (and presumably always will have) that type.

So it is a matter of "potentially ill-formed" vs "potential-UB".

Either way, it doesn't MATTER very much, but from a 'most correct' perspective (as frankly, uint64_t and uintptr_t are probably the same type, and I would bet good money that none of our platforms would ever change that/never give uintptr_t its own representation to take advantage of this UB), but just changing the cast type here is the right thing to do.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not just here though, this is the same for all other kinds, and the same for the parameter type, and the same for where we store this.

It wouldn't change much regarding potential UB if we wrote uintptr_t here, but then this would implicitly cast to uint64_t anyway.

Now if we change that to uintptr_t, then that would be bad in 32 bit platforms, as this is used to pack other things besides pointers. So presumably we want to change the parameter and structure types here to something that's at least as big as both types, which would be uintmax perhaps?

Or maybe just assert somewhere that uint64 is at least as big as uintptr?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as correctness: we need to smuggle pointers through a uintptr_t. Beyond that, there is no harm then again casting that immediately to the uint64_t.

So the 'fixit' suggestion is:

Suggested change
DB.AddTaggedVal(reinterpret_cast<uint64_t>(E), DiagnosticsEngine::ak_expr);
DB.AddTaggedVal(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(E)), DiagnosticsEngine::ak_expr);

The underlying type here is fine, and SHOULD stay uint64_t. But the reinterpret_cast of pointer-to-int itself is the problematic part. If you'd like, you can put a static-assert that sizeof(uintptr_t) is <= sizeof(uint64_t), but I'm not horribly worried about 128 bit pointers

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But presumably, I'd have to go on and do this for all diagnostics stream operators, not just this one, otherwise it would not be helpful to the next individual, who just
like me, did the same way it was done somewhere else :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably SHOULD, and you can do so without review if you care to. Else, we can just make @shafik do this, since he started it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not super sympathetic here, reinterpret_cast in a code review should have both the author and the reviewer on high alert and they should carefully examine the code and if we suspect the same pattern is used elsewhere we should confirm it is used consistently. Which in this case it is not.

The PR was turned over pretty quickly w/o giving any of the other long list of reviewers a chance to comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I had carefully reviewed it and I think it was fine, given the circumstances.

For reference, here are all the other uses:

clang/include/clang/AST/Attr.h:  DB.AddTaggedVal(reinterpret_cast<uint64_t>(At), DiagnosticsEngine::ak_attr);
clang/include/clang/AST/Expr.h:  DB.AddTaggedVal(reinterpret_cast<uint64_t>(E), DiagnosticsEngine::ak_expr);
clang/include/clang/AST/NestedNameSpecifier.h:  DB.AddTaggedVal(reinterpret_cast<uint64_t>(NNS),
clang/include/clang/Basic/Diagnostic.h:  DB.AddTaggedVal(reinterpret_cast<intptr_t>(Str),
clang/include/clang/Basic/Diagnostic.h:  DB.AddTaggedVal(I, DiagnosticsEngine::ak_sint);
clang/include/clang/Basic/Diagnostic.h:  DB.AddTaggedVal(I, DiagnosticsEngine::ak_sint);
clang/include/clang/Basic/Diagnostic.h:  DB.AddTaggedVal(I, DiagnosticsEngine::ak_sint);
clang/include/clang/Basic/Diagnostic.h:  DB.AddTaggedVal(I, DiagnosticsEngine::ak_sint);
clang/include/clang/Basic/Diagnostic.h:  DB.AddTaggedVal(I, DiagnosticsEngine::ak_uint);
clang/include/clang/Basic/Diagnostic.h:  DB.AddTaggedVal(I, DiagnosticsEngine::ak_uint);
clang/include/clang/Basic/Diagnostic.h:  DB.AddTaggedVal(I, DiagnosticsEngine::ak_uint);
clang/include/clang/Basic/Diagnostic.h:  DB.AddTaggedVal(static_cast<unsigned>(I), DiagnosticsEngine::ak_tokenkind);
clang/include/clang/Basic/Diagnostic.h:  DB.AddTaggedVal(reinterpret_cast<intptr_t>(II),
clang/include/clang/Basic/Diagnostic.h:  DB.AddTaggedVal(reinterpret_cast<intptr_t>(DC),
clang/include/clang/Basic/PartialDiagnostic.h:        DB.AddTaggedVal(DiagStorage->DiagArgumentsVal[i],
clang/include/clang/Sema/ParsedAttr.h:  DB.AddTaggedVal(reinterpret_cast<uint64_t>(At.getAttrName()),
clang/include/clang/Sema/ParsedAttr.h:  DB.AddTaggedVal(reinterpret_cast<uint64_t>(At->getAttrName()),
clang/include/clang/Sema/ParsedAttr.h:  DB.AddTaggedVal(reinterpret_cast<uint64_t>(CI.getAttrName()),
clang/include/clang/Sema/ParsedAttr.h:  DB.AddTaggedVal(reinterpret_cast<uint64_t>(CI->getAttrName()),

So 6 uint64_t vs 3 intptr_t (excluding this patch). Without going on and changing everything else, there is no single answer here which would be satisfy everyone.

@damyanp damyanp moved this to Closed in HLSL Support Apr 25, 2025
@damyanp damyanp removed this from HLSL Support Jun 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category clang-tidy clang-tools-extra HLSL HLSL Language Support libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants