This is an archive of the discontinued LLVM Phabricator instance.

[ASTStructuralEquivalence] Fix crash when ObjCCategoryDecl doesn't have corresponding ObjCInterfaceDecl.
ClosedPublic

Authored by vsapsai on May 25 2023, 6:16 PM.

Details

Summary

When this happens, it is invalid code and there is diagnostic

error: cannot find interface declaration for '...'

But clang shouldn't crash even if code is invalid. Though subsequent
diagnostic can be imperfect because without ObjCInterfaceDecl we don't have
a type for error messages.

rdar://108818430

Diff Detail

Event Timeline

vsapsai created this revision.May 25 2023, 6:16 PM
Herald added a project: Restricted Project. · View Herald TranscriptMay 25 2023, 6:16 PM
vsapsai requested review of this revision.May 25 2023, 6:16 PM
Herald added a project: Restricted Project. · View Herald TranscriptMay 25 2023, 6:16 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript

Kinda a follow-up to D121176.

One big alternative that I've considered is to store interface name in ObjCCategoryDecl because when we parse @interface InterfaceName(CategoryName) we know the interface name. The intention was to allow

@interface A(X) @end

and

@interface A @end
@interface A(X) @end

as equivalent. But I'm not convinced it is the right call and that's why it doesn't justify the extra effort.

shafik added inline comments.May 30 2023, 1:14 PM
clang/lib/AST/ASTStructuralEquivalence.cpp
2062

I think this would be easier to read if you checked Intf1 != Intf2 and then checked for nullptr

vsapsai added inline comments.May 30 2023, 4:19 PM
clang/lib/AST/ASTStructuralEquivalence.cpp
2062

I am totally up to the style that is more readable and consistent. I was just trying to mimic the check for Template1 and Template2. I agree that 1 (one) datapoint isn't representative, so I can check this file more thoroughly for the prevalent style. Do you have any other places in mind that are worth checking? I'll look for something more representative but it would help if you have something in mind already.

2291–2294

Another example of checking 2 elements for nullptr.

Kinda a follow-up to D121176.

One big alternative that I've considered is to store interface name in ObjCCategoryDecl because when we parse @interface InterfaceName(CategoryName) we know the interface name. The intention was to allow

@interface A(X) @end

and

@interface A @end
@interface A(X) @end

as equivalent. But I'm not convinced it is the right call and that's why it doesn't justify the extra effort.

Are there any cases you are aware of where this change would fix a bug? In any case, it sounds like that is something that can be done as a follow-up patch after fixing the crash.

clang/lib/AST/ASTStructuralEquivalence.cpp
2062

!Intf1 != !Intf2 should work too.

vsapsai updated this revision to Diff 529735.Jun 8 2023, 2:48 PM

Use different check for nullptr + rebase.

Kinda a follow-up to D121176.

One big alternative that I've considered is to store interface name in ObjCCategoryDecl because when we parse @interface InterfaceName(CategoryName) we know the interface name. The intention was to allow

@interface A(X) @end

and

@interface A @end
@interface A(X) @end

as equivalent. But I'm not convinced it is the right call and that's why it doesn't justify the extra effort.

Are there any cases you are aware of where this change would fix a bug? In any case, it sounds like that is something that can be done as a follow-up patch after fixing the crash.

No, I don't know about any such cases. My conclusion is that a category without an interface is invalid code, so trying to do something smart in this situation is a questionable approach. That's why I've decided not to do anything smart for a missing interface.

clang/lib/AST/ASTStructuralEquivalence.cpp
2062

So after checking for the common patterns I've found the following examples

if (!Child1 || !Child2)
  return false;
if (!S1 || !S2)
  return S1 == S2;
if (T1.isNull() || T2.isNull())
  return T1.isNull() && T2.isNull();
if (TemplateDeclN1 && TemplateDeclN2) {
  ...
} else if (TemplateDeclN1 || TemplateDeclN2)
  return false;

So I went for (!Intf1 || !Intf2) check and added (Intf1 != Intf2) because we can check further if both interfaces are nullptr. The boolean expression is more complex than before but it captures my thought process better.

Bigcheese accepted this revision.Jun 8 2023, 4:07 PM
Bigcheese added a subscriber: Bigcheese.

Looks good as long as people are happy with the condition.

This revision is now accepted and ready to land.Jun 8 2023, 4:07 PM
This revision was landed with ongoing or failed builds.Jun 9 2023, 5:10 PM
This revision was automatically updated to reflect the committed changes.

If anybody has further comments about the condition style, I'm happy to change the code post-commit.