Skip to content

Commit

Permalink
Debug Info: Nest Objective-C property function decls inside their con…
Browse files Browse the repository at this point in the history
…tainer.

This has the nice side-effect of also fixing a crash in Clang.

Starting with DWARF 5 we are emitting ObjC method declarations as
children of their containing entity. This worked for interfaces, but
didn't consider the case of synthessized properties. When a property
of a protocol is synthesized in an interface implementation the
ObjCMethodDecl that was passed to CGF::StartFunction was the property
*declaration* which obviously couldn't have a containing
interface. This patch passes the containing interface all the way
through to CGDebugInfo, so the function declaration can be created
with the correct parent (= the class implementing the protocol).

rdar://problem/53782400

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

(cherry picked from commit 901cc4a)
  • Loading branch information
adrian-prantl committed Nov 11, 2019
1 parent ef6f270 commit ff9842c
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 25 deletions.
67 changes: 42 additions & 25 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Expand Up @@ -2608,8 +2608,8 @@ llvm::DIType *CGDebugInfo::CreateTypeDefinition(const ObjCInterfaceType *Ty,
SourceLocation Loc = PD->getLocation();
llvm::DIFile *PUnit = getOrCreateFile(Loc);
unsigned PLine = getLineNumber(Loc);
ObjCMethodDecl *Getter = PD->getGetterMethodDecl();
ObjCMethodDecl *Setter = PD->getSetterMethodDecl();
ObjCMethodDecl *Getter = PImpD->getGetterMethodDecl();
ObjCMethodDecl *Setter = PImpD->getSetterMethodDecl();
PropertyNode = DBuilder.createObjCProperty(
PD->getName(), PUnit, PLine,
hasDefaultGetterName(PD, Getter)
Expand Down Expand Up @@ -3498,6 +3498,38 @@ llvm::DISubprogram *CGDebugInfo::getFunctionDeclaration(const Decl *D) {
return nullptr;
}

llvm::DISubprogram *CGDebugInfo::getObjCMethodDeclaration(
const Decl *D, llvm::DISubroutineType *FnType, unsigned LineNo,
llvm::DINode::DIFlags Flags, llvm::DISubprogram::DISPFlags SPFlags) {
if (!D || DebugKind <= codegenoptions::DebugLineTablesOnly)
return nullptr;

if (CGM.getCodeGenOpts().DwarfVersion < 5)
return nullptr;

// Starting with DWARF V5 method declarations are emitted as children of
// the interface type.
const auto *OMD = dyn_cast<ObjCMethodDecl>(D);
if (!OMD)
return nullptr;
auto *ID = dyn_cast_or_null<ObjCInterfaceDecl>(D->getDeclContext());
if (!ID)
ID = OMD->getClassInterface();
if (!ID)
return nullptr;
QualType QTy(ID->getTypeForDecl(), 0);
auto It = TypeCache.find(QTy.getAsOpaquePtr());
if (It == TypeCache.end())
return nullptr;
auto *InterfaceType = cast<llvm::DICompositeType>(It->second);
llvm::DISubprogram *FD = DBuilder.createFunction(
InterfaceType, getObjCMethodName(OMD), StringRef(),
InterfaceType->getFile(), LineNo, FnType, LineNo, Flags, SPFlags);
DBuilder.finalizeSubprogram(FD);
ObjCMethodCache[ID].push_back(FD);
return FD;
}

// getOrCreateFunctionType - Construct type. If it is a c++ method, include
// implicit parameter "this".
llvm::DISubroutineType *CGDebugInfo::getOrCreateFunctionType(const Decl *D,
Expand Down Expand Up @@ -3640,16 +3672,21 @@ void CGDebugInfo::EmitFunctionStart(GlobalDecl GD, SourceLocation Loc,

unsigned LineNo = getLineNumber(Loc);
unsigned ScopeLine = getLineNumber(ScopeLoc);
llvm::DISubroutineType *DIFnType = getOrCreateFunctionType(D, FnType, Unit);
llvm::DISubprogram *Decl = nullptr;
if (D)
Decl = isa<ObjCMethodDecl>(D)
? getObjCMethodDeclaration(D, DIFnType, LineNo, Flags, SPFlags)
: getFunctionDeclaration(D);

// FIXME: The function declaration we're constructing here is mostly reusing
// declarations from CXXMethodDecl and not constructing new ones for arbitrary
// FunctionDecls. When/if we fix this we can have FDContext be TheCU/null for
// all subprograms instead of the actual context since subprogram definitions
// are emitted as CU level entities by the backend.
llvm::DISubprogram *SP = DBuilder.createFunction(
FDContext, Name, LinkageName, Unit, LineNo,
getOrCreateFunctionType(D, FnType, Unit), ScopeLine, FlagsForDef,
SPFlagsForDef, TParamsArray.get(), getFunctionDeclaration(D));
FDContext, Name, LinkageName, Unit, LineNo, DIFnType, ScopeLine,
FlagsForDef, SPFlagsForDef, TParamsArray.get(), Decl);
Fn->setSubprogram(SP);
// We might get here with a VarDecl in the case we're generating
// code for the initialization of globals. Do not record these decls
Expand All @@ -3666,26 +3703,6 @@ void CGDebugInfo::EmitFunctionStart(GlobalDecl GD, SourceLocation Loc,
if (FD->hasBody() && !FD->param_empty())
SPDefCache[FD].reset(SP);

if (CGM.getCodeGenOpts().DwarfVersion >= 5) {
// Starting with DWARF V5 method declarations are emitted as children of
// the interface type.
if (const auto *OMD = dyn_cast_or_null<ObjCMethodDecl>(D)) {
const ObjCInterfaceDecl *ID = OMD->getClassInterface();
QualType QTy(ID->getTypeForDecl(), 0);
auto It = TypeCache.find(QTy.getAsOpaquePtr());
if (It != TypeCache.end()) {
llvm::DICompositeType *InterfaceDecl =
cast<llvm::DICompositeType>(It->second);
llvm::DISubprogram *FD = DBuilder.createFunction(
InterfaceDecl, Name, LinkageName, Unit, LineNo,
getOrCreateFunctionType(D, FnType, Unit), ScopeLine, Flags, SPFlags,
TParamsArray.get());
DBuilder.finalizeSubprogram(FD);
ObjCMethodCache[ID].push_back(FD);
}
}
}

// Push the function onto the lexical block stack.
LexicalBlockStack.emplace_back(SP);

Expand Down
11 changes: 11 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.h
Expand Up @@ -613,6 +613,17 @@ class CGDebugInfo {
/// declaration for the given method definition.
llvm::DISubprogram *getFunctionDeclaration(const Decl *D);

/// \return debug info descriptor to the describe method declaration
/// for the given method definition.
/// \param FnType For Objective-C methods, their type.
/// \param LineNo The declaration's line number.
/// \param Flags The DIFlags for the method declaration.
/// \param SPFlags The subprogram-spcific flags for the method declaration.
llvm::DISubprogram *
getObjCMethodDeclaration(const Decl *D, llvm::DISubroutineType *FnType,
unsigned LineNo, llvm::DINode::DIFlags Flags,
llvm::DISubprogram::DISPFlags SPFlags);

/// \return debug info descriptor to describe in-class static data
/// member declaration for the given out-of-class definition. If D
/// is an out-of-class definition of a static data member of a
Expand Down
29 changes: 29 additions & 0 deletions clang/test/CodeGenObjC/debug-info-objc-property-dwarf5.m
@@ -0,0 +1,29 @@
// RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -dwarf-version=5 %s -o - | FileCheck %s

@protocol NSObject
@end

@interface NSObject <NSObject> {}
@end

struct Bar {};

@protocol BarProto
@property struct Bar *bar;
@end

@interface Foo <BarProto>
@end

@implementation Foo {}
@synthesize bar = _bar;
- (void)f {}
@end

// CHECK: ![[FOO:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Foo"

// CHECK: ![[DECL:[0-9]+]] = !DISubprogram(name: "-[Foo setBar:]",
// CHECK-SAME: scope: ![[FOO]]

// CHECK: distinct !DISubprogram(name: "-[Foo setBar:]",
// CHECK-SAME: declaration: ![[DECL:[0-9]+]]

0 comments on commit ff9842c

Please sign in to comment.