-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang][AST] fix ast-print of extern <lang> with >=2 declarators, fixed, fixed #98795
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-hlsl @llvm/pr-subscribers-lldb Author: None (temyurchenko) ChangesRelanding of #93913. Now, it also fixes incorrect declaration context in function parameters creation. Full diff: https://github.com/llvm/llvm-project/pull/98795.diff 7 Files Affected:
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 1f19dadafa44e..35973d99f906d 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -576,13 +576,16 @@ template <typename T> static bool isFirstInExternCContext(T *D) {
return First->isInExternCContext();
}
-static bool isSingleLineLanguageLinkage(const Decl &D) {
- if (const auto *SD = dyn_cast<LinkageSpecDecl>(D.getDeclContext()))
- if (!SD->hasBraces())
- return true;
+static bool isUnbracedLanguageLinkage(const DeclContext *DC) {
+ if (const auto *SD = dyn_cast_if_present<LinkageSpecDecl>(DC))
+ return !SD->hasBraces();
return false;
}
+static bool hasUnbracedLanguageLinkage(const Decl &D) {
+ return isUnbracedLanguageLinkage(D.getDeclContext());
+}
+
static bool isDeclaredInModuleInterfaceOrPartition(const NamedDecl *D) {
if (auto *M = D->getOwningModule())
return M->isInterfaceOrPartition();
@@ -644,7 +647,7 @@ LinkageComputer::getLVForNamespaceScopeDecl(const NamedDecl *D,
if (Var->getStorageClass() != SC_Extern &&
Var->getStorageClass() != SC_PrivateExtern &&
- !isSingleLineLanguageLinkage(*Var))
+ !hasUnbracedLanguageLinkage(*Var))
return LinkageInfo::internal();
}
@@ -2133,6 +2136,12 @@ VarDecl::VarDecl(Kind DK, ASTContext &C, DeclContext *DC,
"ParmVarDeclBitfields too large!");
static_assert(sizeof(NonParmVarDeclBitfields) <= sizeof(unsigned),
"NonParmVarDeclBitfields too large!");
+
+ // The unbraced `extern "C"` invariant is that the storage class
+ // specifier is omitted in the source code, i.e. SC_None (but is,
+ // implicitly, `extern`).
+ assert(!isUnbracedLanguageLinkage(DC) || SC == SC_None);
+
AllBits = 0;
VarDeclBits.SClass = SC;
// Everything else is implicitly initialized to false.
@@ -2316,7 +2325,7 @@ VarDecl::isThisDeclarationADefinition(ASTContext &C) const {
// A declaration directly contained in a linkage-specification is treated
// as if it contains the extern specifier for the purpose of determining
// the linkage of the declared name and whether it is a definition.
- if (isSingleLineLanguageLinkage(*this))
+ if (hasUnbracedLanguageLinkage(*this))
return DeclarationOnly;
// C99 6.9.2p2:
@@ -3041,6 +3050,12 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
DeclContext(DK), redeclarable_base(C), Body(), ODRHash(0),
EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) {
assert(T.isNull() || T->isFunctionType());
+
+ // The unbraced `extern "C"` invariant is that the storage class
+ // specifier is omitted in the source code, i.e. SC_None (but is,
+ // implicitly, `extern`).
+ assert(!isUnbracedLanguageLinkage(DC) || S == SC_None);
+
FunctionDeclBits.SClass = S;
FunctionDeclBits.IsInline = isInlineSpecified;
FunctionDeclBits.IsInlineSpecified = isInlineSpecified;
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 0cf4e64f83b8d..9250a7f6eceb2 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -633,7 +633,7 @@ static void printExplicitSpecifier(ExplicitSpecifier ES, llvm::raw_ostream &Out,
Out << Proto;
}
-static void MaybePrintTagKeywordIfSupressingScopes(PrintingPolicy &Policy,
+static void maybePrintTagKeywordIfSupressingScopes(PrintingPolicy &Policy,
QualType T,
llvm::raw_ostream &Out) {
StringRef prefix = T->isClassType() ? "class "
@@ -643,6 +643,22 @@ static void MaybePrintTagKeywordIfSupressingScopes(PrintingPolicy &Policy,
Out << prefix;
}
+/// Return the language of the linkage spec of `D`, if applicable.
+///
+/// \Return - "C" if `D` has been declared with unbraced `extern "C"`
+/// - "C++" if `D` has been declared with unbraced `extern "C++"`
+/// - nullptr in any other case
+static const char *tryGetUnbracedLinkageLanguage(const Decl *D) {
+ const auto *SD = dyn_cast<LinkageSpecDecl>(D->getDeclContext());
+ if (!SD || SD->hasBraces())
+ return nullptr;
+ if (SD->getLanguage() == LinkageSpecLanguageIDs::C)
+ return "C";
+ assert(SD->getLanguage() == LinkageSpecLanguageIDs::CXX &&
+ "unknown language in linkage specification");
+ return "C++";
+}
+
void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
if (!D->getDescribedFunctionTemplate() &&
!D->isFunctionTemplateSpecialization()) {
@@ -662,6 +678,11 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
CXXConversionDecl *ConversionDecl = dyn_cast<CXXConversionDecl>(D);
CXXDeductionGuideDecl *GuideDecl = dyn_cast<CXXDeductionGuideDecl>(D);
if (!Policy.SuppressSpecifiers) {
+ if (const char *Lang = tryGetUnbracedLinkageLanguage(D)) {
+ // the "extern" specifier is implicit
+ assert(D->getStorageClass() == SC_None);
+ Out << "extern \"" << Lang << "\" ";
+ }
switch (D->getStorageClass()) {
case SC_None: break;
case SC_Extern: Out << "extern "; break;
@@ -807,7 +828,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
}
if (!Policy.SuppressTagKeyword && Policy.SuppressScope &&
!Policy.SuppressUnwrittenScope)
- MaybePrintTagKeywordIfSupressingScopes(Policy, AFT->getReturnType(),
+ maybePrintTagKeywordIfSupressingScopes(Policy, AFT->getReturnType(),
Out);
AFT->getReturnType().print(Out, Policy, Proto);
Proto.clear();
@@ -932,6 +953,11 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) {
: D->getASTContext().getUnqualifiedObjCPointerType(D->getType());
if (!Policy.SuppressSpecifiers) {
+ if (const char *Lang = tryGetUnbracedLinkageLanguage(D)) {
+ // the "extern" specifier is implicit
+ assert(D->getStorageClass() == SC_None);
+ Out << "extern \"" << Lang << "\" ";
+ }
StorageClass SC = D->getStorageClass();
if (SC != SC_None)
Out << VarDecl::getStorageClassSpecifierString(SC) << " ";
@@ -961,7 +987,7 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) {
if (!Policy.SuppressTagKeyword && Policy.SuppressScope &&
!Policy.SuppressUnwrittenScope)
- MaybePrintTagKeywordIfSupressingScopes(Policy, T, Out);
+ maybePrintTagKeywordIfSupressingScopes(Policy, T, Out);
printDeclType(T, (isa<ParmVarDecl>(D) && Policy.CleanUglifiedParameters &&
D->getIdentifier())
@@ -1064,6 +1090,8 @@ void DeclPrinter::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) {
void DeclPrinter::VisitEmptyDecl(EmptyDecl *D) {
prettyPrintAttributes(D);
+ if (const char *Lang = tryGetUnbracedLinkageLanguage(D))
+ Out << "extern \"" << Lang << "\";";
}
void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) {
@@ -1136,22 +1164,21 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) {
}
void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) {
- const char *l;
+ if (!D->hasBraces()) {
+ VisitDeclContext(D);
+ return;
+ }
+ const char *L;
if (D->getLanguage() == LinkageSpecLanguageIDs::C)
- l = "C";
+ L = "C";
else {
assert(D->getLanguage() == LinkageSpecLanguageIDs::CXX &&
"unknown language in linkage specification");
- l = "C++";
+ L = "C++";
}
-
- Out << "extern \"" << l << "\" ";
- if (D->hasBraces()) {
- Out << "{\n";
- VisitDeclContext(D);
- Indent() << "}";
- } else
- Visit(*D->decls_begin());
+ Out << "extern \"" << L << "\" {\n";
+ VisitDeclContext(D);
+ Indent() << "}";
}
void DeclPrinter::printTemplateParameters(const TemplateParameterList *Params,
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 4b9b735f1cfb4..4e2f02ba7007a 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2380,7 +2380,7 @@ FunctionDecl *Sema::CreateBuiltin(IdentifierInfo *II, QualType Type,
}
FunctionDecl *New = FunctionDecl::Create(Context, Parent, Loc, Loc, II, Type,
- /*TInfo=*/nullptr, SC_Extern,
+ /*TInfo=*/nullptr, SC_None,
getCurFPFeatures().isFPConstrained(),
false, Type->isFunctionProtoType());
New->setImplicit();
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 76145f291887c..60075120fe030 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6286,7 +6286,7 @@ static FunctionDecl *rewriteBuiltinFunctionDecl(Sema *Sema, ASTContext &Context,
FunctionDecl *OverloadDecl = FunctionDecl::Create(
Context, Parent, FDecl->getLocation(), FDecl->getLocation(),
FDecl->getIdentifier(), OverloadTy,
- /*TInfo=*/nullptr, SC_Extern, Sema->getCurFPFeatures().isFPConstrained(),
+ /*TInfo=*/nullptr, SC_None, Sema->getCurFPFeatures().isFPConstrained(),
false,
/*hasPrototype=*/true);
SmallVector<ParmVarDecl*, 16> Params;
diff --git a/clang/test/AST/ast-print-language-linkage.cpp b/clang/test/AST/ast-print-language-linkage.cpp
new file mode 100644
index 0000000000000..7e4dc3f25f062
--- /dev/null
+++ b/clang/test/AST/ast-print-language-linkage.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -ast-print %s -o - | FileCheck %s
+
+// CHECK: extern "C" int printf(const char *, ...);
+extern "C" int printf(const char *...);
+
+// CHECK: extern "C++" int f(int);
+// CHECK-NEXT: extern "C++" int g(int);
+extern "C++" int f(int), g(int);
+
+// CHECK: extern "C" char a;
+// CHECK-NEXT: extern "C" char b;
+extern "C" char a, b;
+
+// CHECK: extern "C" {
+// CHECK-NEXT: void foo();
+// CHECK-NEXT: int x;
+// CHECK-NEXT: int y;
+// CHECK-NEXT: extern short z;
+// CHECK-NEXT: }
+extern "C" {
+ void foo(void);
+ int x, y;
+ extern short z;
+}
+
+// CHECK: extern "C" {
+// CHECK-NEXT: }
+extern "C" {}
+
+// CHECK: extern "C++";
+extern "C++";
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
index 1dd98567f8d69..12aa1bd60bcb7 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
@@ -881,7 +881,7 @@ class CodeComplete : public CodeCompleteConsumer {
else
ToInsert += "(";
raw_string_ostream OS(Description);
- F->print(OS, m_desc_policy, false);
+ F->print(OS, m_desc_policy);
OS.flush();
} else if (const VarDecl *V = dyn_cast<VarDecl>(D)) {
Description = V->getType().getAsString(m_desc_policy);
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp b/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp
index da59855a9f162..da02e4c72cd19 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp
@@ -77,7 +77,8 @@ clang::NamedDecl *NameSearchContext::AddFunDecl(const CompilerType &type,
clang::FunctionDecl *func_decl = FunctionDecl::Create(
ast, context, SourceLocation(), SourceLocation(), decl_name, qual_type,
- nullptr, SC_Extern, /*UsesFPIntrin=*/false, isInlineSpecified, hasWrittenPrototype,
+ nullptr, SC_None, /*UsesFPIntrin=*/false, isInlineSpecified,
+ hasWrittenPrototype,
isConstexprSpecified ? ConstexprSpecKind::Constexpr
: ConstexprSpecKind::Unspecified);
@@ -97,10 +98,9 @@ clang::NamedDecl *NameSearchContext::AddFunDecl(const CompilerType &type,
for (ArgIndex = 0; ArgIndex < NumArgs; ++ArgIndex) {
QualType arg_qual_type(func_proto_type->getParamType(ArgIndex));
- parm_var_decls.push_back(
- ParmVarDecl::Create(ast, const_cast<DeclContext *>(context),
- SourceLocation(), SourceLocation(), nullptr,
- arg_qual_type, nullptr, SC_Static, nullptr));
+ parm_var_decls.push_back(ParmVarDecl::Create(
+ ast, func_decl, SourceLocation(), SourceLocation(), nullptr,
+ arg_qual_type, nullptr, SC_Static, nullptr));
}
func_decl->setParams(ArrayRef<ParmVarDecl *>(parm_var_decls));
|
@AaronBallman could you please review? I've fixed at least the LLDB tests. There was an actual bug in function parameters construction that was revealed by the invariant check. However, I can't run the whole LLDB suite. Even among the reported failing tests, some are «unsupported» on my machine. Even without my changes, a lot of LLDB tests are unsupported or failing, I guess some were killed by oom-killer. Regarding the orc-rt test failures: I tried building one of the failing objects and I can't reproduce the failure. Perhaps, because the tests are run on Windows and I'm running on Linux? It would be very helpful if @Prabhuk could run the failing command on a debug build. |
CC @lhames |
This is a frontend crash, rather than a JIT one, and I don't have access to a Windows dev machine so I'm not well placed to look into this. |
A bit lost as to what I should do now, @AaronBallman. |
Ping @JDevlieghere for lldb test suite assistance. As for ORC JIT, the compiler-rt project does not claim to support Windows to begin with, so it's unclear whether the bot should not be testing on Windows or whether compiler-rt needs to update their support claims. CC @vitalybuka for more opinions/help on this one (my preference is for Windows to be a first-class citizen with compiler-rt, but if nobody is willing to do the maintenance work, we should not let bots failing when testing compiler-rt on Windows be a blocking concern). |
I don't have the full context but I can definitely run the lldb test suite for you. I'm building now. (BTW, I see there are some merge conflicts on the PR.) |
I have the results now. The patch is based on a fairly old revision, so it didn't work for me without a python 3.12 fix (22ea97d). After patching that it, I got a clean check-lldb run (on x86_64-linux). |
Thank you so much! Glad to hear that. The PR hasn't been updated for a while, but I will fix the conflicts now |
…lvm#93131) Problem: the printer used to ignore all but the first declarator for unbraced language linkage declarators. Furthemore, that one would be printed without the final semicolon. Solution: for unbraced case we traverse all declarators via `VisitDeclContext`. Furthermore, in appropriate visitors we query for whether they are a part of the unbraced extern language linkage spec, and if so, print appropriately.
Quoting 9.11.8: > A declaration directly contained in a linkage-specification is > treated as if it contains the extern specifier (9.2.2) for the > purpose of determining the linkage of the declared name and whether > it is a definition. Such a declaration shall not specify a storage > class. So, the invariant is that function and variable declarations within an unbraced language linkage specification have `StorageClass` set to `SC_None`. Problem: in several places the invariant was broken. Solution: remove the explicit `SC_Extern` specification. Note, that my changes result in the `extern` being implicit in some functions that are not contained in a language linkage specification.
It needs to be the function declaration that the parameter belongs to, not the context of that function.
Pinging @vitalybuka. |
Relanding of #93913. Now, it also fixes incorrect declaration context in function parameters creation.