@@ -453,109 +453,38 @@ class StmtComparer {
453453};
454454} // namespace
455455
456- static bool IsStructurallyEquivalent (StructuralEquivalenceContext &Context,
457- const Attr *Attr1, const Attr *Attr2) {
458- // Two attributes are structurally equivalent if they are the same kind
459- // of attribute, spelled with the same spelling kind, and have the same
460- // arguments. This means that [[noreturn]] and __attribute__((noreturn)) are
461- // not structurally equivalent, nor are [[nodiscard("foo")]] and
462- // [[nodiscard("bar")]].
463- if (Attr1->getKind () != Attr2->getKind ())
464- return false ;
465-
466- if (Attr1->getSyntax () != Attr2->getSyntax ())
467- return false ;
468-
469- if (Attr1->getSpellingListIndex () != Attr2->getSpellingListIndex ())
470- return false ;
471-
472- auto GetAttrName = [](const Attr *A) {
473- if (const IdentifierInfo *II = A->getAttrName ())
474- return II->getName ();
475- return StringRef{};
476- };
477-
478- if (GetAttrName (Attr1) != GetAttrName (Attr2))
479- return false ;
480-
481- // FIXME: check the attribute arguments. Attr does not track the arguments on
482- // the base class, which makes this awkward. We may want to tablegen a
483- // comparison function for attributes? In the meantime, we're doing this the
484- // cheap way by pretty printing the attributes and checking they produce
485- // equivalent string representations.
486- std::string AttrStr1, AttrStr2;
487- PrintingPolicy DefaultPolicy (Context.LangOpts );
488- llvm::raw_string_ostream SS1 (AttrStr1), SS2 (AttrStr2);
489- Attr1->printPretty (SS1, DefaultPolicy);
490- Attr2->printPretty (SS2, DefaultPolicy);
491-
492- return SS1.str () == SS2.str ();
493- }
494-
495456static bool
496457CheckStructurallyEquivalentAttributes (StructuralEquivalenceContext &Context,
497458 const Decl *D1, const Decl *D2,
498459 const Decl *PrimaryDecl = nullptr ) {
499- // Gather the attributes and sort them by name so that they're in equivalent
500- // orders. This means that __attribute__((foo, bar)) is equivalent to
501- // __attribute__((bar, foo)).
502- llvm::SmallVector<const Attr *, 2 > Attrs1, Attrs2;
503- llvm::copy (D1->attrs (), std::back_inserter (Attrs1));
504- llvm::copy (D2->attrs (), std::back_inserter (Attrs2));
505-
506- auto Sorter = [](const Attr *LHS, const Attr *RHS) {
507- const IdentifierInfo *II1 = LHS->getAttrName (), *II2 = RHS->getAttrName ();
508- if (!II1 || !II2)
509- return II1 == II2;
510- return *II1 < *II2;
511- };
512- llvm::sort (Attrs1, Sorter);
513- llvm::sort (Attrs2, Sorter);
514-
515- auto A2 = Attrs2.begin (), A2End = Attrs2.end ();
516- const auto *DiagnoseDecl = cast<TypeDecl>(PrimaryDecl ? PrimaryDecl : D2);
517- for (auto A1 = Attrs1.begin (), A1End = Attrs1.end (); A1 != A1End;
518- ++A1, ++A2) {
519- if (A2 == A2End) {
520- if (Context.Complain ) {
521- Context.Diag2 (DiagnoseDecl->getLocation (),
522- Context.getApplicableDiagnostic (
523- diag::err_odr_tag_type_inconsistent))
524- << Context.ToCtx .getTypeDeclType (DiagnoseDecl)
525- << (&Context.FromCtx != &Context.ToCtx );
526- Context.Diag1 ((*A1)->getLocation (), diag::note_odr_attr) << *A1;
527- Context.Diag2 (D2->getLocation (), diag::note_odr_attr_missing);
528- }
529- return false ;
530- }
531-
532- if (!IsStructurallyEquivalent (Context, *A1, *A2)) {
533- if (Context.Complain ) {
534- Context.Diag2 (DiagnoseDecl->getLocation (),
535- Context.getApplicableDiagnostic (
536- diag::err_odr_tag_type_inconsistent))
537- << Context.ToCtx .getTypeDeclType (DiagnoseDecl)
538- << (&Context.FromCtx != &Context.ToCtx );
539- Context.Diag2 ((*A2)->getLocation (), diag::note_odr_attr) << *A2;
540- Context.Diag1 ((*A1)->getLocation (), diag::note_odr_attr) << *A1;
541- }
542- return false ;
543- }
544- }
545-
546- if (A2 != A2End) {
547- if (Context.Complain ) {
548- Context.Diag2 (
549- DiagnoseDecl->getLocation (),
550- Context.getApplicableDiagnostic (diag::err_odr_tag_type_inconsistent))
551- << Context.ToCtx .getTypeDeclType (DiagnoseDecl)
552- << (&Context.FromCtx != &Context.ToCtx );
553- Context.Diag1 (D1->getLocation (), diag::note_odr_attr_missing);
554- Context.Diag1 ((*A2)->getLocation (), diag::note_odr_attr) << *A2;
555- }
556- return false ;
557- }
558-
460+ // If either declaration has an attribute on it, we treat the declarations
461+ // as not being structurally equivalent.
462+ // FIXME: this should be handled on a case-by-case basis via tablegen in
463+ // Attr.td. There are multiple cases to consider: one declation with the
464+ // attribute, another without it; different attribute syntax|spellings for
465+ // the same semantic attribute, differences in attribute arguments, order
466+ // in which attributes are applied, how to merge attributes if the types are
467+ // structurally equivalent, etc.
468+ const Attr *D1Attr = nullptr , *D2Attr = nullptr ;
469+ if (D1->hasAttrs ())
470+ D1Attr = *D1->getAttrs ().begin ();
471+ if (D2->hasAttrs ())
472+ D2Attr = *D2->getAttrs ().begin ();
473+ if (D1Attr || D2Attr) {
474+ const auto *DiagnoseDecl = cast<TypeDecl>(PrimaryDecl ? PrimaryDecl : D2);
475+ Context.Diag2 (DiagnoseDecl->getLocation (),
476+ diag::warn_odr_tag_type_with_attributes)
477+ << Context.ToCtx .getTypeDeclType (DiagnoseDecl)
478+ << (PrimaryDecl != nullptr );
479+ if (D1Attr)
480+ Context.Diag1 (D1Attr->getLoc (), diag::note_odr_attr_here) << D1Attr;
481+ if (D2Attr)
482+ Context.Diag1 (D2Attr->getLoc (), diag::note_odr_attr_here) << D2Attr;
483+ }
484+
485+ // The above diagnostic is a warning which defaults to an error. If treated
486+ // as a warning, we'll go ahead and allow any attribute differences to be
487+ // undefined behavior and the user gets what they get in terms of behavior.
559488 return true ;
560489}
561490
0 commit comments