george.burgess.iv created this revision.
george.burgess.iv added a reviewer: rsmith.
george.burgess.iv added a subscriber: cfe-commits.
Test case:
```
int foo(int A) __attribute__((enable_if(A == 0, "")));
template <int A> int bar() { return foo(A); }
int G = bar<1>(); // calls foo(1), which should be a compile-time error, but
isn't.
```
We get around this by making `CheckEnableIf` fail all value dependent
`enable_if` conditions, and report whether the condition may have failed due to
a dependent value. If we fail due for this reason during overload resolution,
we hand back an unresolved, type-dependent call expression, because the
following code is perfectly legal:
```
int ThisIsABadIdea(int A) __attribute__((enable_if(A == 1, "")));
double ThisIsABadIdea(int A) __attribute__((enable_if(A == 2, "")));
```
...But if we're not in overload resolution, we can get away with just marking
the expression as value dependent, because we know our target. :)
http://reviews.llvm.org/D18425
Files:
include/clang/Sema/Sema.h
lib/Sema/SemaExpr.cpp
lib/Sema/SemaOverload.cpp
test/SemaCXX/enable_if.cpp
Index: test/SemaCXX/enable_if.cpp
===================================================================
--- test/SemaCXX/enable_if.cpp
+++ test/SemaCXX/enable_if.cpp
@@ -133,13 +133,18 @@
int t1 = y.h(1, 2); // expected-error{{no matching member function for call to 'h'}}
}
-// FIXME: issue an error (without instantiation) because ::h(T()) is not
-// convertible to bool, because return types aren't overloadable.
+void ovlH(int);
+int ovlH(double);
void h(int);
-template <typename T> void outer() {
- void local_function() __attribute__((enable_if(::h(T()), "")));
+template <typename T> int outer() {
+ void local_function() __attribute__((enable_if(::h(T()), ""))); // expected-error{{value of type 'void' is not contextually convertible to 'bool'}}
+ void local_function2() __attribute__((enable_if(::ovlH(T()), ""))); // expected-error{{value of type 'void' is not contextually convertible to 'bool'}}
local_function();
-};
+ local_function2();
+ return 0;
+}
+
+int runOuter = outer<int>(); // expected-note{{in instantiation of function template specialization 'outer<int>'}}
namespace PR20988 {
struct Integer {
@@ -191,11 +196,11 @@
int ovlConflict(int m) __attribute__((enable_if(true, "")));
int ovlConflict(int m) __attribute__((enable_if(1, "")));
void test3() {
- int (*p)(int) = ovlConflict; // expected-error{{address of overloaded function 'ovlConflict' is ambiguous}} expected-note@191{{candidate function}} expected-note@192{{candidate function}}
- int (*p2)(int) = &ovlConflict; // expected-error{{address of overloaded function 'ovlConflict' is ambiguous}} expected-note@191{{candidate function}} expected-note@192{{candidate function}}
+ int (*p)(int) = ovlConflict; // expected-error{{address of overloaded function 'ovlConflict' is ambiguous}} expected-note@196{{candidate function}} expected-note@197{{candidate function}}
+ int (*p2)(int) = &ovlConflict; // expected-error{{address of overloaded function 'ovlConflict' is ambiguous}} expected-note@196{{candidate function}} expected-note@197{{candidate function}}
int (*a)(int);
- a = ovlConflict; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@191{{candidate function}} expected-note@192{{candidate function}}
- a = &ovlConflict; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@191{{candidate function}} expected-note@192{{candidate function}}
+ a = ovlConflict; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@196{{candidate function}} expected-note@197{{candidate function}}
+ a = &ovlConflict; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@196{{candidate function}} expected-note@197{{candidate function}}
}
template <typename T>
@@ -213,11 +218,11 @@
template <typename T>
T templatedBar(T m) __attribute__((enable_if(m > 0, ""))) { return T(); }
void test5() {
- int (*p)(int) = templatedBar<int>; // expected-error{{address of overloaded function 'templatedBar' does not match required type 'int (int)'}} expected-note@214{{candidate function made ineligible by enable_if}}
- int (*p2)(int) = &templatedBar<int>; // expected-error{{address of overloaded function 'templatedBar' does not match required type 'int (int)'}} expected-note@214{{candidate function made ineligible by enable_if}}
+ int (*p)(int) = templatedBar<int>; // expected-error{{address of overloaded function 'templatedBar' does not match required type 'int (int)'}} expected-note@219{{candidate function made ineligible by enable_if}}
+ int (*p2)(int) = &templatedBar<int>; // expected-error{{address of overloaded function 'templatedBar' does not match required type 'int (int)'}} expected-note@219{{candidate function made ineligible by enable_if}}
int (*a)(int);
- a = templatedBar<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@214{{candidate function made ineligible by enable_if}}
- a = &templatedBar<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@214{{candidate function made ineligible by enable_if}}
+ a = templatedBar<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@219{{candidate function made ineligible by enable_if}}
+ a = &templatedBar<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@219{{candidate function made ineligible by enable_if}}
}
template <typename T>
@@ -227,21 +232,21 @@
template <typename T>
T templatedConflict(T m) __attribute__((enable_if(1, ""))) { return T(); }
void test6() {
- int (*p)(int) = templatedConflict<int>; // expected-error{{address of overloaded function 'templatedConflict' is ambiguous}} expected-note@224{{candidate function made ineligible by enable_if}} expected-note@226{{candidate function}} expected-note@228{{candidate function}}
- int (*p0)(int) = &templatedConflict<int>; // expected-error{{address of overloaded function 'templatedConflict' is ambiguous}} expected-note@224{{candidate function made ineligible by enable_if}} expected-note@226{{candidate function}} expected-note@228{{candidate function}}
+ int (*p)(int) = templatedConflict<int>; // expected-error{{address of overloaded function 'templatedConflict' is ambiguous}} expected-note@229{{candidate function made ineligible by enable_if}} expected-note@231{{candidate function}} expected-note@233{{candidate function}}
+ int (*p0)(int) = &templatedConflict<int>; // expected-error{{address of overloaded function 'templatedConflict' is ambiguous}} expected-note@229{{candidate function made ineligible by enable_if}} expected-note@231{{candidate function}} expected-note@233{{candidate function}}
int (*a)(int);
- a = templatedConflict<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@226{{candidate function}} expected-note@228{{candidate function}}
- a = &templatedConflict<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@226{{candidate function}} expected-note@228{{candidate function}}
+ a = templatedConflict<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@231{{candidate function}} expected-note@233{{candidate function}}
+ a = &templatedConflict<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@231{{candidate function}} expected-note@233{{candidate function}}
}
int ovlNoCandidate(int m) __attribute__((enable_if(false, "")));
int ovlNoCandidate(int m) __attribute__((enable_if(0, "")));
void test7() {
- int (*p)(int) = ovlNoCandidate; // expected-error{{address of overloaded function 'ovlNoCandidate' does not match required type}} expected-note@237{{made ineligible by enable_if}} expected-note@238{{made ineligible by enable_if}}
- int (*p2)(int) = &ovlNoCandidate; // expected-error{{address of overloaded function 'ovlNoCandidate' does not match required type}} expected-note@237{{made ineligible by enable_if}} expected-note@238{{made ineligible by enable_if}}
+ int (*p)(int) = ovlNoCandidate; // expected-error{{address of overloaded function 'ovlNoCandidate' does not match required type}} expected-note@242{{made ineligible by enable_if}} expected-note@243{{made ineligible by enable_if}}
+ int (*p2)(int) = &ovlNoCandidate; // expected-error{{address of overloaded function 'ovlNoCandidate' does not match required type}} expected-note@242{{made ineligible by enable_if}} expected-note@243{{made ineligible by enable_if}}
int (*a)(int);
- a = ovlNoCandidate; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@237{{made ineligible by enable_if}} expected-note@238{{made ineligible by enable_if}}
- a = &ovlNoCandidate; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@237{{made ineligible by enable_if}} expected-note@238{{made ineligible by enable_if}}
+ a = ovlNoCandidate; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@242{{made ineligible by enable_if}} expected-note@243{{made ineligible by enable_if}}
+ a = &ovlNoCandidate; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@242{{made ineligible by enable_if}} expected-note@243{{made ineligible by enable_if}}
}
int noOvlNoCandidate(int m) __attribute__((enable_if(false, "")));
@@ -346,3 +351,48 @@
auto CRef = (NoMatchTy)&foo; // expected-error{{address of overloaded function 'foo' does not match required type 'void ()'}}
}
}
+
+namespace value_dependent {
+// Ensure that enable_if works well with dependent expressions...
+
+constexpr int noOverload(int N) __attribute__((enable_if(!N, ""))) { return 0; } //expected-note 2 {{candidate disabled}}
+
+template <int N> constexpr int callNoOverload() { return noOverload(N); }
+
+static_assert(callNoOverload<0>() == 0, "");
+constexpr int Fail = callNoOverload<1>(); // expected-error@-3{{no matching function for call to 'noOverload'}} expected-error{{constexpr variable 'Fail' must be initialized by a constant expression}} expected-note{{in instantiation of function}}
+
+// Prefixing noOverload with its namespace means we get to skip ADL, so we won't
+// go into overload resolution for this.
+template <int N> constexpr int directCallNoOverload() {
+ return ::value_dependent::noOverload(N);
+}
+
+static_assert(directCallNoOverload<0>() == 0, "");
+constexpr int FailAgain = directCallNoOverload<1>(); // expected-error@-4{{no matching function for call to 'noOverload'}} expected-note{{in instantiation of function}}
+
+constexpr int overloaded(int N) __attribute__((enable_if(N == 0, ""))) {
+ return 1;
+}
+
+constexpr int overloaded(int N) __attribute__((enable_if(N == 1, ""))) {
+ return 2;
+}
+
+constexpr int overloaded(int N) { return 4; }
+
+template <int N> constexpr int callIt() { return overloaded(N); }
+
+static_assert(callIt<0>() == 1, "");
+static_assert(callIt<1>() == 2, "");
+static_assert(callIt<2>() == 4, "");
+
+template <int N>
+constexpr int directlyDepends() __attribute__((enable_if(N, ""))) {
+ return N;
+}
+
+static_assert(directlyDepends<1>() == 1, "");
+constexpr int FailYetAgain = directlyDepends<0>(); // expected-error{{no matching function for call to 'directlyDepends'}} expected-note@-5{{candidate disabled}}
+
+}
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp
+++ lib/Sema/SemaOverload.cpp
@@ -5684,6 +5684,33 @@
return false;
}
+namespace {
+/// In overload resolution, we need to know if any enable_if candidates failed
+/// due to value-dependent values. So, we pack that into the FailedAttr pointer,
+/// rather than recomputing it.
+struct EnableIfFailureResults {
+ EnableIfAttr *FailedAttr;
+ bool ValueDependent;
+};
+}
+
+static EnableIfFailureResults unpackEnableIfFailure(void *Opaque) {
+ llvm::PointerIntPair<EnableIfAttr*, 1> Pair;
+ Pair.setFromOpaqueValue(Opaque);
+ return {Pair.getPointer(), bool(Pair.getInt())};
+}
+
+static void *packedCheckEnableIf(Sema &S, FunctionDecl *Fn,
+ ArrayRef<Expr *> Args,
+ bool MissingImplicitThis = false) {
+ bool ValDep = false;
+ EnableIfAttr *EIA = S.CheckEnableIf(Fn, Args, MissingImplicitThis, &ValDep);
+ // ValueDep is meaningless if we didn't fail.
+ if (!EIA)
+ return nullptr;
+ return llvm::PointerIntPair<EnableIfAttr *, 1>(EIA, ValDep).getOpaqueValue();
+}
+
/// AddOverloadCandidate - Adds the given function to the set of
/// candidate functions, using the given function call arguments. If
/// @p SuppressUserConversions, then don't allow user-defined
@@ -5841,10 +5868,10 @@
}
}
- if (EnableIfAttr *FailedAttr = CheckEnableIf(Function, Args)) {
+ if (void *Failed = packedCheckEnableIf(*this, Function, Args)) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_enable_if;
- Candidate.DeductionFailure.Data = FailedAttr;
+ Candidate.DeductionFailure.Data = Failed;
return;
}
}
@@ -5955,7 +5982,11 @@
}
EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
- bool MissingImplicitThis) {
+ bool MissingImplicitThis,
+ bool *FailedDueToValueDependentExpr) {
+ if (FailedDueToValueDependentExpr)
+ *FailedDueToValueDependentExpr = false;
+
auto EnableIfAttrs = getOrderedEnableIfAttrs(Function);
if (EnableIfAttrs.empty())
return nullptr;
@@ -6016,18 +6047,26 @@
for (auto *EIA : EnableIfAttrs) {
APValue Result;
+
if (EIA->getCond()->isValueDependent()) {
- // Don't even try now, we'll examine it after instantiation.
- continue;
+ if (FailedDueToValueDependentExpr)
+ *FailedDueToValueDependentExpr = true;
+ return EIA;
}
if (!EIA->getCond()->EvaluateWithSubstitution(
Result, Context, Function, llvm::makeArrayRef(ConvertedArgs))) {
- if (!ContainsValueDependentExpr)
- return EIA;
- } else if (!Result.isInt() || !Result.getInt().getBoolValue()) {
+ // FailedDueToValueDependentExpr is a best effort check that notes that we
+ // *may* have failed due to a value dependent expression. There's no cheap
+ // way to figure out if this is true, so set it in all cases where it may
+ // apply. If we're wrong, we'll find that out after instantiation.
+ if (FailedDueToValueDependentExpr)
+ *FailedDueToValueDependentExpr = ContainsValueDependentExpr;
return EIA;
}
+
+ if (!Result.isInt() || !Result.getInt().getBoolValue())
+ return EIA;
}
return nullptr;
}
@@ -6224,10 +6263,10 @@
}
}
- if (EnableIfAttr *FailedAttr = CheckEnableIf(Method, Args, true)) {
+ if (void *Failed = packedCheckEnableIf(*this, Method, Args, true)) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_enable_if;
- Candidate.DeductionFailure.Data = FailedAttr;
+ Candidate.DeductionFailure.Data = Failed;
return;
}
}
@@ -6533,10 +6572,10 @@
"Can only end up with a standard conversion sequence or failure");
}
- if (EnableIfAttr *FailedAttr = CheckEnableIf(Conversion, None)) {
+ if (void *Failed = packedCheckEnableIf(*this, Conversion, None)) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_enable_if;
- Candidate.DeductionFailure.Data = FailedAttr;
+ Candidate.DeductionFailure.Data = Failed;
return;
}
}
@@ -6685,10 +6724,10 @@
}
}
- if (EnableIfAttr *FailedAttr = CheckEnableIf(Conversion, None)) {
+ if (void *Failed = packedCheckEnableIf(*this, Conversion, None)) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_enable_if;
- Candidate.DeductionFailure.Data = FailedAttr;
+ Candidate.DeductionFailure.Data = Failed;
return;
}
}
@@ -9609,7 +9648,8 @@
static void DiagnoseFailedEnableIfAttr(Sema &S, OverloadCandidate *Cand) {
FunctionDecl *Callee = Cand->Function;
- EnableIfAttr *Attr = static_cast<EnableIfAttr*>(Cand->DeductionFailure.Data);
+ void *PackedFailure = Cand->DeductionFailure.Data;
+ EnableIfAttr *Attr = unpackEnableIfFailure(PackedFailure).FailedAttr;
S.Diag(Callee->getLocation(),
diag::note_ovl_candidate_disabled_by_enable_if_attr)
@@ -11376,8 +11416,24 @@
&result))
return result;
+ // If we had any enable_if attributes fail because they were value dependent,
+ // then we can't accurately resolve the overload now. Defer it until later.
+ if (getLangOpts().CPlusPlus)
+ for (auto I = CandidateSet.begin(), E = CandidateSet.end(); I != E; ++I)
+ if (!I->Viable && I->FailureKind == ovl_fail_enable_if &&
+ unpackEnableIfFailure(I->DeductionFailure.Data).ValueDependent) {
+ // The overload we select may change depending on the value the failing
+ // enable_if is dependent upon. So, the type of the function we're
+ // using is both type dependent and value dependent.
+ CallExpr *CE = buildDependentCallExpr(ExecConfig, Fn, Args, RParenLoc);
+ CE->setInstantiationDependent(true);
+ CE->setValueDependent(true);
+ CE->setTypeDependent(true);
+ return CE;
+ }
+
// If the user handed us something like `(&Foo)(Bar)`, we need to ensure that
- // functions that aren't addressible are considered unviable.
+ // functions that aren't addressable are considered unviable.
if (CalleesAddressIsTaken)
markUnaddressableCandidatesUnviable(*this, CandidateSet);
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -5044,6 +5044,17 @@
return OverloadDecl;
}
+CallExpr *Sema::buildDependentCallExpr(Expr *ExecConfig, Expr *Fn,
+ MultiExprArg ArgExprs,
+ SourceLocation RParenLoc) {
+ if (ExecConfig)
+ return new (Context)
+ CUDAKernelCallExpr(Context, Fn, cast<CallExpr>(ExecConfig), ArgExprs,
+ Context.DependentTy, VK_RValue, RParenLoc);
+ return new (Context) CallExpr(Context, Fn, ArgExprs, Context.DependentTy,
+ VK_RValue, RParenLoc);
+}
+
/// ActOnCallExpr - Handle a call to Fn with the specified array of arguments.
/// This provides the location of the left/right parens and a list of comma
/// locations.
@@ -5083,22 +5094,8 @@
// in which case we won't do any semantic analysis now.
// FIXME: Will need to cache the results of name lookup (including ADL) in
// Fn.
- bool Dependent = false;
- if (Fn->isTypeDependent())
- Dependent = true;
- else if (Expr::hasAnyTypeDependentArguments(ArgExprs))
- Dependent = true;
-
- if (Dependent) {
- if (ExecConfig) {
- return new (Context) CUDAKernelCallExpr(
- Context, Fn, cast<CallExpr>(ExecConfig), ArgExprs,
- Context.DependentTy, VK_RValue, RParenLoc);
- } else {
- return new (Context) CallExpr(
- Context, Fn, ArgExprs, Context.DependentTy, VK_RValue, RParenLoc);
- }
- }
+ if (Fn->isTypeDependent() || Expr::hasAnyTypeDependentArguments(ArgExprs))
+ return buildDependentCallExpr(ExecConfig, Fn, ArgExprs, RParenLoc);
// Determine whether this is a call to an object (C++ [over.call.object]).
if (Fn->getType()->isRecordType())
@@ -5169,28 +5166,45 @@
} else if (isa<MemberExpr>(NakedFn))
NDecl = cast<MemberExpr>(NakedFn)->getMemberDecl();
+ bool MakeCallValueDependent = false;
if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(NDecl)) {
if (CallingNDeclIndirectly &&
!checkAddressOfFunctionIsAvailable(FD, /*Complain=*/true,
Fn->getLocStart()))
return ExprError();
if (FD->hasAttr<EnableIfAttr>()) {
- if (const EnableIfAttr *Attr = CheckEnableIf(FD, ArgExprs, true)) {
- Diag(Fn->getLocStart(),
- isa<CXXMethodDecl>(FD) ?
- diag::err_ovl_no_viable_member_function_in_call :
- diag::err_ovl_no_viable_function_in_call)
- << FD << FD->getSourceRange();
- Diag(FD->getLocation(),
- diag::note_ovl_candidate_disabled_by_enable_if_attr)
- << Attr->getCond()->getSourceRange() << Attr->getMessage();
+ bool FailedByValueDependentExpr = false;
+ if (const EnableIfAttr *Attr =
+ CheckEnableIf(FD, ArgExprs, true, &FailedByValueDependentExpr)) {
+ if (FailedByValueDependentExpr) {
+ // We can build this as usual, but we need to check the enable_if
+ // condition when we have all of the necessary values, so we need to
+ // ensure the resultant function is properly marked as value
+ // dependent.
+ MakeCallValueDependent = true;
+ } else {
+ Diag(Fn->getLocStart(),
+ isa<CXXMethodDecl>(FD) ?
+ diag::err_ovl_no_viable_member_function_in_call :
+ diag::err_ovl_no_viable_function_in_call)
+ << FD << FD->getSourceRange();
+ Diag(FD->getLocation(),
+ diag::note_ovl_candidate_disabled_by_enable_if_attr)
+ << Attr->getCond()->getSourceRange() << Attr->getMessage();
+ }
}
}
}
- return BuildResolvedCallExpr(Fn, NDecl, LParenLoc, ArgExprs, RParenLoc,
- ExecConfig, IsExecConfig);
+ ExprResult BuiltCall = BuildResolvedCallExpr(
+ Fn, NDecl, LParenLoc, ArgExprs, RParenLoc, ExecConfig, IsExecConfig);
+ if (MakeCallValueDependent) {
+ BuiltCall.get()->setValueDependent(true);
+ BuiltCall.get()->setInstantiationDependent(true);
+ }
+
+ return BuiltCall;
}
/// ActOnAsTypeExpr - create a new asType (bitcast) from the arguments.
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -2480,8 +2480,13 @@
/// Check the enable_if expressions on the given function. Returns the first
/// failing attribute, or NULL if they were all successful.
+ ///
+ /// If `FailedDueToValueDependentExpr` is non-null,
+ /// *FailedDueToValueDependentExpr will report whether the check may have
+ /// failed because a value dependent expression was present.
EnableIfAttr *CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
- bool MissingImplicitThis = false);
+ bool MissingImplicitThis = false,
+ bool *FailedDueToValueDependentExpr = nullptr);
/// Returns whether the given function's address can be taken or not,
/// optionally emitting a diagnostic if the address can't be taken.
@@ -3970,6 +3975,12 @@
private:
static BinaryOperatorKind ConvertTokenKindToBinaryOpcode(tok::TokenKind Kind);
+ /// \brief Build dependent call expression. This is needed for attributes that
+ /// may interfere with overload resolution.
+ CallExpr *buildDependentCallExpr(Expr *ExecConfig, Expr *Fn,
+ MultiExprArg ArgExprs,
+ SourceLocation RParenLoc);
+
public:
ExprResult ActOnBinOp(Scope *S, SourceLocation TokLoc,
tok::TokenKind Kind, Expr *LHSExpr, Expr *RHSExpr);
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits