================
Comment at: lib/Sema/SemaOverload.cpp:8905-8918
@@ +8904,16 @@
+      // a relevant part of the alias template instantiation.
+      assert(PD.getNumArgs() >= 1 && PDRanges.size() >= 1 &&
+             PD.getArgKind(0) == DiagnosticsEngine::ak_declcontext &&
+             (PD.getNumArgs() == 1 ||
+              (PD.getArgKind(1) == DiagnosticsEngine::ak_nameddecl &&
+               PDRanges.size() >= 2)) &&
+             "an err_typename_nested_not_found_enable_if diagnostic does"
+             " not contain the expected arguments and source ranges");
+      const bool IsAlias = PDRanges.size() > 1;
+      const NamedDecl *EnableIfOrAliasDecl =
+          IsAlias ? reinterpret_cast<NamedDecl *>(PD.getRawArg(1))
+                  : cast<ClassTemplateSpecializationDecl>(
+                        reinterpret_cast<DeclContext *>(PD.getRawArg(0)))
+                        ->getSpecializedTemplate();
+      SourceRange Range = PDRanges[IsAlias ? 1 : 0].getAsRange();
+      // If an alias template that contains an enable_if type lookup is
----------------
Richard Smith wrote:
> Stephan Tolksdorf wrote:
> > Richard Smith wrote:
> > > I don't like inspecting the source ranges on a `PartialDiagnostic` -- 
> > > they're not part of the numbered argument set for the diagnostic, so 
> > > someone emitting the diagnostic is within their rights to change the set 
> > > of ranges they provide. More generally, I'm not really a fan of digging 
> > > into the implementation of the `PartialDiagnostic` to find its arguments.
> > > 
> > > Instead, how about switching the diagnostic ID from 
> > > err_typename_nested_not_found_enable_if to 
> > > note_ovl_candidate_disabled_by_enable_if (with comments in the .td file 
> > > saying that they're required to take the same arguments)?
> > > 
> > > Instead of using the `Range`, below, to determine the location for the 
> > > diagnostic, you could use something like 
> > > `TemplDecl->getSourceRange().contains(PDiag->first) ? PDiag->first : 
> > > Templated->getLocation()`.
> > In my view a `PartialDiagnostic` (or `Diagnostic`) is not just the input to 
> > some formatting routine but a strongly typed container of an error id and 
> > additional pieces of information about the error. So, accessing the 
> > arguments to the diagnostic through a proper API (introduced by D3060) in 
> > order to generate a better error message doesn't seem that objectionable to 
> > me. Also, I interpreted your FIXME as a request to do exactly this.
> > 
> > I used a second source range and the 
> > `err_typename_nested_not_found_enable_if` id because I assumed that in 
> > `Sema::CheckTypenameType` we should always generate an error that would be 
> > appropriate if the error message is not suppressed by the SFINAE logic. If 
> > this is not the case, it might be cleaner to always prepare the enable_if 
> > diagnostic in `CheckTypenameType` and then just reemit it in 
> > `DiagnoseBadDeduction` (adding the missing `TemplateArgString`).
> > 
> > If you'd like this code to not fail in the hypothetical case where someone 
> > generates a `err_typename_nested_not_found_enable_if` diagnostic with 
> > unexpected contents, I could replace the assert with some always-on 
> > fallback logic.
> > 
> > 
> I don't think a `PartialDiagnostic` is even notionally strongly typed (you 
> can use the same diagnostic with a string or a Decl for the same argument, 
> for instance, and we do that sort of thing in some cases). The approach 
> you're taking is fragile in the presence of refactoring or people reusing the 
> same diagnostic message in other circumstances (where they may not provide 
> the same SourceRanges). I'm also not particularly enthusiastic about a 
> fallback codepath -- I'd much rather have a clean way to pass the necessary 
> information through. I'm not yet sure what that mechanism is, though.
> 
> (Yes, the diagnostic produced in `CheckTypenameType` should be appropriate in 
> the case where we're not in a SFINAE condition.)
What I meant with strongly typed is that you can access the diagnostic 
arguments in a type safe way, as the types of the arguments are recorded and 
can be queried. Documenting the extra arguments in the diagnostic .td file 
should minimize any fragility.

If I want to pass on additional information about an error, putting it into the 
diagnostic data structure still seems rather natural to me. In this case the 
only alternative I can think of is to put this as an additional field into the 
`TemplateDeductionInfo`, but I don't think that would be cleaner.






================
Comment at: lib/Sema/SemaTemplate.cpp:7887-7919
@@ -7844,1 +7886,35 @@
+               << Ctx << CondRange;
+      if (const Sema::ActiveTemplateInstantiation *ATI =
+              findOutermostAliasTemplateInstantiationInSFINAEContext(*this)) {
+        // For a failed enable_if lookup within an alias template instantiation
+        // we also store the source range of the instantiation and a pointer to
+        // the TypeAliasTemplateDecl in the PartialDiagnostic.
+        // This allows us is Sema::DiagnoseBadDeduction to highlight a source
+        // range that is actually *within* the declaration of the disabled
+        // overload (and not in the declaration of the alias template).
+        // We don't restrict this handling to alias templates named enable_if_t
+        // because if a failed 'typename enable_if<...>::type' lookup occurs
+        // within an alias template instantiation in a SFINAE context, it is
+        // highly likely that the alias is used similarly to std::enable_if_t.
+        auto *AliasDecl = cast<TypeAliasTemplateDecl>(ATI->Entity);
+        // Sema::CheckTemplateIdType stores the source range for the first
+        // template argument in ATI->InstantiationRange.
+        SourceRange Arg0Range = ATI->InstantiationRange;
+        auto *EnableIfII = AliasDecl->getDeclName().getAsIdentifierInfo();
+        TemplateParameterList *TPL = AliasDecl->getTemplateParameters();
+        if (Arg0Range.isInvalid() || TPL->size() == 0)
+          CondRange = ATI->PointOfInstantiation;
+        else if (TPL->size() == 1 || EnableIfII->isStr("enable_if_t"))
+          CondRange = Arg0Range;
+        else {
+          // If the alias template has more than one parameter and is not
+          // called enable_if_t, we can't be sure that the first argument is
+          // actually the one that caused the error. So instead of underlining
+          // the full argument, we only point to the beginning of it, which
+          // should avoid misunderstandings while still pointing (close) to the
+          // origin of the error.
+          CondRange = Arg0Range.getBegin();
+        }
+        D << AliasDecl << CondRange;
+      }
       return QualType();
----------------
Richard Smith wrote:
> Stephan Tolksdorf wrote:
> > Richard Smith wrote:
> > > Having reflected on this a bit more, I think we should *only* do this if 
> > > the context surrounding the `enable_if` is an alias template named 
> > > `enable_if_t` (and we should only walk through one such alias template). 
> > > If we walk too far away from the `enable_if` expression itself, it will 
> > > no longer be obvious what we're complaining about.
> > If we don't walk up all alias template instantiations in the current SFINAE 
> > context on the instantiation stack, the SFINAE diagnostic will only state 
> > ##no type named 'type' in 'enable_if<false>'## or ##disabled by 
> > 'enable_if'## without any indication which alias template instantiation 
> > caused this error, since the SFINAE diagnostic doesn't contain the stack of 
> > instantiated alias templates for the suppressed error. (Would it maybe be 
> > an alternative to store and print this stack when we don't have a 
> > straightforward enable_if case?)
> > 
> > In my opinion such a diagnostic is significantly worse than a diagnostic 
> > that states something like e.g. ##disabled by 'enable_if_is_callback'## or 
> > ##disabled by an error during the substitution of the alias template 
> > 'enable_if_is_callback'## and that points to the source location of the 
> > `enable_if_is_callback` instantiation in the function declaration 
> > triggering the error.
> > 
> > Could you maybe give an example for a situation where you'd find the 
> > proposed diagnostic confusing? Would rewording the diagnostic in that 
> > situation be an alternative to falling back to the basic SFINAE diagnostic?
> > 
> > 
> Hmm, suppose I have:
> 
>   template<typename T> void f(Iterator<T> iter);
> 
> with `Iterator` an alias template. If substitution fails for any reason 
> within `Iterator<T>`, I don't want to be told `disabled by 'Iterator'`. I 
> think in this case I actually want to produce two notes for the substitution 
> failure: one indicating that the candidate was ignored due to a substitution 
> failure (pointing at the candidate), and another pointing at the location 
> within the alias template where we hit the problem.
> 
> I think we can do this without any alias template special-casing: when we 
> come to emit a delayed 'substitution failure' diagnostic, check whether the 
> location of the diagnostic is within the source range of the declaration of 
> the candidate. If so, do what we do today, and if not, produce two notes (one 
> pointing at the candidate and the other pointing at the location where we hit 
> the error.
It would be really nice if we could tell the user that the substitution failure 
occurred within `Iterator<T>`. In this case there is no other possible place, 
but in more complicated situations it would make the error easier to 
understand, especially if the error occurred not directly within the 
`Iterator<T>` declaration but inside a nested alias template use. When you work 
with with an IDE that highlights the source ranges in an editor, this can be a 
real improvement. (And, of course, we can use a different wording than 
"disabled by xxx" for this and similar cases.)

Without any alias template special-casing the location of the original 
diagnostic for an error inside an alias template instantiation will never be 
within the source range of the declaration of the candidate and `enable_if_t` 
uses will generate arguably worse error messages than `enable_if` uses.






http://llvm-reviews.chandlerc.com/D3061
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to