nickdesaulniers updated this revision to Diff 358814.
nickdesaulniers added a comment.
- remove accidentally committed logging statement
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D106030/new/
https://reviews.llvm.org/D106030
Files:
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/AttrDocs.td
clang/include/clang/Basic/DiagnosticFrontendKinds.td
clang/include/clang/Basic/DiagnosticGroups.td
clang/lib/CodeGen/CGCall.cpp
clang/lib/CodeGen/CodeGenAction.cpp
clang/lib/CodeGen/CodeGenModule.cpp
clang/lib/Sema/SemaDeclAttr.cpp
clang/test/CodeGen/attr-error.c
clang/test/CodeGen/attr-warning.c
clang/test/Frontend/backend-attribute-error-warning-optimize.c
clang/test/Frontend/backend-attribute-error-warning.c
clang/test/Frontend/backend-attribute-error-warning.cpp
clang/test/Misc/pragma-attribute-supported-attributes-list.test
clang/test/Sema/attr-error.c
clang/test/Sema/attr-warning.c
llvm/docs/LangRef.rst
llvm/include/llvm/IR/DiagnosticInfo.h
llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
llvm/lib/IR/DiagnosticInfo.cpp
llvm/test/CodeGen/Generic/attr-user-diagnostic.ll
Index: llvm/test/CodeGen/Generic/attr-user-diagnostic.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/Generic/attr-user-diagnostic.ll
@@ -0,0 +1,9 @@
+; RUN: not llc -stop-after=pre-isel-intrinsic-lowering %s 2>&1 | FileCheck %s
+
+declare void @foo() "user-diagnostic"="oh no"
+define void @bar() {
+ call void @foo()
+ ret void
+}
+
+; CHECK: error: call to foo: oh no
Index: llvm/lib/IR/DiagnosticInfo.cpp
===================================================================
--- llvm/lib/IR/DiagnosticInfo.cpp
+++ llvm/lib/IR/DiagnosticInfo.cpp
@@ -401,3 +401,7 @@
void OptimizationRemarkAnalysisFPCommute::anchor() {}
void OptimizationRemarkAnalysisAliasing::anchor() {}
+
+void DiagnosticInfoUser::print(DiagnosticPrinter &DP) const {
+ DP << "call to " << getFunctionName() << ": " << getMsgStr();
+}
Index: llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
===================================================================
--- llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
+++ llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
@@ -14,6 +14,7 @@
#include "llvm/CodeGen/PreISelIntrinsicLowering.h"
#include "llvm/Analysis/ObjCARCInstKind.h"
#include "llvm/CodeGen/Passes.h"
+#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
@@ -204,6 +205,25 @@
Changed |= lowerObjCCall(F, "objc_sync_exit");
break;
}
+ // TODO: is this the best pass for this?
+ for (BasicBlock &BB : F) {
+ for (Instruction &I : BB) {
+ if (CallBase *CB = dyn_cast<CallBase>(&I)) {
+ if (Function *Callee = CB->getCalledFunction()) {
+ if (Callee->hasFnAttribute("user-diagnostic")) {
+ unsigned LocCookie = 0;
+ if (MDNode *MD = CB->getMetadata("srcloc"))
+ LocCookie = mdconst::extract<ConstantInt>(MD->getOperand(0))
+ ->getZExtValue();
+ DiagnosticInfoUser DU(
+ Callee->getFnAttribute("user-diagnostic").getValueAsString(),
+ Callee->getName(), LocCookie);
+ F.getContext().diagnose(DU);
+ }
+ }
+ }
+ }
+ }
}
return Changed;
}
Index: llvm/include/llvm/IR/DiagnosticInfo.h
===================================================================
--- llvm/include/llvm/IR/DiagnosticInfo.h
+++ llvm/include/llvm/IR/DiagnosticInfo.h
@@ -79,6 +79,7 @@
DK_PGOProfile,
DK_Unsupported,
DK_SrcMgr,
+ DK_UserDiagnostic,
DK_FirstPluginKind // Must be last value to work with
// getNextAvailablePluginDiagnosticKind
};
@@ -1070,6 +1071,25 @@
}
};
+class DiagnosticInfoUser : public DiagnosticInfo {
+ StringRef MsgStr;
+ StringRef CalleeName;
+ unsigned LocCookie;
+
+public:
+ DiagnosticInfoUser(StringRef MsgStr, StringRef CalleeName, unsigned LocCookie,
+ DiagnosticSeverity Severity = DS_Error)
+ : DiagnosticInfo(DK_UserDiagnostic, Severity), MsgStr(MsgStr),
+ CalleeName(CalleeName), LocCookie(LocCookie) {}
+ StringRef getMsgStr() const { return MsgStr; }
+ StringRef getFunctionName() const { return CalleeName; }
+ unsigned getLocCookie() const { return LocCookie; }
+ void print(DiagnosticPrinter &DP) const override;
+ static bool classof(const DiagnosticInfo *DI) {
+ return DI->getKind() == DK_UserDiagnostic;
+ }
+};
+
} // end namespace llvm
#endif // LLVM_IR_DIAGNOSTICINFO_H
Index: llvm/docs/LangRef.rst
===================================================================
--- llvm/docs/LangRef.rst
+++ llvm/docs/LangRef.rst
@@ -2037,6 +2037,12 @@
show that no exceptions passes by it. This is normally the case for
the ELF x86-64 abi, but it can be disabled for some compilation
units.
+``"user-diagnostic"="<diagnostic string>"``
+ This attribute provides a diagnostic string to emit when a call of a
+ function with this attribute is not eliminated via optimization. Front ends
+ can provide optional ``srcloc`` metadata nodes on call sites of such
+ callees to attach information about where in the source language such a
+ call came from.
``nocf_check``
This attribute indicates that no control-flow check will be performed on
the attributed entity. It disables -fcf-protection=<> for a specific
Index: clang/test/Sema/attr-warning.c
===================================================================
--- /dev/null
+++ clang/test/Sema/attr-warning.c
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+#if !__has_attribute(warning)
+#warning "warning attribute missing"
+#endif
+
+__attribute__((warning("don't call me!"))) int foo(void);
+
+__attribute__((warning)) // expected-error {{'warning' attribute takes one argument}}
+int
+bad0(void);
+
+int bad1(__attribute__((warning("bad1"))) int param); // expected-warning {{'warning' attribute only applies to functions}}
+
+int bad2(void) {
+ __attribute__((warning("bad2"))); // expected-error {{'warning' attribute cannot be applied to a statement}}
+}
+
+__attribute__((warning(3))) // expected-error {{'warning' attribute requires a string}}
+int
+bad3(void);
Index: clang/test/Sema/attr-error.c
===================================================================
--- /dev/null
+++ clang/test/Sema/attr-error.c
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+#if !__has_attribute(error)
+#error "error attribute missing"
+#endif
+
+__attribute__((error("don't call me!"))) int foo(void);
+
+__attribute__((error)) // expected-error {{'error' attribute takes one argument}}
+int
+bad0(void);
+
+int bad1(__attribute__((error("bad1"))) int param); // expected-error {{'error' attribute only applies to functions}}
+
+int bad2(void) {
+ __attribute__((error("bad2"))); // expected-error {{'error' attribute cannot be applied to a statement}}
+}
+
+__attribute__((error(3))) // expected-error {{'error' attribute requires a string}}
+int
+bad3(void);
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -61,6 +61,7 @@
// CHECK-NEXT: EnforceTCB (SubjectMatchRule_function)
// CHECK-NEXT: EnforceTCBLeaf (SubjectMatchRule_function)
// CHECK-NEXT: EnumExtensibility (SubjectMatchRule_enum)
+// CHECK-NEXT: Error (SubjectMatchRule_function)
// CHECK-NEXT: ExcludeFromExplicitInstantiation (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record)
// CHECK-NEXT: ExternalSourceSymbol ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable))
// CHECK-NEXT: FlagEnum (SubjectMatchRule_enum)
@@ -177,6 +178,7 @@
// CHECK-NEXT: VecTypeHint (SubjectMatchRule_function)
// CHECK-NEXT: WarnUnused (SubjectMatchRule_record)
// CHECK-NEXT: WarnUnusedResult (SubjectMatchRule_objc_method, SubjectMatchRule_enum, SubjectMatchRule_record, SubjectMatchRule_hasType_functionType)
+// CHECK-NEXT: Warning (SubjectMatchRule_function)
// CHECK-NEXT: Weak (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record)
// CHECK-NEXT: WeakRef (SubjectMatchRule_variable, SubjectMatchRule_function)
// CHECK-NEXT: WebAssemblyExportName (SubjectMatchRule_function)
Index: clang/test/Frontend/backend-attribute-error-warning.cpp
===================================================================
--- /dev/null
+++ clang/test/Frontend/backend-attribute-error-warning.cpp
@@ -0,0 +1,29 @@
+// REQUIRES: x86-registered-target
+// RUN: %clang_cc1 -verify -emit-codegen-only %s
+
+__attribute__((error("oh no foo"))) void foo(void);
+
+__attribute__((error("oh no bar"))) void bar(void);
+
+int x(void) {
+ return 8 % 2 == 1;
+}
+
+__attribute__((warning("oh no quux"))) void quux(void);
+
+__attribute__((error("demangle me"))) void __compiletime_assert_455(void);
+
+template <typename T>
+__attribute__((error("demangle me, too")))
+T
+nocall(T t);
+
+void baz(void) {
+ // Test that we demangle correctly in the diagnostic for C++.
+ foo(); // expected-error {{call to foo declared with attribute error: oh no foo}}
+ if (x())
+ bar(); // expected-error {{call to bar declared with attribute error: oh no bar}}
+ quux(); // expected-warning {{call to quux declared with attribute warning: oh no quux}}
+ __compiletime_assert_455(); // expected-error {{call to __compiletime_assert_455 declared with attribute error: demangle me}}
+ nocall<int>(42); // expected-error {{call to nocall declared with attribute error: demangle me, too}}
+}
Index: clang/test/Frontend/backend-attribute-error-warning.c
===================================================================
--- /dev/null
+++ clang/test/Frontend/backend-attribute-error-warning.c
@@ -0,0 +1,22 @@
+// REQUIRES: x86-registered-target
+// RUN: %clang_cc1 -verify -emit-codegen-only %s
+
+__attribute__((error("oh no foo"))) void foo(void);
+
+__attribute__((error("oh no bar"))) void bar(void);
+
+int x(void) {
+ return 8 % 2 == 1;
+}
+
+__attribute__((warning("oh no quux"))) void quux(void);
+
+__attribute__((error("demangle me"))) void __compiletime_assert_455(void);
+
+void baz(void) {
+ foo(); // expected-error {{call to foo declared with attribute error: oh no foo}}
+ if (x())
+ bar(); // expected-error {{call to bar declared with attribute error: oh no bar}}
+ quux(); // expected-warning {{call to quux declared with attribute warning: oh no quux}}
+ __compiletime_assert_455(); // expected-error {{call to __compiletime_assert_455 declared with attribute error: demangle me}}
+}
Index: clang/test/Frontend/backend-attribute-error-warning-optimize.c
===================================================================
--- /dev/null
+++ clang/test/Frontend/backend-attribute-error-warning-optimize.c
@@ -0,0 +1,15 @@
+// REQUIRES: x86-registered-target
+// RUN: %clang_cc1 -O2 -verify -emit-codegen-only %s
+
+__attribute__((error("oh no foo"))) void foo(void);
+
+__attribute__((error("oh no bar"))) void bar(void);
+
+int x(void) {
+ return 8 % 2 == 1;
+}
+void baz(void) {
+ foo(); // expected-error {{call to foo declared with attribute error: oh no foo}}
+ if (x())
+ bar();
+}
Index: clang/test/CodeGen/attr-warning.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/attr-warning.c
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+__attribute__((warning("oh no"))) void foo(void);
+
+void bar(void) {
+ foo();
+}
+
+// CHECK: call void @foo(), !srcloc [[SRCLOC:![0-9]+]]
+// CHECK: declare void @foo() [[ATTR:#[0-9]+]]
+// CHECK: attributes [[ATTR]] = {{{.*}}"user-diagnostic"="oh no"
+// CHECK: [[SRCLOC]] = !{i32 {{[0-9]+}}}
Index: clang/test/CodeGen/attr-error.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/attr-error.c
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+__attribute__((error("oh no"))) void foo(void);
+
+void bar(void) {
+ foo();
+}
+
+// CHECK: call void @foo(), !srcloc [[SRCLOC:![0-9]+]]
+// CHECK: declare void @foo() [[ATTR:#[0-9]+]]
+// CHECK: attributes [[ATTR]] = {{{.*}}"user-diagnostic"="oh no"
+// CHECK: [[SRCLOC]] = !{i32 {{[0-9]+}}}
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -946,6 +946,19 @@
D->addAttr(::new (S.Context) EnableIfAttr(S.Context, AL, Cond, Msg));
}
+static void handleErrorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+ StringRef Str;
+ if (!S.checkStringLiteralArgumentAttr(AL, 0, Str))
+ return;
+ D->addAttr(::new (S.Context) ErrorAttr(S.Context, AL, Str));
+}
+static void handleWarningAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+ StringRef Str;
+ if (!S.checkStringLiteralArgumentAttr(AL, 0, Str))
+ return;
+ D->addAttr(::new (S.Context) WarningAttr(S.Context, AL, Str));
+}
+
namespace {
/// Determines if a given Expr references any of the given function's
/// ParmVarDecls, or the function's implicit `this` parameter (if applicable).
@@ -7843,6 +7856,9 @@
case ParsedAttr::AT_EnableIf:
handleEnableIfAttr(S, D, AL);
break;
+ case ParsedAttr::AT_Error:
+ handleErrorAttr(S, D, AL);
+ break;
case ParsedAttr::AT_DiagnoseIf:
handleDiagnoseIfAttr(S, D, AL);
break;
@@ -8058,6 +8074,9 @@
case ParsedAttr::AT_TypeVisibility:
handleVisibilityAttr(S, D, AL, true);
break;
+ case ParsedAttr::AT_Warning:
+ handleWarningAttr(S, D, AL);
+ break;
case ParsedAttr::AT_WarnUnusedResult:
handleWarnUnusedResult(S, D, AL);
break;
Index: clang/lib/CodeGen/CodeGenModule.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenModule.cpp
+++ clang/lib/CodeGen/CodeGenModule.cpp
@@ -2172,6 +2172,13 @@
CalleeIdx, PayloadIndices,
/* VarArgsArePassed */ false)}));
}
+
+ // TODO: move to CodeGenModule::ConstructAttributeList() or
+ // CodeGenFunction::EmitCall() ???
+ if (const auto *EA = FD->getAttr<ErrorAttr>())
+ F->addFnAttr("user-diagnostic", EA->getUserDiagnostic());
+ if (const auto *WA = FD->getAttr<WarningAttr>())
+ F->addFnAttr("user-diagnostic", WA->getUserDiagnostic());
}
void CodeGenModule::addUsedGlobal(llvm::GlobalValue *GV) {
Index: clang/lib/CodeGen/CodeGenAction.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenAction.cpp
+++ clang/lib/CodeGen/CodeGenAction.cpp
@@ -401,6 +401,7 @@
const llvm::OptimizationRemarkAnalysisAliasing &D);
void OptimizationFailureHandler(
const llvm::DiagnosticInfoOptimizationFailure &D);
+ void UserDiagnosticHandler(const DiagnosticInfoUser &D);
};
void BackendConsumer::anchor() {}
@@ -758,6 +759,30 @@
EmitOptimizationMessage(D, diag::warn_fe_backend_optimization_failure);
}
+void BackendConsumer::UserDiagnosticHandler(const DiagnosticInfoUser &D) {
+ unsigned DiagID = diag::err_fe_backend_user_diagnostic;
+ llvm::StringRef CalleeName = D.getFunctionName();
+ SourceLocation LocCookie =
+ SourceLocation::getFromRawEncoding(D.getLocCookie());
+
+ if (const Decl *D = Gen->GetDeclForMangledName(CalleeName)) {
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ assert((FD->hasAttr<ErrorAttr>() || FD->hasAttr<WarningAttr>()) &&
+ "expected error or warning function attribute");
+
+ if (FD->hasAttr<WarningAttr>())
+ DiagID = diag::warn_fe_backend_user_diagnostic;
+
+ CalleeName = FD->getName();
+ }
+ }
+
+ if (LocCookie.isValid())
+ Diags.Report(LocCookie, DiagID) << CalleeName << D.getMsgStr();
+ else
+ Diags.Report(DiagID) << CalleeName << D.getMsgStr();
+}
+
/// This function is invoked when the backend needs
/// to report something to the user.
void BackendConsumer::DiagnosticHandlerImpl(const DiagnosticInfo &DI) {
@@ -833,6 +858,9 @@
case llvm::DK_Unsupported:
UnsupportedDiagHandler(cast<DiagnosticInfoUnsupported>(DI));
return;
+ case llvm::DK_UserDiagnostic:
+ UserDiagnosticHandler(cast<DiagnosticInfoUser>(DI));
+ return;
default:
// Plugin IDs are not bound to any value as they are set dynamically.
ComputeDiagRemarkID(Severity, backend_plugin, DiagID);
Index: clang/lib/CodeGen/CGCall.cpp
===================================================================
--- clang/lib/CodeGen/CGCall.cpp
+++ clang/lib/CodeGen/CGCall.cpp
@@ -5304,6 +5304,17 @@
TargetDecl->hasAttr<MSAllocatorAttr>())
getDebugInfo()->addHeapAllocSiteMetadata(CI, RetTy->getPointeeType(), Loc);
+ // Add metadata if calling an __attribute__((error(""))) or warning fn.
+ if (TargetDecl)
+ if (TargetDecl->hasAttr<ErrorAttr>() ||
+ TargetDecl->hasAttr<WarningAttr>()) {
+ llvm::ConstantInt *Line =
+ llvm::ConstantInt::get(Int32Ty, Loc.getRawEncoding());
+ llvm::ConstantAsMetadata *MD = llvm::ConstantAsMetadata::get(Line);
+ llvm::MDTuple *MDT = llvm::MDNode::get(getLLVMContext(), {MD});
+ CI->setMetadata("srcloc", MDT);
+ }
+
// 4. Finish the call.
// If the call doesn't return, finish the basic block and clear the
Index: clang/include/clang/Basic/DiagnosticGroups.td
===================================================================
--- clang/include/clang/Basic/DiagnosticGroups.td
+++ clang/include/clang/Basic/DiagnosticGroups.td
@@ -1202,6 +1202,7 @@
def BackendOptimizationRemarkMissed : DiagGroup<"pass-missed">;
def BackendOptimizationRemarkAnalysis : DiagGroup<"pass-analysis">;
def BackendOptimizationFailure : DiagGroup<"pass-failed">;
+def BackendUserDiagnostic : DiagGroup<"user-diagnostic">;
// Instrumentation based profiling warnings.
def ProfileInstrMissing : DiagGroup<"profile-instr-missing">;
Index: clang/include/clang/Basic/DiagnosticFrontendKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -72,6 +72,11 @@
def err_fe_backend_unsupported : Error<"%0">, BackendInfo;
def warn_fe_backend_unsupported : Warning<"%0">, BackendInfo;
+def err_fe_backend_user_diagnostic :
+ Error<"call to %0 declared with attribute error: %1">, BackendInfo;
+def warn_fe_backend_user_diagnostic :
+ Warning<"call to %0 declared with attribute warning: %1">, BackendInfo, InGroup<BackendUserDiagnostic>;
+
def err_fe_invalid_code_complete_file : Error<
"cannot locate code-completion file %0">, DefaultFatal;
def err_fe_dependency_file_requires_MT : Error<
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -6015,3 +6015,24 @@
- ``enforce_tcb_leaf(Name)`` indicates that this function is a part of the TCB named ``Name``
}];
}
+
+def ErrorAttrDocs : Documentation {
+ let Category = DocCatFunction;
+ let Content = [{
+ The ``error`` function attribute can be used to specify a custom diagnostic
+ to be emitted when a call to such a function is not eliminated via
+ optimizations. This can be used to create compile time assertions that
+ depend on optimizations, while providing diagnostics pointing to precise
+ locations of the call site in the source.
+ }];
+}
+def WarningAttrDocs : Documentation {
+ let Category = DocCatFunction;
+ let Content = [{
+ The ``warning`` function attribute can be used to specify a custom
+ diagnostic to be emitted when a call to such a function is not eliminated
+ via optimizations. This can be used to create compile time assertions that
+ depend on optimizations, while providing diagnostics pointing to precise
+ locations of the call site in the source.
+ }];
+}
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -3812,3 +3812,17 @@
let Documentation = [EnforceTCBLeafDocs];
bit InheritEvenIfAlreadyPresent = 1;
}
+
+def Error : Attr {
+ let Spellings = [GCC<"error">];
+ let Args = [StringArgument<"UserDiagnostic">];
+ let Subjects = SubjectList<[Function], ErrorDiag>;
+ let Documentation = [ErrorAttrDocs];
+}
+
+def Warning : Attr {
+ let Spellings = [GCC<"warning">];
+ let Args = [StringArgument<"UserDiagnostic">];
+ let Subjects = SubjectList<[Function]>;
+ let Documentation = [WarningAttrDocs];
+}
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -122,7 +122,8 @@
C Language Changes in Clang
---------------------------
-- ...
+- Support for ``__attribute__((error("")))`` and
+ ``__attribute__((warning("")))`` function attributes have been added.
C++ Language Changes in Clang
-----------------------------
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits