Skip to content

Commit

Permalink
[Modules] Implement ODR-like semantics for tag types in C/ObjC
Browse files Browse the repository at this point in the history
Allow ODR for ObjC/C in the sense that we won't keep more that
one definition around (merge them). However, ensure the decl
pass the structural compatibility check in C11 6.2.7/1, for that,
reuse the structural equivalence checks used by the ASTImporter.

Few other considerations:
- Create error diagnostics for tag types mismatches and thread
them into the structural equivalence checks.
- Note that by doing this we only support redefinition between types
that are considered "compatible types" by C.

This is mixed approach of the suggestions discussed in
http://lists.llvm.org/pipermail/cfe-dev/2017-March/053257.html

Differential Revision: https://reviews.llvm.org/D31778

rdar://problem/31909368

llvm-svn: 306918
  • Loading branch information
bcardosolopes committed Jul 1, 2017
1 parent 057c82c commit df0ee34
Show file tree
Hide file tree
Showing 15 changed files with 284 additions and 53 deletions.
6 changes: 4 additions & 2 deletions clang/include/clang/AST/ASTStructuralEquivalence.h
Expand Up @@ -62,9 +62,11 @@ struct StructuralEquivalenceContext {
StructuralEquivalenceContext(
ASTContext &FromCtx, ASTContext &ToCtx,
llvm::DenseSet<std::pair<Decl *, Decl *>> &NonEquivalentDecls,
bool StrictTypeSpelling = false, bool Complain = true)
bool StrictTypeSpelling = false, bool Complain = true,
bool ErrorOnTagTypeMismatch = false)
: FromCtx(FromCtx), ToCtx(ToCtx), NonEquivalentDecls(NonEquivalentDecls),
StrictTypeSpelling(StrictTypeSpelling), Complain(Complain),
StrictTypeSpelling(StrictTypeSpelling),
ErrorOnTagTypeMismatch(ErrorOnTagTypeMismatch), Complain(Complain),
LastDiagFromC2(false) {}

DiagnosticBuilder Diag1(SourceLocation Loc, unsigned DiagID);
Expand Down
11 changes: 8 additions & 3 deletions clang/include/clang/Basic/DiagnosticASTKinds.td
Expand Up @@ -200,12 +200,17 @@ def note_odr_defined_here : Note<"also defined here">;
def err_odr_function_type_inconsistent : Error<
"external function %0 declared with incompatible types in different "
"translation units (%1 vs. %2)">;
def warn_odr_tag_type_inconsistent : Warning<
"type %0 has incompatible definitions in different translation units">,
InGroup<DiagGroup<"odr">>;
def warn_odr_tag_type_inconsistent
: Warning<"type %0 has incompatible definitions in different translation "
"units">,
InGroup<DiagGroup<"odr">>;
def err_odr_tag_type_inconsistent
: Error<"type %0 has incompatible definitions in different translation "
"units">;
def note_odr_tag_kind_here: Note<
"%0 is a %select{struct|interface|union|class|enum}1 here">;
def note_odr_field : Note<"field %0 has type %1 here">;
def note_odr_field_name : Note<"field has name %0 here">;
def note_odr_missing_field : Note<"no corresponding field here">;
def note_odr_bit_field : Note<"bit-field %0 with type %1 and length %2 here">;
def note_odr_not_bit_field : Note<"field %0 is not a bit-field">;
Expand Down
32 changes: 22 additions & 10 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -1542,6 +1542,10 @@ class Sema {

bool hasVisibleMergedDefinition(NamedDecl *Def);

/// Determine if \p D and \p Suggested have a structurally compatible
/// layout as described in C11 6.2.7/1.
bool hasStructuralCompatLayout(Decl *D, Decl *Suggested);

/// Determine if \p D has a visible definition. If not, suggest a declaration
/// that should be made visible to expose the definition.
bool hasVisibleDefinition(NamedDecl *D, NamedDecl **Suggested,
Expand Down Expand Up @@ -1629,9 +1633,13 @@ class Sema {
//

struct SkipBodyInfo {
SkipBodyInfo() : ShouldSkip(false), Previous(nullptr) {}
SkipBodyInfo()
: ShouldSkip(false), CheckSameAsPrevious(false), Previous(nullptr),
New(nullptr) {}
bool ShouldSkip;
bool CheckSameAsPrevious;
NamedDecl *Previous;
NamedDecl *New;
};

DeclGroupPtrTy ConvertDeclToDeclGroup(Decl *Ptr, Decl *OwnedType = nullptr);
Expand Down Expand Up @@ -2145,13 +2153,11 @@ class Sema {
};

Decl *ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
SourceLocation KWLoc, CXXScopeSpec &SS,
IdentifierInfo *Name, SourceLocation NameLoc,
AttributeList *Attr, AccessSpecifier AS,
SourceLocation ModulePrivateLoc,
MultiTemplateParamsArg TemplateParameterLists,
bool &OwnedDecl, bool &IsDependent,
SourceLocation ScopedEnumKWLoc,
SourceLocation KWLoc, CXXScopeSpec &SS, IdentifierInfo *Name,
SourceLocation NameLoc, AttributeList *Attr,
AccessSpecifier AS, SourceLocation ModulePrivateLoc,
MultiTemplateParamsArg TemplateParameterLists, bool &OwnedDecl,
bool &IsDependent, SourceLocation ScopedEnumKWLoc,
bool ScopedEnumUsesClassTag, TypeResult UnderlyingType,
bool IsTypeSpecifier, bool IsTemplateParamOrArg,
SkipBodyInfo *SkipBody = nullptr);
Expand Down Expand Up @@ -2219,6 +2225,12 @@ class Sema {
/// struct, or union).
void ActOnTagStartDefinition(Scope *S, Decl *TagDecl);

/// Perform ODR-like check for C/ObjC when merging tag types from modules.
/// Differently from C++, actually parse the body and reject / error out
/// in case of a structural mismatch.
bool ActOnDuplicateDefinition(DeclSpec &DS, Decl *Prev,
SkipBodyInfo &SkipBody);

typedef void *SkippedDefinitionContext;

/// \brief Invoked when we enter a tag definition that we're skipping.
Expand Down Expand Up @@ -2272,8 +2284,8 @@ class Sema {

Decl *ActOnEnumConstant(Scope *S, Decl *EnumDecl, Decl *LastEnumConstant,
SourceLocation IdLoc, IdentifierInfo *Id,
AttributeList *Attrs,
SourceLocation EqualLoc, Expr *Val);
AttributeList *Attrs, SourceLocation EqualLoc,
Expr *Val);
void ActOnEnumBody(SourceLocation EnumLoc, SourceRange BraceRange,
Decl *EnumDecl,
ArrayRef<Decl *> Elements,
Expand Down
58 changes: 48 additions & 10 deletions clang/lib/AST/ASTStructuralEquivalence.cpp
Expand Up @@ -735,13 +735,28 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
// Check for equivalent field names.
IdentifierInfo *Name1 = Field1->getIdentifier();
IdentifierInfo *Name2 = Field2->getIdentifier();
if (!::IsStructurallyEquivalent(Name1, Name2))
if (!::IsStructurallyEquivalent(Name1, Name2)) {
if (Context.Complain) {
Context.Diag2(Owner2->getLocation(),
Context.ErrorOnTagTypeMismatch
? diag::err_odr_tag_type_inconsistent
: diag::warn_odr_tag_type_inconsistent)
<< Context.ToCtx.getTypeDeclType(Owner2);
Context.Diag2(Field2->getLocation(), diag::note_odr_field_name)
<< Field2->getDeclName();
Context.Diag1(Field1->getLocation(), diag::note_odr_field_name)
<< Field1->getDeclName();
}
return false;
}

if (!IsStructurallyEquivalent(Context, Field1->getType(),
Field2->getType())) {
if (Context.Complain) {
Context.Diag2(Owner2->getLocation(), diag::warn_odr_tag_type_inconsistent)
Context.Diag2(Owner2->getLocation(),
Context.ErrorOnTagTypeMismatch
? diag::err_odr_tag_type_inconsistent
: diag::warn_odr_tag_type_inconsistent)
<< Context.ToCtx.getTypeDeclType(Owner2);
Context.Diag2(Field2->getLocation(), diag::note_odr_field)
<< Field2->getDeclName() << Field2->getType();
Expand All @@ -753,7 +768,10 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,

if (Field1->isBitField() != Field2->isBitField()) {
if (Context.Complain) {
Context.Diag2(Owner2->getLocation(), diag::warn_odr_tag_type_inconsistent)
Context.Diag2(Owner2->getLocation(),
Context.ErrorOnTagTypeMismatch
? diag::err_odr_tag_type_inconsistent
: diag::warn_odr_tag_type_inconsistent)
<< Context.ToCtx.getTypeDeclType(Owner2);
if (Field1->isBitField()) {
Context.Diag1(Field1->getLocation(), diag::note_odr_bit_field)
Expand All @@ -780,7 +798,9 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
if (Bits1 != Bits2) {
if (Context.Complain) {
Context.Diag2(Owner2->getLocation(),
diag::warn_odr_tag_type_inconsistent)
Context.ErrorOnTagTypeMismatch
? diag::err_odr_tag_type_inconsistent
: diag::warn_odr_tag_type_inconsistent)
<< Context.ToCtx.getTypeDeclType(Owner2);
Context.Diag2(Field2->getLocation(), diag::note_odr_bit_field)
<< Field2->getDeclName() << Field2->getType() << Bits2;
Expand All @@ -799,7 +819,10 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
RecordDecl *D1, RecordDecl *D2) {
if (D1->isUnion() != D2->isUnion()) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent)
Context.Diag2(D2->getLocation(),
Context.ErrorOnTagTypeMismatch
? diag::err_odr_tag_type_inconsistent
: diag::warn_odr_tag_type_inconsistent)
<< Context.ToCtx.getTypeDeclType(D2);
Context.Diag1(D1->getLocation(), diag::note_odr_tag_kind_here)
<< D1->getDeclName() << (unsigned)D1->getTagKind();
Expand Down Expand Up @@ -927,7 +950,10 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
Field1 != Field1End; ++Field1, ++Field2) {
if (Field2 == Field2End) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent)
Context.Diag2(D2->getLocation(),
Context.ErrorOnTagTypeMismatch
? diag::err_odr_tag_type_inconsistent
: diag::warn_odr_tag_type_inconsistent)
<< Context.ToCtx.getTypeDeclType(D2);
Context.Diag1(Field1->getLocation(), diag::note_odr_field)
<< Field1->getDeclName() << Field1->getType();
Expand All @@ -942,7 +968,10 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,

if (Field2 != Field2End) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent)
Context.Diag2(D2->getLocation(),
Context.ErrorOnTagTypeMismatch
? diag::err_odr_tag_type_inconsistent
: diag::warn_odr_tag_type_inconsistent)
<< Context.ToCtx.getTypeDeclType(D2);
Context.Diag2(Field2->getLocation(), diag::note_odr_field)
<< Field2->getDeclName() << Field2->getType();
Expand All @@ -964,7 +993,10 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
EC1 != EC1End; ++EC1, ++EC2) {
if (EC2 == EC2End) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent)
Context.Diag2(D2->getLocation(),
Context.ErrorOnTagTypeMismatch
? diag::err_odr_tag_type_inconsistent
: diag::warn_odr_tag_type_inconsistent)
<< Context.ToCtx.getTypeDeclType(D2);
Context.Diag1(EC1->getLocation(), diag::note_odr_enumerator)
<< EC1->getDeclName() << EC1->getInitVal().toString(10);
Expand All @@ -978,7 +1010,10 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
if (!llvm::APSInt::isSameValue(Val1, Val2) ||
!IsStructurallyEquivalent(EC1->getIdentifier(), EC2->getIdentifier())) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent)
Context.Diag2(D2->getLocation(),
Context.ErrorOnTagTypeMismatch
? diag::err_odr_tag_type_inconsistent
: diag::warn_odr_tag_type_inconsistent)
<< Context.ToCtx.getTypeDeclType(D2);
Context.Diag2(EC2->getLocation(), diag::note_odr_enumerator)
<< EC2->getDeclName() << EC2->getInitVal().toString(10);
Expand All @@ -991,7 +1026,10 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,

if (EC2 != EC2End) {
if (Context.Complain) {
Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent)
Context.Diag2(D2->getLocation(),
Context.ErrorOnTagTypeMismatch
? diag::err_odr_tag_type_inconsistent
: diag::warn_odr_tag_type_inconsistent)
<< Context.ToCtx.getTypeDeclType(D2);
Context.Diag2(EC2->getLocation(), diag::note_odr_enumerator)
<< EC2->getDeclName() << EC2->getInitVal().toString(10);
Expand Down
19 changes: 12 additions & 7 deletions clang/lib/Parse/ParseDecl.cpp
Expand Up @@ -4319,8 +4319,15 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
return;
}

if (Tok.is(tok::l_brace) && TUK != Sema::TUK_Reference)
ParseEnumBody(StartLoc, TagDecl);
if (Tok.is(tok::l_brace) && TUK != Sema::TUK_Reference) {
Decl *D = SkipBody.CheckSameAsPrevious ? SkipBody.New : TagDecl;
ParseEnumBody(StartLoc, D);
if (SkipBody.CheckSameAsPrevious &&
!Actions.ActOnDuplicateDefinition(DS, TagDecl, SkipBody)) {
DS.SetTypeSpecError();
return;
}
}

if (DS.SetTypeSpecType(DeclSpec::TST_enum, StartLoc,
NameLoc.isValid() ? NameLoc : StartLoc,
Expand Down Expand Up @@ -4392,11 +4399,9 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl) {
}

// Install the enumerator constant into EnumDecl.
Decl *EnumConstDecl = Actions.ActOnEnumConstant(getCurScope(), EnumDecl,
LastEnumConstDecl,
IdentLoc, Ident,
attrs.getList(), EqualLoc,
AssignedVal.get());
Decl *EnumConstDecl = Actions.ActOnEnumConstant(
getCurScope(), EnumDecl, LastEnumConstDecl, IdentLoc, Ident,
attrs.getList(), EqualLoc, AssignedVal.get());
EnumAvailabilityDiags.back().done();

EnumConstantDecls.push_back(EnumConstDecl);
Expand Down
14 changes: 12 additions & 2 deletions clang/lib/Parse/ParseDeclCXX.cpp
Expand Up @@ -1910,8 +1910,18 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
else if (getLangOpts().CPlusPlus)
ParseCXXMemberSpecification(StartLoc, AttrFixitLoc, attrs, TagType,
TagOrTempResult.get());
else
ParseStructUnionBody(StartLoc, TagType, TagOrTempResult.get());
else {
Decl *D =
SkipBody.CheckSameAsPrevious ? SkipBody.New : TagOrTempResult.get();
// Parse the definition body.
ParseStructUnionBody(StartLoc, TagType, D);
if (SkipBody.CheckSameAsPrevious &&
!Actions.ActOnDuplicateDefinition(DS, TagOrTempResult.get(),
SkipBody)) {
DS.SetTypeSpecError();
return;
}
}
}

if (!TagOrTempResult.isInvalid())
Expand Down

0 comments on commit df0ee34

Please sign in to comment.