On Thu, Oct 23, 2014 at 11:14 AM, Kaelyn Takata <[email protected]> wrote:

> Well, I've found a test case that would trigger the assertion but in the
> code's current form does the right thing:
>

Great!


>
> class Foo {
> public:
>   template <> void testIt();
>   void textIt();
> };
>
> void test(Foo *f) {
>   f->TestIt();
> }
>
>
> With the patchset in it's current incarnation (including the fix I
> mentioned in my last email):
>

I didn't quite follow the description of the other fix/issue there - few
too many patches in my head. Could you send the latest version of this
patch under review?


>
> fail.cc:3:20: error: no function template matches function template
> specialization 'testIt'
>   template <> void testIt();
>                    ^
> fail.cc:8:6: error: no member named 'TestIt' in 'Foo'; did you mean
> 'textIt'?
>   f->TestIt();
>      ^~~~~~
>      textIt
> fail.cc:4:8: note: 'textIt' declared here
>   void textIt();
>        ^
> 2 errors generated.
>
>
> With the "if (!NE.isInvalid())" as an assertion:
>
> fail.cc:3:20: error: no function template matches function template
> specialization 'testIt'
>   template <> void testIt();
>                    ^
> clang-3.6: ../tools/clang/lib/Sema/SemaExprCXX.cpp:6055: ExprResult
> (anonymous namespace)::TransformTypos::TransformTypoExpr(clang::TypoExpr
> *): Assertion `!NE.isInvalid()' failed.
>
>
> Note that the (invalid) function template "testIt" has an edit distance of
> 1 from "TestIt" and the proper suggestion, "textIt", has an edit distance
> of 2. I've added the above test case to typo-correction-delayed.cpp.
>
>
> On Thu, Oct 23, 2014 at 11:05 AM, David Blaikie <[email protected]>
> wrote:
>
>> (sorry for the repetition - I'm failing at 'reply all' this morning)
>>
>> On Thu, Oct 23, 2014 at 10:53 AM, Kaelyn Takata <[email protected]> wrote:
>>
>>> On Wed, Oct 22, 2014 at 2:19 PM, David Blaikie <[email protected]>
>>> wrote:
>>>
>>>>
>>>>
>>>> On Tue, Oct 21, 2014 at 1:37 PM, Kaelyn Takata <[email protected]>
>>>> wrote:
>>>>
>>>>>
>>>>>
>>>>> On Fri, Oct 17, 2014 at 9:52 AM, David Blaikie <[email protected]>
>>>>> wrote:
>>>>>
>>>>>>
>>>>>>
>>>>>> On Thu, Oct 9, 2014 at 10:12 AM, David Blaikie <[email protected]>
>>>>>> wrote:
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Thu, Oct 9, 2014 at 9:58 AM, Kaelyn Takata <[email protected]>
>>>>>>> wrote:
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On Tue, Oct 7, 2014 at 5:03 PM, David Blaikie <[email protected]>
>>>>>>>> wrote:
>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Tue, Oct 7, 2014 at 2:29 PM, Kaelyn Takata <[email protected]>
>>>>>>>>> wrote:
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On Tue, Oct 7, 2014 at 11:43 AM, David Blaikie <
>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> On Mon, Oct 6, 2014 at 3:46 PM, Kaelyn Takata <[email protected]>
>>>>>>>>>>> wrote:
>>>>>>>>>>>
>>>>>>>>>>>> On Fri, Oct 3, 2014 at 11:45 AM, David Blaikie <
>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>
>>>>>>>>>>>>> Given that these are parts of a patch series, some of which
>>>>>>>>>>>>> have already been signed off on/I haven't looked at, it's a bit 
>>>>>>>>>>>>> hard to
>>>>>>>>>>>>> review the overall design - but I believe you & Richard talked 
>>>>>>>>>>>>> that over,
>>>>>>>>>>>>> so I'm not too worried about that (just explaining why my review 
>>>>>>>>>>>>> feedback's
>>>>>>>>>>>>> going to be a bit narrow)
>>>>>>>>>>>>>
>>>>>>>>>>>>> +  if (NumTypos > 0 && !ExprEvalContexts.empty())
>>>>>>>>>>>>> +    ExprEvalContexts.back().NumTypos += NumTypos;
>>>>>>>>>>>>> +  else
>>>>>>>>>>>>> +    assert(NumTypos == 0 && "There are outstanding typos after 
>>>>>>>>>>>>> popping the "
>>>>>>>>>>>>> +                            "last 
>>>>>>>>>>>>> ExpressionEvaluationContextRecord");
>>>>>>>>>>>>>
>>>>>>>>>>>>> Could this be simplified? I think it's equivalent to:
>>>>>>>>>>>>>
>>>>>>>>>>>>>   if (!ExprEvalContexts.empty())
>>>>>>>>>>>>>     ExprEvalContexts.back().NumTypos += NumTypos;
>>>>>>>>>>>>>   else
>>>>>>>>>>>>>     assert(NumTypos == 0 ... );
>>>>>>>>>>>>>
>>>>>>>>>>>>> or:
>>>>>>>>>>>>>
>>>>>>>>>>>>>   assert(ExprEvalContexts.empty() || NumTypos != 0 && ...)
>>>>>>>>>>>>>   if (!ExprEvalContexts.empty())
>>>>>>>>>>>>>     ...
>>>>>>>>>>>>>
>>>>>>>>>>>>> Yes, the "NumTypos > 0" was unnecessary and has been removed.
>>>>>>>>>>>> (FYI, the second form you suggested doesn't quite work because the 
>>>>>>>>>>>> assert
>>>>>>>>>>>> would fail on the common case where ExprEvalContexts is not empty 
>>>>>>>>>>>> and there
>>>>>>>>>>>> are no typos.)
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Hmm, right, maybe:
>>>>>>>>>>>
>>>>>>>>>>>   assert(ExprEvalContexts.empty() ^ NumTypos != 0 && ... )
>>>>>>>>>>>   if (!ExprEvalContexts.empty())
>>>>>>>>>>>     ExprEvalContexts.back().NumTypos += NumTypos;
>>>>>>>>>>>
>>>>>>>>>>> but perhaps that's just too confusing. I just find an assertion
>>>>>>>>>>> as the only thing inside a block of code to be a bit off... but it's
>>>>>>>>>>> certainly not unprecedented in LLVM and is probably more legible - 
>>>>>>>>>>> just my
>>>>>>>>>>> own hangup to get over :)
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>> There's an unnecessary semicolon at the end of the "while (true)" 
>>>>>>>>>>>> loop in TransformTypos::Transform
>>>>>>>>>>>>>
>>>>>>>>>>>>> Removed.
>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> I'd roll this up a bit:
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> +      auto &State = SemaRef.getTypoExprState(TE);
>>>>>>>>>>>>> +      if (State.DiagHandler)
>>>>>>>>>>>>> +        
>>>>>>>>>>>>> (*State.DiagHandler)(State.Consumer->getCurrentCorrection());
>>>>>>>>>>>>>
>>>>>>>>>>>>> into:
>>>>>>>>>>>>>
>>>>>>>>>>>>>   if (auto *Handler = SemaRef.getTypoExprState(TE).DiagHandler)
>>>>>>>>>>>>>     (*DiagHandler)(State.Consumer->getCurrentCorrection());
>>>>>>>>>>>>>
>>>>>>>>>>>>> That rollup doesn't work because State would be unknown when
>>>>>>>>>>>> accessing State.Consumer in the argument to the DiagHandler.
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Oh, right!
>>>>>>>>>>>
>>>>>>>>>>>> count in the condition, plus insert in the body of the if in 
>>>>>>>>>>>> TransformTypos::TransformTypoExpr would be more efficient as a 
>>>>>>>>>>>> single insert-and-check (by avoiding two separate lookups into the 
>>>>>>>>>>>> set):
>>>>>>>>>>>>>   if (TypoExprs.insert(E).second) // means this element 
>>>>>>>>>>>>> previously didn't exist in the set, and now does
>>>>>>>>>>>>>
>>>>>>>>>>>>> I've changed the count-then-insert to just an insert since
>>>>>>>>>>>> SmallPtrSetImpl::insert returns true when the item wasn't already 
>>>>>>>>>>>> in the
>>>>>>>>>>>> set.
>>>>>>>>>>>>
>>>>>>>>>>>>> This:
>>>>>>>>>>>>>
>>>>>>>>>>>>>   TransformCache.find(E) != TransformCache.end()
>>>>>>>>>>>>>
>>>>>>>>>>>>> might commonly be written as:
>>>>>>>>>>>>>
>>>>>>>>>>>>>   TransformCache.count(E)
>>>>>>>>>>>>>
>>>>>>>>>>>>> Optionally with "!= 0", but often count is used directly as a 
>>>>>>>>>>>>> boolean test, especially when it's a single set/map (not a multi 
>>>>>>>>>>>>> set/map).
>>>>>>>>>>>>>
>>>>>>>>>>>>> Changed.
>>>>>>>>>>>>
>>>>>>>>>>>>> This comment didn't quite make sense to me:
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> +      // If corrections for the first TypoExpr have been 
>>>>>>>>>>>>> exhausted, retry them
>>>>>>>>>>>>> +      // against the next combination of substitutions for all 
>>>>>>>>>>>>> of the other
>>>>>>>>>>>>> +      // TypoExprs.
>>>>>>>>>>>>>
>>>>>>>>>>>>> When you say "retry them" which them do you mean? The corrections 
>>>>>>>>>>>>> for the first TypoExpr? But what does it mean for them to be 
>>>>>>>>>>>>> exhausted then, if they can still be considered?
>>>>>>>>>>>>>
>>>>>>>>>>>>> Maybe I need a bit of help with the higher level algorithm here - 
>>>>>>>>>>>>> I assume the idea is:
>>>>>>>>>>>>>
>>>>>>>>>>>>> Call transform.
>>>>>>>>>>>>> As it visits TypoExpr nodes, it tries their first typo candidate 
>>>>>>>>>>>>> available.
>>>>>>>>>>>>> If we get to the end and have invalid code, reset the state of 
>>>>>>>>>>>>> all the typo corrections except the first one (on its next query, 
>>>>>>>>>>>>> it'll give back its second alternative).
>>>>>>>>>>>>>
>>>>>>>>>>>>> Here is where the confusion is at. As the transform visits
>>>>>>>>>>>> each TypoExpr node it tries the first candidate available. If the 
>>>>>>>>>>>> first
>>>>>>>>>>>> candidates of all the TypoExprs don't work, it then tries the next 
>>>>>>>>>>>> (and
>>>>>>>>>>>> subsequent) candidates of only the first TypoExpr that was seen. 
>>>>>>>>>>>> When it
>>>>>>>>>>>> exhausts the candidates from the first TypoExpr, it then resets 
>>>>>>>>>>>> the stream
>>>>>>>>>>>> of candidates for the first TypoExpr and fetches the next 
>>>>>>>>>>>> candidate of the
>>>>>>>>>>>> TypoExpr, continuing in this way until all combinations of 
>>>>>>>>>>>> candidates of
>>>>>>>>>>>> all TypoExprs have been tried.
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> So I'm still a bit confused about which parts of the retry logic
>>>>>>>>>>> are in which parts of the code.
>>>>>>>>>>>
>>>>>>>>>>> OK, I think I've got it. Transform is called and we don't even
>>>>>>>>>>> know which TypoExprs are in the Expr. So we transform.
>>>>>>>>>>>
>>>>>>>>>>> As transform runs it comes across TypoExprs, and attempts to
>>>>>>>>>>> transform them. For each one, it tries candidates until it finds a 
>>>>>>>>>>> good one
>>>>>>>>>>> (based on the immediate context - what are the cases where
>>>>>>>>>>> BuildDeclarationNameExpr fails here? (when does NE.isInvalid 
>>>>>>>>>>> fail?)).
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> I think there is a bit of confusion on what a "retry" is. Within
>>>>>>>>>> TransformTypoExpr, for either the first TypoExpr ever encountered by 
>>>>>>>>>> this
>>>>>>>>>> TreeTransform or for a TypoExpr without a cached substitution, it 
>>>>>>>>>> tries to
>>>>>>>>>> build a valid subexpression from the TypoCorrection candidates (e.g. 
>>>>>>>>>> a
>>>>>>>>>> DeclRefExpr) and continues trying candidates until it finds one for 
>>>>>>>>>> which a
>>>>>>>>>> valid subexpression can be formed, which it caches and returns. If 
>>>>>>>>>> none of
>>>>>>>>>> the candidates worked, it will cache and return an invalid 
>>>>>>>>>> ExprResult. The
>>>>>>>>>> TreeTransform then takes the returned subexpression and tries to 
>>>>>>>>>> rebuild
>>>>>>>>>> the full expression (possibly trying to transform multiple TypoExprs 
>>>>>>>>>> along
>>>>>>>>>> with CallExprs, etc).
>>>>>>>>>>
>>>>>>>>>> I don't remember off the top of my head without looking at the
>>>>>>>>>> body, but I think BuildDeclarationNameExpr will fail in cases where 
>>>>>>>>>> the
>>>>>>>>>> correction is e.g. a type name, or a member reference 
>>>>>>>>>> name--basically,
>>>>>>>>>> BuildDeclarationNameExpr returns an ExprResult and the correct 
>>>>>>>>>> behavior in
>>>>>>>>>> principle is to check for an invalid result and try the next 
>>>>>>>>>> correction
>>>>>>>>>> candidate when one is seen--even if the Consumer never returns a 
>>>>>>>>>> candidate
>>>>>>>>>> for which BuildDeclarationNameExpr would fail.
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> So, in your example below, maybe the right answer is BEH, but AF
>>>>>>>>>>> is valid, with no correct candidate for the 3 choice.
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> No, AF with no correction for the 3rd TypoExpr would result in
>>>>>>>>>> the entire Expr result being invalid within the main Transform 
>>>>>>>>>> method.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>> Consumers start at "before the first" element, calling
>>>>>>>>>>> "getNextCorrection" on a new consumer gives you the first 
>>>>>>>>>>> correction.
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> That is correct. And the "before the first" element is also an
>>>>>>>>>> empty correction for use with getCurrentCorrection. ;)
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> On the first call to TransformExpr, the first call to
>>>>>>>>>>> TransformTypoExpr chooses A, records it, continues.
>>>>>>>>>>> Gets to the second TransformTypoExpr, tries D and E, succeeds at
>>>>>>>>>>> F.
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> it would only try D then E then F within a single call to
>>>>>>>>>> TransformTypoExpr if BuildDeclarationNameExpr failed for D and E.
>>>>>>>>>>
>>>>>>>>>>> On the third TransformTypoExpr, it tries H and I, both fail, so
>>>>>>>>>>> it returns from the last line ("return TransformCache[E] = 
>>>>>>>>>>> ExprError]).
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Same case here--that last line would only be reached if H and I
>>>>>>>>>> both failed within the call to BuildDeclarationNameExpr.
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> We get back to Transform with a failure. So we walk the
>>>>>>>>>>> TypoExprs (in CheckAndAdvanceTypoExprCorrectionStreams) we reset 
>>>>>>>>>>> the 3rd
>>>>>>>>>>> consumer (because it was finished).
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> No, CheckAndAdvanceTypoExprCorrectionStreams would do nothing if
>>>>>>>>>> the first TypoExpr is not finished. If the first TypoExpr is 
>>>>>>>>>> finished, then
>>>>>>>>>> it would reset it and clear the cached result for the second 
>>>>>>>>>> TypoExpr (so
>>>>>>>>>> that the next call to TransformTypoExpr on the second TypoExpr would 
>>>>>>>>>> try a
>>>>>>>>>> new candidate instead of using the cached result). Only if both the 
>>>>>>>>>> first
>>>>>>>>>> and second TypoExprs were *both* finished would the cached value of 
>>>>>>>>>> the
>>>>>>>>>> third TypoExpr be cleared (and the candidate streams for both the 
>>>>>>>>>> first and
>>>>>>>>>> second TypoExprs would be reset in addition to their cached results 
>>>>>>>>>> being
>>>>>>>>>> removed from the cache). The only way
>>>>>>>>>> CheckAndAdvanceTypoExprCorrectionStreams would see if the third 
>>>>>>>>>> TypoExpr
>>>>>>>>>> was finished would be if both the first and second TypoExprs were 
>>>>>>>>>> also
>>>>>>>>>> finished, and upon resetting it the tree transform would only be 
>>>>>>>>>> attempted
>>>>>>>>>> again if there were at least four known TypoExprs (i.e. the number 
>>>>>>>>>> of known
>>>>>>>>>> TypoExprs is greater than the number of TypoExprs that were finished 
>>>>>>>>>> and
>>>>>>>>>> had to be reset by CheckAndAdvanceTypoExprCorrectionStreams).
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> We transform again:
>>>>>>>>>>>
>>>>>>>>>>> First typo) We are at A so we call next and get B
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Yes. The first TypoExpr would always have it's next candidate
>>>>>>>>>> tried by a call to TransformTypoExpr.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>> Second typo) From F we continue and try G, fail, then we're
>>>>>>>>>>> finished for the second typo.
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> No, G would only be tried if the result from transforming F was
>>>>>>>>>> removed from the cache.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>> Third typo) Perhaps we find some solution for this, or not, it
>>>>>>>>>>> doesn't matter.
>>>>>>>>>>>
>>>>>>>>>>> We get back to Transform with a failure (second typo), So we
>>>>>>>>>>> walk the TypoExprs again, resetting the second (and maybe the 
>>>>>>>>>>> third).
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> If there are only three TypoExprs, then resetting the third
>>>>>>>>>> TypoExpr would mean there are no valid combinations of corrections 
>>>>>>>>>> that
>>>>>>>>>> yield a valid expression (where the final result of the transform is
>>>>>>>>>> neither an invalid ExprResult nor caused any errors to be reported 
>>>>>>>>>> by the
>>>>>>>>>> SFINAETrap).
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>>>> First typo) We are at B so we call next and get to C, let's say
>>>>>>>>>>> we fail here - C isn't even remotely valid.
>>>>>>>>>>> Now it really matters whether we find/fail on the second and
>>>>>>>>>>> third typo. If we fail on the second and third... we're going to 
>>>>>>>>>>> give up.
>>>>>>>>>>> We'll find that all the corrections are finished and we'll conclude 
>>>>>>>>>>> that
>>>>>>>>>>> we've tried everything.
>>>>>>>>>>>
>>>>>>>>>>> Am I following this right?
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Not quite. I think you are forgetting about the fact that, for
>>>>>>>>>> all TypoExprs except the first, a cached result is used if it exists
>>>>>>>>>> instead of fetching a new candidate from the Consumer and trying to 
>>>>>>>>>> build a
>>>>>>>>>> valid Expr with it.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Ah, looking at the code again I see what you mean. The 'break' is
>>>>>>>>> the bit that I missed in the reset/advance loop.
>>>>>>>>>
>>>>>>>>> Which patches would I have to apply to ToT to be able to exercise
>>>>>>>>> this code? It might help for me to step through some of these 
>>>>>>>>> scenarios in
>>>>>>>>> a debugger.
>>>>>>>>>
>>>>>>>>
>>>>>>>> To exercise this code, you'd need the full patch series (all 8) to
>>>>>>>> exercise this code because it's not until patch #8 that anything 
>>>>>>>> actually
>>>>>>>> generates TypoExprs in the AST. I can send you a combined patch 
>>>>>>>> rebased on
>>>>>>>> ToT off-list if you'd like (it's ~112KB uncompressed), and the new test
>>>>>>>> cases in test/SemaCXX/typo-correction-delayed.cpp explicitly test this
>>>>>>>> functionality.
>>>>>>>>
>>>>>>>
>>>>>>> That'd be great!
>>>>>>>
>>>>>>
>>>>>> With your total patch applied I've been able to reproduce a few typo
>>>>>> correction scenarios - thanks!
>>>>>>
>>>>>> But I can't seem to reach/exercise the loop in
>>>>>> TransformTypos::TransformTypoExpr - if I change the "if 
>>>>>> (!NE.isInvalid())"
>>>>>> condition at the end of the loop (the only way to repeat the body of the
>>>>>> loop) to "assert(!NE.isInvalid())" it doesn't fire on any of the test 
>>>>>> cases
>>>>>> (nor on my own modified versions). Do you have some tests that cover 
>>>>>> this?
>>>>>> Could you show/describe a simple situation where it occurs so I can play
>>>>>> around with that part of the logic?
>>>>>>
>>>>>
>>>>> I'm not sure it can be triggered with CorrectTypoDelayed only hooked
>>>>> up to LookupMemberExprInRecord (a static function called by
>>>>> Sema::BuildMemberReferenceExpr directly and indirectly via
>>>>> LookupMemberExpr), but in the second patch set which hooks
>>>>> CorrectTypoDelayed up to DiagnoseEmptyLookup, the assert is triggered by
>>>>> the tests SemaCXX/type-definition-in-specifier.cpp
>>>>> and SemaCXX/warn-thread-safety-parsing.cpp (in addition to the loop being
>>>>> the correct/principled behavior since there is no guarantee that the
>>>>> recovery callback will generate a valid ExprResult from a non-empty
>>>>> TypoCorrection). For this patch set, to trigger the assert a code snippet
>>>>> would have to correct a class member lookup and have a candidate that is a
>>>>> member function, constant, variable, or template in the current class or
>>>>> one of its base classes (per the RecordMemberExprValidatorCCC) yet which
>>>>> yields causes MemberExprTypoRecovery to return an ExprError when trying to
>>>>> build the member reference. Basically, it would require having the typo
>>>>> correction try a candidate that is an accessible class member that cannot
>>>>> be reference--something that I'm not sure is even possible in C++.
>>>>>
>>>>
>>>> Hrm, OK - could you change it to an assertion (& remove the outer
>>>> loop/replace it with a conditional) until that later patch where it becomes
>>>> necessary, so it'll be easier to review it in the context of it being
>>>> executed/used code?
>>>>
>>>
>>> I understand your suggestion, however I don't think changing the code to
>>> do the wrong thing and assert if a false assumption is violated is the
>>> right thing to do. If the only source of an ExprResult in the loop was the
>>> call to Sema::BuildDeclarationNameExpr, I'd be a bit more willing to do
>>> so... but if a recovery handler callback is provided and invoked, there's
>>> no guarantee it won't return an invalid ExprResult, and no documentation of
>>> the assumption that the callback must return a valid ExprResult (outside of
>>> the one assertion you are suggesting, buried within the TypoExpr handling
>>> machinery). Additionally, changing the code to do the wrong thing by
>>> changing the "while (...)" to "if (...)" and replacing "if
>>> (!NE.isInvalid())" with a bogus assertion does nothing to simplify the
>>> actual code block... particularly since it would immediately be changed
>>> right back to the way it is now in the next patch set (a version of which I
>>> had already mailed out two months ago).
>>>
>>
>> My concern is that this is untested (& actually untestable/dead in this
>> revision) code - if someone were to sync to this revision and try to
>> understand what the code did, why it was there, and how it should be
>> modified to do other things, they'd be confused & probably get it wrong
>> (because there are no tests to demonstrate the motivation). It's nice for
>> code to go along with the use - the usual rules of providing tests along
>> with code.
>>
>> But I'll muddle through & try to remember to figure out the tests & the
>> design when you commit/review the next patches if necessary.
>>
>>
>>>
>>> (FYI, I also just noticed I had a mistake in the full patch I sent
>>> you... specifically I seem to have forgotten to delete a return statement
>>> when I changed the code to look up the TypoExpr in the TransformCache only
>>> one and assign the result to the created entry and rebased the rest of the
>>> patch set:
>>>
>>> diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
>>> index 81b3b01..9e05e82 100644
>>> --- a/lib/Sema/SemaExprCXX.cpp
>>> +++ b/lib/Sema/SemaExprCXX.cpp
>>> @@ -6049,8 +6049,6 @@ public:
>>>          if (!TC.isKeyword())
>>>            R.addDecl(TC.getCorrectionDecl());
>>>          NE = SemaRef.BuildDeclarationNameExpr(CXXScopeSpec(), R, false);
>>> -        return TransformCache[E] =
>>> -                   SemaRef.BuildDeclarationNameExpr(CXXScopeSpec(), R,
>>> false);
>>>        }
>>>        assert(!NE.isUnset() &&
>>>               "Typo was transformed into a valid-but-null ExprResult");
>>>
>>> So the "if (!NE.isUnset())" is intended to be the only early return
>>> within the loop, though it still does not fail as an assert within the
>>> patchset.)
>>>
>>>>
>>>> Without that wrinkle, I think I've followed the code enough to sign off
>>>> on it. Please commit.
>>>>
>>>> [I do find the way the algorithm visits the corrections to be a little
>>>> hard to follow, in that its split between the TypoExprs cache, consumer
>>>> state, TransformTypoExpr, and CheckAndAdvanceTypoExprCorrectionStreams - in
>>>> some very abstract theory I would like having each sequence of typo
>>>> candidates (the Consumers represent this) expose an iterator pair, then a
>>>> generic utility to do combinations of a list of ranges and it would expose
>>>> an iterator over those combinations which you could just increment to get
>>>> the next one - it'd separate out the combination logic into a reusable
>>>> component that could be easily examined/tested, then composed with the typo
>>>> correction stuff to keep the logic there relatively simple/clearer.
>>>>
>>>> I might try to work up an example of what I mean]
>>>>
>>>>
>>>>
>>>>
>>>>>
>>>>>> - David
>>>>>>
>>>>>> (micro review comment I saw along the way: I /think/ 
>>>>>> CheckAndAdvanceTypoExprCorrectionStreams
>>>>>> could be simplified as follows:
>>>>>>
>>>>>
>>>>> You're right, it can be simplified now that the loop is in its own
>>>>> function. Changed.
>>>>>
>>>>>>
>>>>>>   bool CheckAndAdvanceTypoExprCorrectionStreams() {
>>>>>>     for (auto TE : TypoExprs) {
>>>>>>       TransformCache.erase(TE);
>>>>>>       auto &State = SemaRef.getTypoExprState(TE);
>>>>>>       if (!State.Consumer->finished())
>>>>>>         return true;
>>>>>>       State.Consumer->resetCorrectionStream();
>>>>>>     }
>>>>>>     return false;
>>>>>>   }
>>>>>>
>>>>>>
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>> (I'm sort of wondering if this might be better expressed by some
>>>>>>>>> iterator scheme, but I'm not sure - there's no pretty next_combination
>>>>>>>>> algorithm (someone proposed one to boost at some point, it seems, but
>>>>>>>>> that's about as close as it's got to anything common/generic) anyway)
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>> So for 3 TypoExprs... let's say 1 has the candidates (A, B, C),
>>>>>>>>>>>> 2 has the candidates (D, E, F, G) and 3 has the candidates (H, I) 
>>>>>>>>>>>> the
>>>>>>>>>>>> iterations would test the combinations in order:
>>>>>>>>>>>>
>>>>>>>>>>>> A D H
>>>>>>>>>>>> B D H
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> I suppose a simpler form of my above example is: if, on the
>>>>>>>>>>> first pass, we get ADH, on the next pass the code as-is would 
>>>>>>>>>>> result in
>>>>>>>>>>> BCI, wouldn't it? It'll "next" each of the corrections.
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> (I think you meant BEI, and C is only a candidate of the first
>>>>>>>>>> TypoExpr, not the second one.) No it won't because of the caching. 
>>>>>>>>>> The
>>>>>>>>>> second TypoExpr is only effectively "next"ed once all of the 
>>>>>>>>>> candidates for
>>>>>>>>>> the first TypoExpr have been tried with the current candidate of the 
>>>>>>>>>> second
>>>>>>>>>> TypoExpr, and the third TypoExpr would only be "next"ed if all of the
>>>>>>>>>> candidates of both the first and second TypoExpr have been tried 
>>>>>>>>>> with the
>>>>>>>>>> current candidate of the third TypoExpr.
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> And I'm still not quite understanding why there's the  E !=
>>>>>>>>>>> TypoExprs[0] condition... maybe that's part of what I'm missing. It 
>>>>>>>>>>> looks
>>>>>>>>>>> like that code would cause, in a single Transform, multiple 
>>>>>>>>>>> instances of
>>>>>>>>>>> the first typo to be computed with different/subsequent typo 
>>>>>>>>>>> results...
>>>>>>>>>>> which seems confusing/strange. (though I don't fully understand 
>>>>>>>>>>> how/where
>>>>>>>>>>> multiple instances of the same TypoExpr will be visited in a single
>>>>>>>>>>> Transform)
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> That condition ensures that the cached result is never used for
>>>>>>>>>> the first TypoExpr (instead of having an extra conditional for all 
>>>>>>>>>> of the
>>>>>>>>>> return statements below here to not cache the result for the first
>>>>>>>>>> TypoExpr).
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> (some nitpicks of the code - there's a duplicate lookup of
>>>>>>>>>>> TransformCache (count then [], or count then insert further down the
>>>>>>>>>>> algorithm - it should probably just be:
>>>>>>>>>>>
>>>>>>>>>>> ExprResult &CacheEntry = TransformCache[E];
>>>>>>>>>>> if (CacheEntry)
>>>>>>>>>>>   return CacheEntry;
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> This wouldn't work because "TransformCache[E]" would add a cache
>>>>>>>>>> entry for E, and either the "if (CacheEntry)" would always be true, 
>>>>>>>>>> or an
>>>>>>>>>> ExprError() would be considered false and an ExprError cache entry 
>>>>>>>>>> would
>>>>>>>>>> always be ignored, both of which would be bad.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> you're right that ExprResult isn't boolean testable - but it is
>>>>>>>>> possible to distinguish ExprError from a default-constructed 
>>>>>>>>> ExprResult. A
>>>>>>>>> default constructed ExprResult is in the valid-but-unset state, so 
>>>>>>>>> far as I
>>>>>>>>> can tell, so it would look like:
>>>>>>>>>
>>>>>>>>> if (!CacheEntry.isUnset())
>>>>>>>>>   return CacheEntry
>>>>>>>>>
>>>>>>>>> (which is, admittedly, not the /most/ legible thing ever)
>>>>>>>>>
>>>>>>>>
>>>>>>>> I'm worried this won't work, or at least would be (potentially)
>>>>>>>> fragile, because .isUnset() is defined as "valid-but-null" and Sema 
>>>>>>>> sets
>>>>>>>> valid-but-null as an explicit state in the return values of various
>>>>>>>> functions--at least a few of which I saw while looking at users of the 
>>>>>>>> typo
>>>>>>>> correction code.
>>>>>>>>
>>>>>>>
>>>>>>> Ooh... I could totally see that happening (& thanks for the
>>>>>>> explanation).
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>> Of course, a new local variable could be created containing the
>>>>>>>>>> result of TransformCache.find(E) and then compared to 
>>>>>>>>>> TransformCache.end()
>>>>>>>>>> and returning as appropriate, but I find it to be more verbose and 
>>>>>>>>>> less
>>>>>>>>>> clear:
>>>>>>>>>>
>>>>>>>>>> auto CacheEntry = TransformCache.find(E);
>>>>>>>>>> if (!TypoExprs.insert(E) && CacheEntry != TransformCache.end())
>>>>>>>>>>   return CacheEntry->second;
>>>>>>>>>>
>>>>>>>>>> And neither of the subsequent return statements would be changed
>>>>>>>>>> because the cache entry would still need to be created...
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> If you were using the iterator version, they could be hinted
>>>>>>>>> insertions which might be better - but I agree, if you're just using 
>>>>>>>>> the
>>>>>>>>> iterators it's probably not terribly helpful. But as shown above, you 
>>>>>>>>> can
>>>>>>>>> actually just use the op[] at the start, check !isUnset, then 
>>>>>>>>> initialize
>>>>>>>>> the elements. This is a fairly common idiom in map related code, 
>>>>>>>>> though
>>>>>>>>> more often it looks like
>>>>>>>>>
>>>>>>>>
>>>>>>>> After a bit more consideration, I've taken your suggested approach
>>>>>>>> of using .isUnset() and added an assertion on trying to cache an 
>>>>>>>> "unset"
>>>>>>>> value ("assert(!NE.isUnset() && ...);").
>>>>>>>>
>>>>>>>
>>>>>>> Sounds like a fair tradeoff - if the assertion does fire we can add
>>>>>>> a test case for it & switch back to something like your original
>>>>>>> formulation.
>>>>>>>
>>>>>>> The other gotcha to be aware of is that since it's a reference into
>>>>>>> a DenseMap are invalidated by insertions - so if any of the work to
>>>>>>> initialize the TransformCache (the while nextCorrection loop, and 
>>>>>>> probably
>>>>>>> most importantly the "BuildDeclarationNameExpr" call) can also modify 
>>>>>>> the
>>>>>>> TransformCache (by transforming another typo expr) then the reference 
>>>>>>> will
>>>>>>> be invalidated and the approach I'd suggested won't be applicable (you
>>>>>>> could still keep just one lookup in the cache hit case, but in the cache
>>>>>>> miss you'd have to do a second lookup after the work)
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> ...
>>>>>>>>>>>     return CacheEntry = NE;
>>>>>>>>>>> }
>>>>>>>>>>> return CacheEntry = ExprError();
>>>>>>>>>>>
>>>>>>>>>>> )
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>> C D H
>>>>>>>>>>>> A E H
>>>>>>>>>>>> B E H
>>>>>>>>>>>> C E H
>>>>>>>>>>>> A F H
>>>>>>>>>>>> B F H
>>>>>>>>>>>> C F H
>>>>>>>>>>>> A G H
>>>>>>>>>>>> B G H
>>>>>>>>>>>> C G H
>>>>>>>>>>>> A D I
>>>>>>>>>>>> B D I
>>>>>>>>>>>> C D I
>>>>>>>>>>>> A E I
>>>>>>>>>>>> B E I
>>>>>>>>>>>> C E I
>>>>>>>>>>>> A F I
>>>>>>>>>>>> B F I
>>>>>>>>>>>> C F I
>>>>>>>>>>>> A G I
>>>>>>>>>>>> B G I
>>>>>>>>>>>> C G I
>>>>>>>>>>>>
>>>>>>>>>>>>> If we get to the end, have invalid code, and the first typo has 
>>>>>>>>>>>>> no alternatives left to try - I guess we just want to print 
>>>>>>>>>>>>> errors for everything, without typo corrections?
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>> If all of the combinations of typo corrections failed to create
>>>>>>>>>>>> an acceptable Expr, then the current correction for all of 
>>>>>>>>>>>> TypoExprs would
>>>>>>>>>>>> be an empty TypoCorrection which would cause the corresponding 
>>>>>>>>>>>> diagnostic
>>>>>>>>>>>> handlers to print out errors without suggestions.
>>>>>>>>>>>>
>>>>>>>>>>>>> It might be helpful to split out some of these chunks (the state 
>>>>>>>>>>>>> resetting code and maybe the diagnostic printing at the end) into 
>>>>>>>>>>>>> separate functions to give them logical names & make the higher 
>>>>>>>>>>>>> level algorithm more clear?
>>>>>>>>>>>>>
>>>>>>>>>>>>> I've split the diagnostics emission and the state handling
>>>>>>>>>>>> into separate helpers, and expanded their description comments. 
>>>>>>>>>>>> I'm not
>>>>>>>>>>>> keen on the name for the state handler but it is the best name I 
>>>>>>>>>>>> could come
>>>>>>>>>>>> up with that was even remotely descriptive.
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Roughly works, though I'm still quite confused, it seems.
>>>>>>>>>>> (alternative name for that function would be "reset" rather than 
>>>>>>>>>>> "advance"
>>>>>>>>>>> - but once I understand the algorithm better I might have better
>>>>>>>>>>> suggestions)
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> The function removes the cached result for the first TypoExpr
>>>>>>>>>> then checks the TypoExpr's correction candidate stream; if it has 
>>>>>>>>>> reached
>>>>>>>>>> the end of the stream, it will reset the stream then perform the 
>>>>>>>>>> same check
>>>>>>>>>> for the second TypoExpr, continuing until it either has reset the
>>>>>>>>>> correction streams for all of the TypoExprs or has found a TypoExpr 
>>>>>>>>>> that
>>>>>>>>>> hadn't reached the end of its correction stream. And in the former 
>>>>>>>>>> case, it
>>>>>>>>>> would signal that all combinations of corrections had been retried 
>>>>>>>>>> and the
>>>>>>>>>> TreeTransform should stop searching for a valid combination of 
>>>>>>>>>> corrections.
>>>>>>>>>> And now that I think about it, the "E != TypoExprs[0]" is probably 
>>>>>>>>>> now a
>>>>>>>>>> superfluous check since calling 
>>>>>>>>>> CheckAndAdvanceTypoExprCorrectionStreams
>>>>>>>>>> would always remove the first TypoExpr from the result cache.
>>>>>>>>>>
>>>>>>>>>> The loop at the end ("for (auto E : TypoExprs) {") is over a hashing 
>>>>>>>>>> data structure, right? Is that going to cause unstable output of 
>>>>>>>>>> diagnostics? (eg: different machines, different memory layout, the 
>>>>>>>>>> same program might get the typos printed in a different order) or is 
>>>>>>>>>> there something else that will fix that?
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> If you have to change the set to be a SetVector, or similar (to 
>>>>>>>>>>>>> stabilize output) - then you won't need the FirstTypoExpr anymore 
>>>>>>>>>>>>> - it'll just be the first one in the set, since you iterate in 
>>>>>>>>>>>>> insertion order at that point.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Switched it from a SmallPtrSet to a SmallSetVector like it
>>>>>>>>>>>> should have been. Doing so also allowed me to simplify the state 
>>>>>>>>>>>> handling
>>>>>>>>>>>> code by being able to assume a deterministic order, and eliminating
>>>>>>>>>>>> FirstTypoExpr.
>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> "while (TypoCorrection TC = State.Consumer->getNextCorrection()) 
>>>>>>>>>>>>> {" doesn't seem to be a loop - it has an unconditional return at 
>>>>>>>>>>>>> the end of the body. Should it just be an 'if'?
>>>>>>>>>>>>>
>>>>>>>>>>>>> There was actually a missing check on whether the result of
>>>>>>>>>>>> Sema::BuildDeclarationNameExpr was valid.
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Were there missing/added test cases to cover this situation?
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> No because at this point in the patch series, there is nothing
>>>>>>>>>> that actually generates TypoExprs.
>>>>>>>>>>
>>>>>>>>>>> "+ (FullExpr.get()->isTypeDependent() ||
>>>>>>>>>>>>>
>>>>>>>>>>>>> +       FullExpr.get()->isValueDependent() ||
>>>>>>>>>>>>> +       FullExpr.get()->isInstantiationDependent())) {"
>>>>>>>>>>>>>
>>>>>>>>>>>>> What are those checks for? I don't know what condition we would 
>>>>>>>>>>>>> be in if we had typo corrections but it wasn't type, value, or 
>>>>>>>>>>>>> instantiation dependent. Perhaps a comment could explain that?
>>>>>>>>>>>>>
>>>>>>>>>>>>> The check is to avoid running the tree transform on Expr trees
>>>>>>>>>>>> that are known to not have a TypoExpr in them, regardless of 
>>>>>>>>>>>> whether the
>>>>>>>>>>>> current expression evaluation context still indicates there are 
>>>>>>>>>>>> uncorrected
>>>>>>>>>>>> typos. I've added a comment to that effect.
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Hmm - I suppose my question might then be: When would we have
>>>>>>>>>>> NumTypos > 0 but be in a situation where the full expression cannot 
>>>>>>>>>>> have
>>>>>>>>>>> any typos in it?
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> I don't remember off the top of my head because it's been a few
>>>>>>>>>> months now since I wrote the original patches... but basically any
>>>>>>>>>> situation where more than one FullExpr shares an expression 
>>>>>>>>>> evaluation
>>>>>>>>>> context or when the current expression evaluation context had its 
>>>>>>>>>> NumTypos
>>>>>>>>>> increased by an expression evaluation context that was popped off the
>>>>>>>>>> stack. And TreeTransforms aren't cheap enough to blindly run on 
>>>>>>>>>> every Expr
>>>>>>>>>> when NumTypos is non-zero.
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>> To simplify Sema::clearDelayedTypo you could add 
>>>>>>>>>>>> MapVector::erase(const KeyType&) (given that std::map has such an 
>>>>>>>>>>>> operation, it seems like a reasonable one for MapVector to have).
>>>>>>>>>>>>>
>>>>>>>>>>>>> Done (simple patch attached).
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Great - if you'd like to commit this separately, it should have
>>>>>>>>>>> a unit test. Otherwise it's probably simple enough to commit along 
>>>>>>>>>>> with
>>>>>>>>>>> your new usage if you prefer.
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Committed with unit test as r219240.
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Sema::getTypoExprState looks like it could be a const function
>>>>>>>>>>>>> (it looks like it's never meant to be called when the element 
>>>>>>>>>>>>> isn't already
>>>>>>>>>>>>> in the map) - using 'lookup' instead of operator[] will be a const
>>>>>>>>>>>>> operation on the map (& assert if the element doesn't exist in 
>>>>>>>>>>>>> the map).
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Sadly I cannot do that because the operator[] cannot be used if
>>>>>>>>>>>> the method is made const, and .lookup() can't be used because it 
>>>>>>>>>>>> returns
>>>>>>>>>>>> by-value instead of by-reference and TypoExprState is not copyable:
>>>>>>>>>>>>
>>>>>>>>>>>> ../tools/clang/lib/Sema/SemaLookup.cpp:4600:10: warning:
>>>>>>>>>>>> returning reference to local temporary object 
>>>>>>>>>>>> [-Wreturn-stack-address]
>>>>>>>>>>>>   return DelayedTypos.lookup(TE);
>>>>>>>>>>>>
>>>>>>>>>>>> ../include/llvm/ADT/MapVector.h:88:30: error: call to
>>>>>>>>>>>> implicitly-deleted copy constructor of 'const 
>>>>>>>>>>>> clang::Sema::TypoExprState'
>>>>>>>>>>>>     return Pos == Map.end()? ValueT() :
>>>>>>>>>>>> Vector[Pos->second].second;
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Oh, weird - my mistake in assuming/misremembering what lookup
>>>>>>>>>>> actually does. I'd still make getTypoExprState const and use 
>>>>>>>>>>> find+assert
>>>>>>>>>>> just to constrain the interface a bit more rather than leaving it 
>>>>>>>>>>> possible
>>>>>>>>>>> to accidentally create a new entry in the map when the API is only 
>>>>>>>>>>> intended
>>>>>>>>>>> to query existing entries.
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Done. The new changes I've made overall are:
>>>>>>>>>>
>>>>>>>>>> diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
>>>>>>>>>> index b30bf5e..f644fdd 100644
>>>>>>>>>> --- a/include/clang/Sema/Sema.h
>>>>>>>>>> +++ b/include/clang/Sema/Sema.h
>>>>>>>>>> @@ -2628,7 +2628,7 @@ private:
>>>>>>>>>>                               bool ErrorRecovery, bool
>>>>>>>>>> &IsUnqualifiedLookup);
>>>>>>>>>>
>>>>>>>>>>  public:
>>>>>>>>>> -  const TypoExprState &getTypoExprState(TypoExpr *TE);
>>>>>>>>>> +  const TypoExprState &getTypoExprState(TypoExpr *TE) const;
>>>>>>>>>>
>>>>>>>>>>    /// \brief Clears the state of the given TypoExpr.
>>>>>>>>>>    void clearDelayedTypo(TypoExpr *TE);
>>>>>>>>>> diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
>>>>>>>>>> index 73aa997..1f08276 100644
>>>>>>>>>> --- a/lib/Sema/SemaExprCXX.cpp
>>>>>>>>>> +++ b/lib/Sema/SemaExprCXX.cpp
>>>>>>>>>> @@ -6028,7 +6028,7 @@ public:
>>>>>>>>>>      // If the TypoExpr hasn't been seen before, record it.
>>>>>>>>>> Otherwise, return the
>>>>>>>>>>      // cached transformation result if there is one and the
>>>>>>>>>> TypoExpr isn't the
>>>>>>>>>>      // first one that was encountered.
>>>>>>>>>> -    if (!TypoExprs.insert(E) && E != TypoExprs[0] &&
>>>>>>>>>> TransformCache.count(E)) {
>>>>>>>>>> +    if (!TypoExprs.insert(E) && TransformCache.count(E)) {
>>>>>>>>>>        return TransformCache[E];
>>>>>>>>>>      }
>>>>>>>>>>
>>>>>>>>>> diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp
>>>>>>>>>> index 8ddb5fd..bb6c76a 100644
>>>>>>>>>> --- a/lib/Sema/SemaLookup.cpp
>>>>>>>>>> +++ b/lib/Sema/SemaLookup.cpp
>>>>>>>>>> @@ -4601,8 +4601,11 @@ TypoExpr
>>>>>>>>>> *Sema::createDelayedTypo(std::unique_ptr<TypoCorrectionConsumer> TCC,
>>>>>>>>>>    return TE;
>>>>>>>>>>  }
>>>>>>>>>>
>>>>>>>>>> -const Sema::TypoExprState &Sema::getTypoExprState(TypoExpr *TE) {
>>>>>>>>>> -  return DelayedTypos[TE];
>>>>>>>>>> +const Sema::TypoExprState &Sema::getTypoExprState(TypoExpr *TE)
>>>>>>>>>> const {
>>>>>>>>>> +  auto Entry = DelayedTypos.find(TE);
>>>>>>>>>> +  assert(Entry != DelayedTypos.end() &&
>>>>>>>>>> +         "Failed to get the state for a TypoExpr!");
>>>>>>>>>> +  return Entry->second;
>>>>>>>>>>  }
>>>>>>>>>>
>>>>>>>>>>  void Sema::clearDelayedTypo(TypoExpr *TE) {
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>> Otherwise this all looks like rather neat stuff - though I'm
>>>>>>>>>>>>> not as able to review in detail some of the refactoring of 
>>>>>>>>>>>>> existing typo
>>>>>>>>>>>>> correction stuff. It looks plausible.
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> I've attached the updated patch. Thanks for reviewing this,
>>>>>>>>>>>> David!
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>> On Thu, Sep 25, 2014 at 2:00 PM, Kaelyn Takata <
>>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>>> ping
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> On Tue, Aug 26, 2014 at 11:05 AM, Kaelyn Takata <
>>>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> +dblaikie
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> On Thu, Jul 31, 2014 at 1:20 PM, Kaelyn Takata <
>>>>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Part of the infrastructure is a map from a TypoExpr to the
>>>>>>>>>>>>>>>> Sema-specific
>>>>>>>>>>>>>>>> state needed to correct it, along with helpers to ease
>>>>>>>>>>>>>>>> dealing with the
>>>>>>>>>>>>>>>> state.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> The the typo count is propagated up the stack of
>>>>>>>>>>>>>>>> ExpressionEvaluationContextRecords when one is popped off
>>>>>>>>>>>>>>>> of to
>>>>>>>>>>>>>>>> avoid accidentally dropping TypoExprs on the floor. For
>>>>>>>>>>>>>>>> example,
>>>>>>>>>>>>>>>> the attempted correction of g() in
>>>>>>>>>>>>>>>> test/CXX/class/class.mem/p5-0x.cpp
>>>>>>>>>>>>>>>> happens with an ExpressionEvaluationContextRecord that is
>>>>>>>>>>>>>>>> popped off
>>>>>>>>>>>>>>>> the stack prior to ActOnFinishFullExpr being called and the
>>>>>>>>>>>>>>>> tree
>>>>>>>>>>>>>>>> transform for TypoExprs being run.
>>>>>>>>>>>>>>>> ---
>>>>>>>>>>>>>>>>  include/clang/Sema/Sema.h           |  44 +++++
>>>>>>>>>>>>>>>>  include/clang/Sema/SemaInternal.h   |  15 +-
>>>>>>>>>>>>>>>>  include/clang/Sema/TypoCorrection.h |   2 +-
>>>>>>>>>>>>>>>>  lib/Sema/SemaExpr.cpp               |   7 +
>>>>>>>>>>>>>>>>  lib/Sema/SemaExprCXX.cpp            | 108 ++++++++++++
>>>>>>>>>>>>>>>>  lib/Sema/SemaLookup.cpp             | 316
>>>>>>>>>>>>>>>> ++++++++++++++++++++++++------------
>>>>>>>>>>>>>>>>  6 files changed, 384 insertions(+), 108 deletions(-)
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to