jyu2 created this revision.
jyu2 added reviewers: ABataev, jdoerfert, mikerice.
jyu2 added a project: OpenMP.
Herald added subscribers: arphaman, guansong, yaxunl.
Herald added projects: Flang, All.
jyu2 requested review of this revision.
Herald added subscribers: llvm-commits, cfe-commits, sstefan1.
Herald added projects: clang, LLVM.

Initial support for message clause.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D138312

Files:
  clang/include/clang/AST/OpenMPClause.h
  clang/include/clang/AST/RecursiveASTVisitor.h
  clang/include/clang/Basic/DiagnosticParseKinds.td
  clang/include/clang/Sema/Sema.h
  clang/lib/AST/OpenMPClause.cpp
  clang/lib/AST/StmtProfile.cpp
  clang/lib/CodeGen/CGStmtOpenMP.cpp
  clang/lib/Parse/ParseOpenMP.cpp
  clang/lib/Sema/SemaOpenMP.cpp
  clang/lib/Sema/TreeTransform.h
  clang/lib/Serialization/ASTReader.cpp
  clang/lib/Serialization/ASTWriter.cpp
  clang/test/OpenMP/error_ast_print.cpp
  clang/test/OpenMP/error_message.cpp
  clang/tools/libclang/CIndex.cpp
  flang/lib/Semantics/check-omp-structure.cpp
  llvm/include/llvm/Frontend/OpenMP/OMP.td

Index: llvm/include/llvm/Frontend/OpenMP/OMP.td
===================================================================
--- llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -307,6 +307,9 @@
 def OMPC_Severity : Clause<"severity"> {
   let clangClass = "OMPSeverityClause";
 }
+def OMPC_Message : Clause<"message"> {
+  let clangClass = "OMPMessageClause";
+}
 def OMPC_Allocate : Clause<"allocate"> {
   let clangClass = "OMPAllocateClause";
   let flangClass = "OmpAllocateClause";
@@ -536,7 +539,8 @@
 def OMP_Error : Directive<"error"> {
   let allowedClauses = [
     VersionedClause<OMPC_At, 51>,
-    VersionedClause<OMPC_Severity, 51>
+    VersionedClause<OMPC_Severity, 51>,
+    VersionedClause<OMPC_Message, 51>
   ];
 }
 def OMP_TaskWait : Directive<"taskwait"> {
Index: flang/lib/Semantics/check-omp-structure.cpp
===================================================================
--- flang/lib/Semantics/check-omp-structure.cpp
+++ flang/lib/Semantics/check-omp-structure.cpp
@@ -1870,6 +1870,7 @@
 CHECK_SIMPLE_CLAUSE(Nocontext, OMPC_nocontext)
 CHECK_SIMPLE_CLAUSE(At, OMPC_at)
 CHECK_SIMPLE_CLAUSE(Severity, OMPC_severity)
+CHECK_SIMPLE_CLAUSE(Message, OMPC_message)
 CHECK_SIMPLE_CLAUSE(Filter, OMPC_filter)
 CHECK_SIMPLE_CLAUSE(When, OMPC_when)
 CHECK_SIMPLE_CLAUSE(AdjustArgs, OMPC_adjust_args)
Index: clang/tools/libclang/CIndex.cpp
===================================================================
--- clang/tools/libclang/CIndex.cpp
+++ clang/tools/libclang/CIndex.cpp
@@ -2447,6 +2447,8 @@
 
 void OMPClauseEnqueue::VisitOMPSeverityClause(const OMPSeverityClause *) {}
 
+void OMPClauseEnqueue::VisitOMPMessageClause(const OMPMessageClause *) {}
+
 void OMPClauseEnqueue::VisitOMPDeviceClause(const OMPDeviceClause *C) {
   Visitor->AddStmt(C->getDevice());
 }
Index: clang/test/OpenMP/error_message.cpp
===================================================================
--- clang/test/OpenMP/error_message.cpp
+++ clang/test/OpenMP/error_message.cpp
@@ -94,6 +94,26 @@
 #pragma omp error at(execution) severity(warning) // no error, diagnosic at runtime
 #pragma omp error at(compilation) severity(fatal) // expected-error {{ERROR}}
 #pragma omp error at(execution) severity(fatal) // no error, error at runtime
+
+#pragma omp error message("GPU compiler is needed.") // expected-error {{GPU compiler is needed}}
+#pragma omp error at(compilation) message("GPU compiler is needed.") // expected-error {{GPU compiler is needed}}
+#pragma omp error at(execution) message("GPU compiler is needed.") // no error
+// expected-warning@+1 {{GPU compiler is needed.}}
+#pragma omp error severity(warning) message("GPU compiler is needed.") // expected-warning {{GPU compiler is needed.}}
+#pragma omp error severity(fatal) message("GPU compiler is needed.") // expected-error {{GPU compiler is needed}}
+// expected-warning@+1 {{GPU compiler is needed.}}
+#pragma omp error at(compilation) severity(warning) message("GPU compiler is needed.") // expected-warning {{GPU compiler is needed.}}
+#pragma omp error at(compilation) severity(fatal) message("GPU compiler is needed.") // expected-error {{GPU compiler is needed.}}
+#pragma omp error at(execution) severity(warning) message("GPU compiler is needed.") // no warning warning will emit at runtime.
+#pragma omp error at(execution) severity(fatal) message("GPU compiler is needed.") // no warning warning will emit at runtime.
+
+// expected-error@+1 {{GPU compiler is needed.}}
+#pragma omp error message("GPU compiler is needed.") message("GPU compiler is needed.") // expected-error {{directive '#pragma omp error' cannot contain more than one 'message' clause}}
+  int a;
+// expected-warning@+1 {{expected string literal in 'clause message' - ignoring}}
+#pragma omp error message(a) // expected-error {{ERROR}}
+// expected-error@+1 {{ERROR}}
+#pragma omp error message() // expected-error {{expected expression}}
   return T();
 }
 
Index: clang/test/OpenMP/error_ast_print.cpp
===================================================================
--- clang/test/OpenMP/error_ast_print.cpp
+++ clang/test/OpenMP/error_ast_print.cpp
@@ -13,16 +13,16 @@
 void foo() {}
 // CHECK: template <typename T, int N> int tmain(T argc, char **argv)
 // CHECK: static int a;
-// CHECK-NEXT: #pragma omp error at(execution) severity(fatal)
+// CHECK-NEXT: #pragma omp error at(execution) severity(fatal) message("GNU compiler required.")
 // CHECK-NEXT: a = argv[0][0];
 // CHECK-NEXT: ++a;
-// CHECK-NEXT: #pragma omp error at(execution) severity(warning)
+// CHECK-NEXT: #pragma omp error at(execution) severity(warning) message("Notice: add for loop.")
 // CHECK-NEXT: {
 // CHECK-NEXT: int b = 10;
 // CHECK-NEXT: T c = 100;
 // CHECK-NEXT: a = b + c;
 // CHECK-NEXT: }
-// CHECK-NEXT: #pragma omp error at(execution) severity(fatal)
+// CHECK-NEXT: #pragma omp error at(execution) severity(fatal) message("GPU compiler required.")
 // CHECK-NEXT: foo();
 // CHECK-NEXT: return N;
 
@@ -30,16 +30,16 @@
 int tmain(T argc, char **argv) {
   T b = argc, c, d, e, f, g;
   static int a;
-#pragma omp error at(execution) severity(fatal)
+#pragma omp error at(execution) severity(fatal) message("GNU compiler required.")
   a = argv[0][0];
   ++a;
-#pragma omp error at(execution) severity(warning)
+#pragma omp error at(execution) severity(warning) message("Notice: add for loop.")
   {
     int b = 10;
     T c = 100;
     a = b + c;
   }
-#pragma omp  error at(execution) severity(fatal)
+#pragma omp  error at(execution) severity(fatal) message("GPU compiler required.")
   foo();
 return N;
 }
@@ -47,16 +47,16 @@
 // CHECK: int main(int argc, char **argv)
 // CHECK-NEXT: int b = argc, c, d, e, f, g;
 // CHECK-NEXT: static int a;
-// CHECK-NEXT: #pragma omp error at(execution) severity(fatal)
+// CHECK-NEXT: #pragma omp error at(execution) severity(fatal) message("GPU compiler required.")
 // CHECK-NEXT: a = 2;
-// CHECK-NEXT: #pragma omp error at(execution) severity(warning)
+// CHECK-NEXT: #pragma omp error at(execution) severity(warning) message("Note this is functioncall.")
 // CHECK-NEXT: foo();
 int main (int argc, char **argv) {
   int b = argc, c, d, e, f, g;
   static int a;
-#pragma omp error at(execution) severity(fatal)
+#pragma omp error at(execution) severity(fatal) message("GPU compiler required.")
    a=2;
-#pragma omp error at(execution) severity(warning)
+#pragma omp error at(execution) severity(warning) message("Note this is functioncall.")
   foo();
 }
 #endif
Index: clang/lib/Serialization/ASTWriter.cpp
===================================================================
--- clang/lib/Serialization/ASTWriter.cpp
+++ clang/lib/Serialization/ASTWriter.cpp
@@ -6993,6 +6993,11 @@
   Record.AddSourceLocation(C->getSeverityKindKwLoc());
 }
 
+void OMPClauseWriter::VisitOMPMessageClause(OMPMessageClause *C) {
+  Record.AddStmt(C->getMessageString());
+  Record.AddSourceLocation(C->getLParenLoc());
+}
+
 void OMPClauseWriter::VisitOMPNontemporalClause(OMPNontemporalClause *C) {
   Record.push_back(C->varlist_size());
   Record.AddSourceLocation(C->getLParenLoc());
Index: clang/lib/Serialization/ASTReader.cpp
===================================================================
--- clang/lib/Serialization/ASTReader.cpp
+++ clang/lib/Serialization/ASTReader.cpp
@@ -9955,6 +9955,9 @@
   case llvm::omp::OMPC_severity:
     C = new (Context) OMPSeverityClause();
     break;
+  case llvm::omp::OMPC_message:
+    C = new (Context) OMPMessageClause();
+    break;
   case llvm::omp::OMPC_private:
     C = OMPPrivateClause::CreateEmpty(Context, Record.readInt());
     break;
@@ -10369,6 +10372,11 @@
   C->setSeverityKindKwLoc(Record.readSourceLocation());
 }
 
+void OMPClauseReader::VisitOMPMessageClause(OMPMessageClause *C) {
+  C->setMessageString(Record.readSubExpr());
+  C->setLParenLoc(Record.readSourceLocation());
+}
+
 void OMPClauseReader::VisitOMPPrivateClause(OMPPrivateClause *C) {
   C->setLParenLoc(Record.readSourceLocation());
   unsigned NumVars = C->varlist_size();
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ clang/lib/Sema/TreeTransform.h
@@ -2376,6 +2376,16 @@
                                                EndLoc);
   }
 
+  /// Build a new OpenMP 'message' clause.
+  ///
+  /// By default, performs semantic analysis to build the new OpenMP clause.
+  /// Subclasses may override this routine to provide different behavior.
+  OMPClause *RebuildOMPMessageClause(Expr *MS, SourceLocation StartLoc,
+                                     SourceLocation LParenLoc,
+                                     SourceLocation EndLoc) {
+    return getSema().ActOnOpenMPMessageClause(MS, StartLoc, LParenLoc, EndLoc);
+  }
+
   /// Rebuild the operand to an Objective-C \@synchronized statement.
   ///
   /// By default, performs semantic analysis to build the new statement.
@@ -9895,6 +9905,17 @@
       C->getLParenLoc(), C->getEndLoc());
 }
 
+template <typename Derived>
+OMPClause *
+TreeTransform<Derived>::TransformOMPMessageClause(OMPMessageClause *C) {
+  ExprResult E = getDerived().TransformExpr(C->getMessageString());
+  if (E.isInvalid())
+    return nullptr;
+  return getDerived().RebuildOMPMessageClause(
+      C->getMessageString(), C->getBeginLoc(), C->getLParenLoc(),
+      C->getEndLoc());
+}
+
 template <typename Derived>
 OMPClause *
 TreeTransform<Derived>::TransformOMPPrivateClause(OMPPrivateClause *C) {
Index: clang/lib/Sema/SemaOpenMP.cpp
===================================================================
--- clang/lib/Sema/SemaOpenMP.cpp
+++ clang/lib/Sema/SemaOpenMP.cpp
@@ -6721,6 +6721,7 @@
       case OMPC_when:
       case OMPC_at:
       case OMPC_severity:
+      case OMPC_message:
       default:
         llvm_unreachable("Unexpected clause");
       }
@@ -11044,13 +11045,19 @@
       OMPExecutableDirective::getClausesOfKind<OMPSeverityClause>(Clauses);
   const OMPSeverityClause *SeverityC =
       SeverityClauses.empty() ? nullptr : (*SeverityClauses.begin());
+  auto MessageClauses =
+      OMPExecutableDirective::getClausesOfKind<OMPMessageClause>(Clauses);
+  const OMPMessageClause *MessageC =
+      MessageClauses.empty() ? nullptr : (*MessageClauses.begin());
+  Expr *ME = MessageC ? MessageC->getMessageString() : nullptr;
 
   if (!AtC || AtC->getAtKind() == OMPC_AT_compilation) {
     if (SeverityC && SeverityC->getSeverityKind() == OMPC_SEVERITY_warning)
       Diag(SeverityC->getSeverityKindKwLoc(), diag::warn_diagnose_if_succeeded)
-          << "WARNING";
+          << (ME ? cast<StringLiteral>(ME)->getString().data() : "WARNING");
     else
-      Diag(StartLoc, diag::err_diagnose_if_succeeded) << "ERROR";
+      Diag(StartLoc, diag::err_diagnose_if_succeeded)
+          << (ME ? cast<StringLiteral>(ME)->getString().data() : "ERROR");
     if (!SeverityC || SeverityC->getSeverityKind() != OMPC_SEVERITY_warning)
       return StmtError();
   }
@@ -15137,6 +15144,9 @@
   case OMPC_partial:
     Res = ActOnOpenMPPartialClause(Expr, StartLoc, LParenLoc, EndLoc);
     break;
+  case OMPC_message:
+    Res = ActOnOpenMPMessageClause(Expr, StartLoc, LParenLoc, EndLoc);
+    break;
   case OMPC_align:
     Res = ActOnOpenMPAlignClause(Expr, StartLoc, LParenLoc, EndLoc);
     break;
@@ -16125,6 +16135,7 @@
   case OMPC_order:
   case OMPC_at:
   case OMPC_severity:
+  case OMPC_message:
   case OMPC_destroy:
   case OMPC_detach:
   case OMPC_inclusive:
@@ -16611,6 +16622,7 @@
   case OMPC_uses_allocators:
   case OMPC_affinity:
   case OMPC_when:
+  case OMPC_message:
   default:
     llvm_unreachable("Clause is not allowed.");
   }
@@ -16746,6 +16758,18 @@
       OMPSeverityClause(Kind, KindKwLoc, StartLoc, LParenLoc, EndLoc);
 }
 
+OMPClause *Sema::ActOnOpenMPMessageClause(Expr *ME, SourceLocation StartLoc,
+                                          SourceLocation LParenLoc,
+                                          SourceLocation EndLoc) {
+  assert(ME && "NULL expr in Message clause");
+  if (!isa<StringLiteral>(ME)) {
+    Diag(ME->getBeginLoc(), diag::warn_clause_expected_string)
+        << getOpenMPClauseName(OMPC_message);
+    return nullptr;
+  }
+  return new (Context) OMPMessageClause(ME, StartLoc, LParenLoc, EndLoc);
+}
+
 OMPClause *Sema::ActOnOpenMPOrderClause(OpenMPOrderClauseKind Kind,
                                         SourceLocation KindKwLoc,
                                         SourceLocation StartLoc,
@@ -16947,6 +16971,7 @@
   case OMPC_order:
   case OMPC_at:
   case OMPC_severity:
+  case OMPC_message:
   case OMPC_destroy:
   case OMPC_novariants:
   case OMPC_nocontext:
@@ -17205,6 +17230,7 @@
   case OMPC_order:
   case OMPC_at:
   case OMPC_severity:
+  case OMPC_message:
   case OMPC_novariants:
   case OMPC_nocontext:
   case OMPC_detach:
@@ -17761,6 +17787,7 @@
   case OMPC_order:
   case OMPC_at:
   case OMPC_severity:
+  case OMPC_message:
   case OMPC_destroy:
   case OMPC_novariants:
   case OMPC_nocontext:
Index: clang/lib/Parse/ParseOpenMP.cpp
===================================================================
--- clang/lib/Parse/ParseOpenMP.cpp
+++ clang/lib/Parse/ParseOpenMP.cpp
@@ -3196,6 +3196,7 @@
   case OMPC_filter:
   case OMPC_partial:
   case OMPC_align:
+  case OMPC_message:
     // OpenMP [2.5, Restrictions]
     //  At most one num_threads clause can appear on the directive.
     // OpenMP [2.8.1, simd construct, Restrictions]
@@ -3221,6 +3222,8 @@
     // OpenMP 5.1, 2.3.6 dispatch Construct, Restrictions.
     // At most one novariants clause can appear on a dispatch directive.
     // At most one nocontext clause can appear on a dispatch directive.
+    // OpenMP [5.1, error directive, Restrictions]
+    // At most one message clause can appear on the directive
     if (!FirstClause) {
       Diag(Tok, diag::err_omp_more_one_clause)
           << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind) << 0;
Index: clang/lib/CodeGen/CGStmtOpenMP.cpp
===================================================================
--- clang/lib/CodeGen/CGStmtOpenMP.cpp
+++ clang/lib/CodeGen/CGStmtOpenMP.cpp
@@ -6514,6 +6514,7 @@
   case OMPC_atomic_default_mem_order:
   case OMPC_at:
   case OMPC_severity:
+  case OMPC_message:
   case OMPC_device_type:
   case OMPC_match:
   case OMPC_nontemporal:
Index: clang/lib/AST/StmtProfile.cpp
===================================================================
--- clang/lib/AST/StmtProfile.cpp
+++ clang/lib/AST/StmtProfile.cpp
@@ -534,6 +534,11 @@
 
 void OMPClauseProfiler::VisitOMPSeverityClause(const OMPSeverityClause *C) {}
 
+void OMPClauseProfiler::VisitOMPMessageClause(const OMPMessageClause *C) {
+  if (C->getMessageString())
+    Profiler->VisitStmt(C->getMessageString());
+}
+
 void OMPClauseProfiler::VisitOMPScheduleClause(const OMPScheduleClause *C) {
   VistOMPClauseWithPreInit(C);
   if (auto *S = C->getChunkSize())
Index: clang/lib/AST/OpenMPClause.cpp
===================================================================
--- clang/lib/AST/OpenMPClause.cpp
+++ clang/lib/AST/OpenMPClause.cpp
@@ -154,6 +154,7 @@
   case OMPC_atomic_default_mem_order:
   case OMPC_at:
   case OMPC_severity:
+  case OMPC_message:
   case OMPC_device_type:
   case OMPC_match:
   case OMPC_nontemporal:
@@ -255,6 +256,7 @@
   case OMPC_atomic_default_mem_order:
   case OMPC_at:
   case OMPC_severity:
+  case OMPC_message:
   case OMPC_device_type:
   case OMPC_match:
   case OMPC_nontemporal:
@@ -1796,6 +1798,12 @@
      << ")";
 }
 
+void OMPClausePrinter::VisitOMPMessageClause(OMPMessageClause *Node) {
+  OS << "message(\""
+     << cast<StringLiteral>(Node->getMessageString())->getString().data()
+     << "\")";
+}
+
 void OMPClausePrinter::VisitOMPScheduleClause(OMPScheduleClause *Node) {
   OS << "schedule(";
   if (Node->getFirstScheduleModifier() != OMPC_SCHEDULE_MODIFIER_unknown) {
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -11843,6 +11843,12 @@
                                        SourceLocation LParenLoc,
                                        SourceLocation EndLoc);
 
+  /// Called on well-formed 'message' clause.
+  /// passing string for message.
+  OMPClause *ActOnOpenMPMessageClause(Expr *MS, SourceLocation StartLoc,
+                                      SourceLocation LParenLoc,
+                                      SourceLocation EndLoc);
+
   /// Data used for processing a list of variables in OpenMP clauses.
   struct OpenMPVarListDataTy final {
     Expr *DepModOrTailExpr = nullptr;
Index: clang/include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticParseKinds.td
+++ clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1341,6 +1341,8 @@
   "unexpected OpenMP directive %select{|'#pragma omp %1'}0">;
 def err_omp_expected_punc : Error<
   "expected ',' or ')' in '%0' %select{clause|directive}1">;
+def warn_clause_expected_string : Warning<
+  "expected string literal in 'clause %0' - ignoring">, InGroup<IgnoredPragmas>;
 def err_omp_unexpected_clause : Error<
   "unexpected OpenMP clause '%0' in directive '#pragma omp %1'">;
 def err_omp_immediate_directive : Error<
Index: clang/include/clang/AST/RecursiveASTVisitor.h
===================================================================
--- clang/include/clang/AST/RecursiveASTVisitor.h
+++ clang/include/clang/AST/RecursiveASTVisitor.h
@@ -3320,6 +3320,12 @@
   return true;
 }
 
+template <typename Derived>
+bool RecursiveASTVisitor<Derived>::VisitOMPMessageClause(OMPMessageClause *C) {
+  TRY_TO(TraverseStmt(C->getMessageString()));
+  return true;
+}
+
 template <typename Derived>
 bool
 RecursiveASTVisitor<Derived>::VisitOMPScheduleClause(OMPScheduleClause *C) {
Index: clang/include/clang/AST/OpenMPClause.h
===================================================================
--- clang/include/clang/AST/OpenMPClause.h
+++ clang/include/clang/AST/OpenMPClause.h
@@ -1722,6 +1722,72 @@
   }
 };
 
+/// This represents 'message' clause in the '#pragma omp error' directive
+///
+/// \code
+/// #pragma omp error message("GNU compiler required.")
+/// \endcode
+/// In this example directive '#pragma omp error' has simple
+/// 'message' clause with user error message of "GNU compiler required.".
+class OMPMessageClause final : public OMPClause {
+  friend class OMPClauseReader;
+
+  /// Location of '('
+  SourceLocation LParenLoc;
+
+  // Expression of the 'message' clause.
+  Stmt *MessageString = nullptr;
+
+  /// Set message string of the clause.
+  void setMessageString(Expr *MS) { MessageString = MS; }
+
+  /// Sets the location of '('.
+  void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; }
+
+public:
+  /// Build 'message' clause with message string argument
+  ///
+  /// \param A Argument of the clause (message string).
+  /// \param StartLoc Starting location of the clause.
+  /// \param LParenLoc Location of '('.
+  /// \param EndLoc Ending location of the clause.
+  OMPMessageClause(Expr *MS, SourceLocation StartLoc, SourceLocation LParenLoc,
+                   SourceLocation EndLoc)
+      : OMPClause(llvm::omp::OMPC_message, StartLoc, EndLoc),
+        LParenLoc(LParenLoc), MessageString(MS) {}
+
+  /// Build an empty clause.
+  OMPMessageClause()
+      : OMPClause(llvm::omp::OMPC_message, SourceLocation(), SourceLocation()) {
+  }
+
+  /// Returns the locaiton of '('.
+  SourceLocation getLParenLoc() const { return LParenLoc; }
+
+  /// Returns message string of the clause.
+  Expr *getMessageString() const { return cast_or_null<Expr>(MessageString); }
+
+  child_range children() {
+    return child_range(&MessageString, &MessageString + 1);
+  }
+
+  const_child_range children() const {
+    return const_child_range(&MessageString, &MessageString + 1);
+  }
+
+  child_range used_children() {
+    return child_range(child_iterator(), child_iterator());
+  }
+
+  const_child_range used_children() const {
+    return const_child_range(const_child_iterator(), const_child_iterator());
+  }
+
+  static bool classof(const OMPClause *T) {
+    return T->getClauseKind() == llvm::omp::OMPC_message;
+  }
+};
+
 /// This represents 'schedule' clause in the '#pragma omp ...' directive.
 ///
 /// \code
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to