r192515 - Consumed analysis: replace the consumes attribute with a set_typestate
Delesley Hutchins
delesley at google.com
Mon Oct 14 07:57:59 PDT 2013
Thanks for the review, Aaron! Excellent points -- I'll fix those
issues in a future patch. FYI, my goal right now is to get all of
Chris's stuff working and upstream first; I'll go back and refine the
code afterword. You had some good comments earlier regarding use of
raw pointers that I want to address too. I appreciate the code
review,
-DeLesley
On Sat, Oct 12, 2013 at 8:01 AM, Aaron Ballman <aaron at aaronballman.com> wrote:
> A couple of points below:
>
> On Fri, Oct 11, 2013 at 7:03 PM, DeLesley Hutchins <delesley at google.com> wrote:
>> Author: delesley
>> Date: Fri Oct 11 18:03:26 2013
>> New Revision: 192515
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=192515&view=rev
>> Log:
>> Consumed analysis: replace the consumes attribute with a set_typestate
>> attribute. Patch by chris.wailes at gmail.com; reviewed and edited by delesley.
>>
>> Modified:
>> cfe/trunk/include/clang/Basic/Attr.td
>> cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
>> cfe/trunk/lib/Analysis/Consumed.cpp
>> cfe/trunk/lib/Sema/SemaDeclAttr.cpp
>> cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp
>> cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp
>>
>> Modified: cfe/trunk/include/clang/Basic/Attr.td
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=192515&r1=192514&r2=192515&view=diff
>> ==============================================================================
>> --- cfe/trunk/include/clang/Basic/Attr.td (original)
>> +++ cfe/trunk/include/clang/Basic/Attr.td Fri Oct 11 18:03:26 2013
>> @@ -967,19 +967,6 @@ def CallableWhen : InheritableAttr {
>> ["Unknown", "Consumed", "Unconsumed"]>];
>> }
>>
>> -def TestsTypestate : InheritableAttr {
>> - let Spellings = [GNU<"tests_typestate">];
>> - let Subjects = [CXXMethod];
>> - let Args = [EnumArgument<"TestState", "ConsumedState",
>> - ["consumed", "unconsumed"],
>> - ["Consumed", "Unconsumed"]>];
>> -}
>> -
>> -def Consumes : InheritableAttr {
>> - let Spellings = [GNU<"consumes">];
>> - let Subjects = [CXXMethod];
>> -}
>> -
>> def ReturnTypestate : InheritableAttr {
>> let Spellings = [GNU<"return_typestate">];
>> let Subjects = [Function];
>> @@ -988,6 +975,22 @@ def ReturnTypestate : InheritableAttr {
>> ["Unknown", "Consumed", "Unconsumed"]>];
>> }
>>
>> +def SetTypestate : InheritableAttr {
>> + let Spellings = [GNU<"set_typestate">];
>> + let Subjects = [CXXMethod];
>> + let Args = [EnumArgument<"NewState", "ConsumedState",
>> + ["unknown", "consumed", "unconsumed"],
>> + ["Unknown", "Consumed", "Unconsumed"]>];
>> +}
>> +
>> +def TestsTypestate : InheritableAttr {
>> + let Spellings = [GNU<"tests_typestate">];
>> + let Subjects = [CXXMethod];
>> + let Args = [EnumArgument<"TestState", "ConsumedState",
>> + ["consumed", "unconsumed"],
>> + ["Consumed", "Unconsumed"]>];
>> +}
>> +
>> // Type safety attributes for `void *' pointers and type tags.
>>
>> def ArgumentWithTypeTag : InheritableAttr {
>>
>> Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=192515&r1=192514&r2=192515&view=diff
>> ==============================================================================
>> --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
>> +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Oct 11 18:03:26 2013
>> @@ -2216,8 +2216,8 @@ def warn_attr_on_unconsumable_class : Wa
>> def warn_return_typestate_for_unconsumable_type : Warning<
>> "return state set for an unconsumable type '%0'">, InGroup<Consumed>,
>> DefaultIgnore;
>> -def warn_invalid_test_typestate : Warning<
>> - "invalid test typestate '%0'">, InGroup<Consumed>, DefaultIgnore;
>> +def warn_unknown_consumed_state : Warning<
>> + "unknown consumed analysis state '%0'">, InGroup<Consumed>, DefaultIgnore;
>
> This is not needed (more info below).
>
>> def warn_return_typestate_mismatch : Warning<
>> "return value not in expected state; expected '%0', observed '%1'">,
>> InGroup<Consumed>, DefaultIgnore;
>>
>> Modified: cfe/trunk/lib/Analysis/Consumed.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/Consumed.cpp?rev=192515&r1=192514&r2=192515&view=diff
>> ==============================================================================
>> --- cfe/trunk/lib/Analysis/Consumed.cpp (original)
>> +++ cfe/trunk/lib/Analysis/Consumed.cpp Fri Oct 11 18:03:26 2013
>> @@ -157,6 +157,18 @@ static ConsumedState mapConsumableAttrSt
>> llvm_unreachable("invalid enum");
>> }
>>
>> +static ConsumedState mapSetTypestateAttrState(const SetTypestateAttr *STAttr) {
>> + switch (STAttr->getNewState()) {
>> + case SetTypestateAttr::Unknown:
>> + return CS_Unknown;
>> + case SetTypestateAttr::Unconsumed:
>> + return CS_Unconsumed;
>> + case SetTypestateAttr::Consumed:
>> + return CS_Consumed;
>> + }
>> + llvm_unreachable("invalid_enum");
>> +}
>> +
>> static ConsumedState
>> mapReturnTypestateAttrState(const ReturnTypestateAttr *RTSAttr) {
>> switch (RTSAttr->getState()) {
>> @@ -639,8 +651,9 @@ void ConsumedStmtVisitor::VisitCXXMember
>> if (isTestingFunction(MethodDecl))
>> PropagationMap.insert(PairType(Call,
>> PropagationInfo(PInfo.getVar(), testsFor(MethodDecl))));
>> - else if (MethodDecl->hasAttr<ConsumesAttr>())
>> - StateMap->setState(PInfo.getVar(), consumed::CS_Consumed);
>> + else if (MethodDecl->hasAttr<SetTypestateAttr>())
>> + StateMap->setState(PInfo.getVar(),
>> + mapSetTypestateAttrState(MethodDecl->getAttr<SetTypestateAttr>()));
>> }
>> }
>> }
>> @@ -728,8 +741,9 @@ void ConsumedStmtVisitor::VisitCXXOperat
>> if (isTestingFunction(FunDecl))
>> PropagationMap.insert(PairType(Call,
>> PropagationInfo(PInfo.getVar(), testsFor(FunDecl))));
>> - else if (FunDecl->hasAttr<ConsumesAttr>())
>> - StateMap->setState(PInfo.getVar(), consumed::CS_Consumed);
>> + else if (FunDecl->hasAttr<SetTypestateAttr>())
>> + StateMap->setState(PInfo.getVar(),
>> + mapSetTypestateAttrState(FunDecl->getAttr<SetTypestateAttr>()));
>> }
>> }
>> }
>>
>> Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=192515&r1=192514&r2=192515&view=diff
>> ==============================================================================
>> --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
>> +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Fri Oct 11 18:03:26 2013
>> @@ -1026,20 +1026,6 @@ static bool checkForConsumableClass(Sema
>> return true;
>> }
>>
>> -static void handleConsumesAttr(Sema &S, Decl *D, const AttributeList &Attr) {
>> - if (!isa<CXXMethodDecl>(D)) {
>> - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
>> - Attr.getName() << ExpectedMethod;
>> - return;
>> - }
>> -
>> - if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), Attr))
>> - return;
>> -
>> - D->addAttr(::new (S.Context)
>> - ConsumesAttr(Attr.getRange(), S.Context,
>> - Attr.getAttributeSpellingListIndex()));
>> -}
>>
>> static void handleCallableWhenAttr(Sema &S, Decl *D,
>> const AttributeList &Attr) {
>> @@ -1080,44 +1066,6 @@ static void handleCallableWhenAttr(Sema
>> }
>>
>>
>> -static void handleTestsTypestateAttr(Sema &S, Decl *D,
>> - const AttributeList &Attr) {
>> - if (!checkAttributeNumArgs(S, Attr, 1)) return;
>> -
>> - if (!isa<CXXMethodDecl>(D)) {
>> - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
>> - Attr.getName() << ExpectedMethod;
>> - return;
>> - }
>> -
>> - if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), Attr))
>> - return;
>> -
>> - TestsTypestateAttr::ConsumedState TestState;
>> -
>> - if (Attr.isArgIdent(0)) {
>> - StringRef Param = Attr.getArgAsIdent(0)->Ident->getName();
>> -
>> - if (Param == "consumed") {
>> - TestState = TestsTypestateAttr::Consumed;
>> - } else if (Param == "unconsumed") {
>> - TestState = TestsTypestateAttr::Unconsumed;
>> - } else {
>> - S.Diag(Attr.getLoc(), diag::warn_invalid_test_typestate) << Param;
>> - return;
>> - }
>> -
>> - } else {
>> - S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) <<
>> - Attr.getName() << AANT_ArgumentIdentifier;
>> - return;
>> - }
>> -
>> - D->addAttr(::new (S.Context)
>> - TestsTypestateAttr(Attr.getRange(), S.Context, TestState,
>> - Attr.getAttributeSpellingListIndex()));
>> -}
>> -
>> static void handleReturnTypestateAttr(Sema &S, Decl *D,
>> const AttributeList &Attr) {
>> ReturnTypestateAttr::ConsumedState ReturnState;
>> @@ -1168,6 +1116,84 @@ static void handleReturnTypestateAttr(Se
>> Attr.getAttributeSpellingListIndex()));
>> }
>>
>> +
>> +static void handleSetTypestateAttr(Sema &S, Decl *D, const AttributeList &Attr) {
>> + if (!checkAttributeNumArgs(S, Attr, 1)) return;
>> +
>> + if (!isa<CXXMethodDecl>(D)) {
>> + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
>> + Attr.getName() << ExpectedMethod;
>> + return;
>> + }
>> +
>> + if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), Attr))
>> + return;
>> +
>> + SetTypestateAttr::ConsumedState NewState;
>> +
>> + if (Attr.isArgIdent(0)) {
>> + StringRef Param = Attr.getArgAsIdent(0)->Ident->getName();
>> +
>> + if (Param == "unknown") {
>> + NewState = SetTypestateAttr::Unknown;
>> + } else if (Param == "consumed") {
>> + NewState = SetTypestateAttr::Consumed;
>> + } else if (Param == "unconsumed") {
>> + NewState = SetTypestateAttr::Unconsumed;
>> + } else {
>> + S.Diag(Attr.getLoc(), diag::warn_unknown_consumed_state) << Param;
>> + return;
>> + }
>> +
>> + } else {
>> + S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) <<
>> + Attr.getName() << AANT_ArgumentIdentifier;
>> + return;
>> + }
>
> This whole block (from isArgIdent down) can be replaced with
> ConvertStrToTypeSetState (or something akin to that) -- it's td
> generated code for converting enumerations to their proper value,
> including many diagnostics. As an example, here's visibility:
>
> VisibilityAttr::VisibilityType type;
> if (!VisibilityAttr::ConvertStrToVisibilityType(TypeStr, type)) {
> S.Diag(LiteralLoc, diag::warn_attribute_type_not_supported)
> << Attr.getName() << TypeStr;
> return;
> }
>
> You should be using the warn_attribute_type_not_support diagnostic for
> compatibility.
>
>> +
>> + D->addAttr(::new (S.Context)
>> + SetTypestateAttr(Attr.getRange(), S.Context, NewState,
>> + Attr.getAttributeSpellingListIndex()));
>> +}
>> +
>> +static void handleTestsTypestateAttr(Sema &S, Decl *D,
>> + const AttributeList &Attr) {
>> + if (!checkAttributeNumArgs(S, Attr, 1)) return;
>> +
>> + if (!isa<CXXMethodDecl>(D)) {
>> + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
>> + Attr.getName() << ExpectedMethod;
>> + return;
>> + }
>> +
>> + if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), Attr))
>> + return;
>> +
>> + TestsTypestateAttr::ConsumedState TestState;
>> +
>> + if (Attr.isArgIdent(0)) {
>> + StringRef Param = Attr.getArgAsIdent(0)->Ident->getName();
>> +
>> + if (Param == "consumed") {
>> + TestState = TestsTypestateAttr::Consumed;
>> + } else if (Param == "unconsumed") {
>> + TestState = TestsTypestateAttr::Unconsumed;
>> + } else {
>> + S.Diag(Attr.getLoc(), diag::warn_unknown_consumed_state) << Param;
>> + return;
>> + }
>> +
>> + } else {
>> + S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) <<
>> + Attr.getName() << AANT_ArgumentIdentifier;
>> + return;
>> + }
>
> Same comments apply here as above.
>
>> +
>> + D->addAttr(::new (S.Context)
>> + TestsTypestateAttr(Attr.getRange(), S.Context, TestState,
>> + Attr.getAttributeSpellingListIndex()));
>> +}
>> +
>> static void handleExtVectorTypeAttr(Sema &S, Scope *scope, Decl *D,
>> const AttributeList &Attr) {
>> TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D);
>> @@ -4793,18 +4819,18 @@ static void ProcessDeclAttribute(Sema &S
>> case AttributeList::AT_Consumable:
>> handleConsumableAttr(S, D, Attr);
>> break;
>> - case AttributeList::AT_Consumes:
>> - handleConsumesAttr(S, D, Attr);
>> - break;
>> case AttributeList::AT_CallableWhen:
>> handleCallableWhenAttr(S, D, Attr);
>> break;
>> - case AttributeList::AT_TestsTypestate:
>> - handleTestsTypestateAttr(S, D, Attr);
>> - break;
>> case AttributeList::AT_ReturnTypestate:
>> handleReturnTypestateAttr(S, D, Attr);
>> break;
>> + case AttributeList::AT_SetTypestate:
>> + handleSetTypestateAttr(S, D, Attr);
>> + break;
>> + case AttributeList::AT_TestsTypestate:
>> + handleTestsTypestateAttr(S, D, Attr);
>> + break;
>>
>> // Type safety attributes.
>> case AttributeList::AT_ArgumentWithTypeTag:
>>
>> Modified: cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp?rev=192515&r1=192514&r2=192515&view=diff
>> ==============================================================================
>> --- cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp (original)
>> +++ cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp Fri Oct 11 18:03:26 2013
>> @@ -4,7 +4,7 @@
>>
>> #define CALLABLE_WHEN(...) __attribute__ ((callable_when(__VA_ARGS__)))
>> #define CONSUMABLE(state) __attribute__ ((consumable(state)))
>> -#define CONSUMES __attribute__ ((consumes))
>> +#define SET_TYPESTATE(state) __attribute__ ((set_typestate(state)))
>> #define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state)))
>> #define TESTS_TYPESTATE(state) __attribute__ ((tests_typestate(state)))
>>
>> @@ -23,7 +23,7 @@ public:
>>
>> ConsumableClass<T>& operator=(ConsumableClass<T> &other);
>> ConsumableClass<T>& operator=(ConsumableClass<T> &&other);
>> - ConsumableClass<T>& operator=(nullptr_t) CONSUMES;
>> + ConsumableClass<T>& operator=(nullptr_t) SET_TYPESTATE(consumed);
>>
>> template <typename U>
>> ConsumableClass<T>& operator=(ConsumableClass<U> &other);
>> @@ -31,7 +31,7 @@ public:
>> template <typename U>
>> ConsumableClass<T>& operator=(ConsumableClass<U> &&other);
>>
>> - void operator()(int a) CONSUMES;
>> + void operator()(int a) SET_TYPESTATE(consumed);
>> void operator*() const CALLABLE_WHEN("unconsumed");
>> void unconsumedCall() const CALLABLE_WHEN("unconsumed");
>> void callableWhenUnknown() const CALLABLE_WHEN("unconsumed", "unknown");
>> @@ -44,7 +44,8 @@ public:
>> void constCall() const;
>> void nonconstCall();
>>
>> - void consume() CONSUMES;
>> + void consume() SET_TYPESTATE(consumed);
>> + void unconsume() SET_TYPESTATE(unconsumed);
>> };
>>
>> class CONSUMABLE(unconsumed) DestructorTester {
>> @@ -484,7 +485,7 @@ void testConditionalMerge() {
>> *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
>> }
>>
>> -void testConsumes0() {
>> +void testSetTypestate() {
>> ConsumableClass<int> var(42);
>>
>> *var;
>> @@ -492,15 +493,19 @@ void testConsumes0() {
>> var.consume();
>>
>> *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
>> +
>> + var.unconsume();
>> +
>> + *var;
>> }
>>
>> -void testConsumes1() {
>> +void testConsumes0() {
>> ConsumableClass<int> var(nullptr);
>>
>> *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
>> }
>>
>> -void testConsumes2() {
>> +void testConsumes1() {
>> ConsumableClass<int> var(42);
>>
>> var.unconsumedCall();
>>
>> Modified: cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp
>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp?rev=192515&r1=192514&r2=192515&view=diff
>> ==============================================================================
>> --- cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp (original)
>> +++ cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp Fri Oct 11 18:03:26 2013
>> @@ -1,10 +1,10 @@
>> // RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s
>>
>> #define CALLABLE_WHEN(...) __attribute__ ((callable_when(__VA_ARGS__)))
>> -#define CONSUMABLE(state) __attribute__ ((consumable(state)))
>> -#define CONSUMES __attribute__ ((consumes))
>> -#define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state)))
>> -#define TESTS_TYPESTATE(state) __attribute__ ((tests_typestate(state)))
>> +#define CONSUMABLE(state) __attribute__ ((consumable(state)))
>> +#define SET_TYPESTATE(state) __attribute__ ((set_typestate(state)))
>> +#define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state)))
>> +#define TESTS_TYPESTATE(state) __attribute__ ((tests_typestate(state)))
>>
>> // FIXME: This test is here because the warning is issued by the Consumed
>> // analysis, not SemaDeclAttr. The analysis won't run after an error
>> @@ -17,27 +17,27 @@ int returnTypestateForUnconsumable() {
>> }
>>
>> class AttrTester0 {
>> - void consumes() __attribute__ ((consumes(42))); // expected-error {{attribute takes no arguments}}
>> + void consumes() __attribute__ ((set_typestate())); // expected-error {{attribute takes one argument}}
>> bool testsUnconsumed() __attribute__ ((tests_typestate())); // expected-error {{attribute takes one argument}}
>> void callableWhen() __attribute__ ((callable_when())); // expected-error {{attribute takes at least 1 argument}}
>> };
>>
>> -int var0 CONSUMES; // expected-warning {{'consumes' attribute only applies to methods}}
>> +int var0 SET_TYPESTATE(consumed); // expected-warning {{'set_typestate' attribute only applies to methods}}
>> int var1 TESTS_TYPESTATE(consumed); // expected-warning {{'tests_typestate' attribute only applies to methods}}
>> -int var2 CALLABLE_WHEN(42); // expected-warning {{'callable_when' attribute only applies to methods}}
>> +int var2 CALLABLE_WHEN("consumed"); // expected-warning {{'callable_when' attribute only applies to methods}}
>> int var3 CONSUMABLE(consumed); // expected-warning {{'consumable' attribute only applies to classes}}
>> int var4 RETURN_TYPESTATE(consumed); // expected-warning {{'return_typestate' attribute only applies to functions}}
>>
>> -void function0() CONSUMES; // expected-warning {{'consumes' attribute only applies to methods}}
>> +void function0() SET_TYPESTATE(consumed); // expected-warning {{'set_typestate' attribute only applies to methods}}
>> void function1() TESTS_TYPESTATE(consumed); // expected-warning {{'tests_typestate' attribute only applies to methods}}
>> -void function2() CALLABLE_WHEN(42); // expected-warning {{'callable_when' attribute only applies to methods}}
>> +void function2() CALLABLE_WHEN("consumed"); // expected-warning {{'callable_when' attribute only applies to methods}}
>> void function3() CONSUMABLE(consumed); // expected-warning {{'consumable' attribute only applies to classes}}
>>
>> class CONSUMABLE(unknown) AttrTester1 {
>> void callableWhen0() CALLABLE_WHEN("unconsumed");
>> void callableWhen1() CALLABLE_WHEN(42); // expected-error {{'callable_when' attribute requires a string}}
>> void callableWhen2() CALLABLE_WHEN("foo"); // expected-warning {{'callable_when' attribute argument not supported: foo}}
>> - void consumes() CONSUMES;
>> + void consumes() SET_TYPESTATE(consumed);
>> bool testsUnconsumed() TESTS_TYPESTATE(consumed);
>> };
>>
>> @@ -46,7 +46,7 @@ AttrTester1 returnTypestateTester1() RET
>>
>> class AttrTester2 {
>> void callableWhen() CALLABLE_WHEN("unconsumed"); // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}}
>> - void consumes() CONSUMES; // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}}
>> + void consumes() SET_TYPESTATE(consumed); // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}}
>> bool testsUnconsumed() TESTS_TYPESTATE(consumed); // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}}
>> };
>>
>>
>>
>> _______________________________________________
>> cfe-commits mailing list
>> cfe-commits at cs.uiuc.edu
>> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>
> ~Aaron
--
DeLesley Hutchins | Software Engineer | delesley at google.com | 505-206-0315
More information about the cfe-commits
mailing list