[PATCH] D148381: [Clang] Implement the 'counted_by' attribute

Bill Wendling via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 3 15:28:07 PDT 2023


Yes, I mean to do it as a direct follow-up. 😊

-bw

On Tue, Oct 3, 2023, 6:31 AM Aaron Ballman via Phabricator <
reviews at reviews.llvm.org> wrote:

> aaron.ballman added inline comments.
>
>
> ================
> Comment at: clang/lib/Sema/SemaDeclAttr.cpp:8420-8428
> +    if (Result.getResultKind() == LookupResult::Found) {
> +      SourceRange SR = CBA->getCountedByFieldLoc();
> +      Diag(SR.getBegin(),
> +
>  diag::err_flexible_array_counted_by_attr_field_not_found_in_struct)
> +          << CBA->getCountedByField() << SR;
> +
> +      SR = Result.getAsSingle<NamedDecl>()->getSourceRange();
> ----------------
> void wrote:
> > aaron.ballman wrote:
> > > The logic here still seems incorrect. I was expecting the code to look
> more like this:
> > > ```
> > > bool Sema::CheckCountedByAttr(Scope *S, const FieldDecl *FD) {
> > >   const RecordDecl *RD = FD->getParent();
> > >   const auto *CBA = FD->getAttr<CountedByAttr>();
> > >   const IdentifierInfo *FieldName = CBA->getCountedByField();
> > >   DeclarationNameInfo NameInfo(FieldName,
> > >                                CBA->getCountedByFieldLoc().getBegin());
> > >   LookupResult Result(*this, NameInfo, Sema::LookupMemberName);
> > >
> > >   LookupName(Result, S);
> > >   if (Result.empty()) {
> > >     CXXScopeSpec SS;
> > >     DeclFilterCCC<FieldDecl> Filter(const_cast<IdentifierInfo
> *>(FieldName));
> > >     if (DiagnoseEmptyLookup(S, SS, Result, Filter, nullptr,
> std::nullopt,
> > >                             const_cast<DeclContext
> *>(FD->getDeclContext())))
> > >       return true;
> > >   }
> > >
> > >   const FieldDecl *Field = Result.getAsSingle<FieldDecl>();
> > >   LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
> > >       Context.getLangOpts().getStrictFlexArraysLevel();
> > >   ...
> > > ```
> > > I tested this locally on code like:
> > > ```
> > > struct not_a_fam {
> > >   int foo;
> > >   int fam[] __attribute__((counted_by(fob)));
> > > };
> > > ```
> > > and get a diagnostic like:
> > > ```
> > > C:\Users\aballman\OneDrive - Intel Corporation\Desktop\test.c:3:39:
> error: use of undeclared identifier 'fob'; did you
> > >       mean 'foo'?
> > >     3 |   int fam[] __attribute__((counted_by(fob)));
> > >       |                                       ^~~
> > >       |                                       foo
> > > C:\Users\aballman\OneDrive - Intel Corporation\Desktop\test.c:2:7:
> note: 'foo' declared here
> > >     2 |   int foo;
> > >       |       ^
> > > 1 error generated.
> > > ```
> > > Note, I had to add a constructor to `DeclFilterCCC` to expose the base
> class constructor, and modify `DiagnoseEmptyLookup()` like this:
> > > ```
> > > diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
> > > index 2ed31a90c5dc..3c4ade391a5e 100644
> > > --- a/clang/lib/Sema/SemaExpr.cpp
> > > +++ b/clang/lib/Sema/SemaExpr.cpp
> > > @@ -2458,7 +2458,8 @@ bool Sema::DiagnoseDependentMemberLookup(const
> LookupResult &R) {
> > >  bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS,
> LookupResult &R,
> > >                                 CorrectionCandidateCallback &CCC,
> > >                                 TemplateArgumentListInfo
> *ExplicitTemplateArgs,
> > > -                               ArrayRef<Expr *> Args, TypoExpr **Out)
> {
> > > +                               ArrayRef<Expr *> Args, DeclContext
> *LookupCtx,
> > > +                               TypoExpr **Out) {
> > >    DeclarationName Name = R.getLookupName();
> > >
> > >    unsigned diagnostic = diag::err_undeclared_var_use;
> > > @@ -2474,7 +2475,9 @@ bool Sema::DiagnoseEmptyLookup(Scope *S,
> CXXScopeSpec &SS, LookupResult &R,
> > >    // unqualified lookup.  This is useful when (for example) the
> > >    // original lookup would not have found something because it was a
> > >    // dependent name.
> > > -  DeclContext *DC = SS.isEmpty() ? CurContext : nullptr;
> > > +  DeclContext *DC =
> > > +      LookupCtx ? LookupCtx : (SS.isEmpty() ? CurContext : nullptr);
> > > +  DeclContext *OrigLookupCtx = DC;
> > >    while (DC) {
> > >      if (isa<CXXRecordDecl>(DC)) {
> > >        LookupQualifiedName(R, DC);
> > > @@ -2517,12 +2520,12 @@ bool Sema::DiagnoseEmptyLookup(Scope *S,
> CXXScopeSpec &SS, LookupResult &R,
> > >            emitEmptyLookupTypoDiagnostic(TC, *this, SS, Name, TypoLoc,
> Args,
> > >                                          diagnostic,
> diagnostic_suggest);
> > >          },
> > > -        nullptr, CTK_ErrorRecovery);
> > > +        nullptr, CTK_ErrorRecovery, OrigLookupCtx);
> > >      if (*Out)
> > >        return true;
> > > -  } else if (S &&
> > > -             (Corrected = CorrectTypo(R.getLookupNameInfo(),
> R.getLookupKind(),
> > > -                                      S, &SS, CCC,
> CTK_ErrorRecovery))) {
> > > +  } else if (S && (Corrected = CorrectTypo(R.getLookupNameInfo(),
> > > +                                           R.getLookupKind(), S, &SS,
> CCC,
> > > +                                           CTK_ErrorRecovery,
> OrigLookupCtx))) {
> > >      std::string CorrectedStr(Corrected.getAsString(getLangOpts()));
> > >      bool DroppedSpecifier =
> > >          Corrected.WillReplaceSpecifier() && Name.getAsString() ==
> CorrectedStr;
> > > @@ -2812,7 +2815,7 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec
> &SS,
> > >      // a template name, but we happen to have always already looked
> up the name
> > >      // before we get here if it must be a template name.
> > >      if (DiagnoseEmptyLookup(S, SS, R, CCC ? *CCC : DefaultValidator,
> nullptr,
> > > -                            std::nullopt, &TE)) {
> > > +                            std::nullopt, nullptr, &TE)) {
> > >        if (TE && KeywordReplacement) {
> > >          auto &State = getTypoExprState(TE);
> > >          auto BestTC = State.Consumer->getNextCorrection();
> > > ```
> > > Can you give something like this a shot and see how well it works for
> you?
> > I wanted to check for two different issues when the field isn't found:
> >
> > 1. The field is outside of the struct, or
> > 2. The field isn't found (or is misspelled).
> >
> > If I do your version, that distinction goes away.
> >
> > But if you want me to work on this more, would you mind if I did it in a
> follow-up commit? I'm not at all happy about adding yet another parameter
> to a method that already has a million parameters. It would be nice if that
> was cleaned up.
> > I wanted to check for two different issues when the field isn't found:
> >
> >   1. The field is outside of the struct, or
> >   2. The field isn't found (or is misspelled).
> >
> > If I do your version, that distinction goes away.
>
> Correct, my version is only looking for the field as a member name because
> we never want it to find a variable outside the scope of the structure. We
> could augment it to do what you want though:
> ```
> bool Sema::CheckCountedByAttr(Scope *S, const FieldDecl *FD) {
>   const RecordDecl *RD = FD->getParent();
>   const auto *CBA = FD->getAttr<CountedByAttr>();
>   const IdentifierInfo *FieldName = CBA->getCountedByField();
>   DeclarationNameInfo NameInfo(FieldName,
>                                CBA->getCountedByFieldLoc().getBegin());
>   LookupResult Result(*this, NameInfo, Sema::LookupMemberName);
>
>   LookupName(Result, S);
>   if (Result.empty()) {
>     // Look for the name outside of the scope of the structure so we can
> issue a different
>     // diagnostic in that case.
>     LookupResult BackupResult(*this, NameInfo, Sema::LookupOrdinaryName);
>     LookupName(BackupResult, S);
>     if (!BackupResult.empty()) {
>       return Diag(Loc, diag::whatever);
>     } else {
>       // Otherwise, diagnose the empty lookup.
>       CXXScopeSpec SS;
>       DeclFilterCCC<FieldDecl> Filter(const_cast<IdentifierInfo
> *>(FieldName));
>       if (DiagnoseEmptyLookup(S, SS, Result, Filter, nullptr, std::nullopt,
>                               const_cast<DeclContext
> *>(FD->getDeclContext())))
>         return true;
>     }
>   }
>
>   const FieldDecl *Field = Result.getAsSingle<FieldDecl>();
>   LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
>       Context.getLangOpts().getStrictFlexArraysLevel();
>   ...
> ```
>
> > But if you want me to work on this more, would you mind if I did it in a
> follow-up commit? I'm not at all happy about adding yet another parameter
> to a method that already has a million parameters. It would be nice if that
> was cleaned up.
>
> I don't insist on doing it as part of this patch but I do strongly prefer
> doing it now. Waiting runs the risk of not actually doing that work, but
> also, I worry that the lookups will be subtly different and we might risk
> breaking code by changing it later. That said, if you mean "(almost)
> immediately after landing this" kind of follow-up, I'm not worried about
> the breakage and that's fine.
>
>
> Repository:
>   rG LLVM Github Monorepo
>
> CHANGES SINCE LAST ACTION
>   https://reviews.llvm.org/D148381/new/
>
> https://reviews.llvm.org/D148381
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20231003/87e5b5b5/attachment-0001.html>


More information about the cfe-commits mailing list