clang 如何检查重定义?
How does clang check redefinitions?
我是 Clang 的新手,正在尝试编写一些 clang-tidy 检查。我想找到一些可以用作 "变量 table" 的东西,以检查某些名称是否格式正确。
我的直觉是这样的:
写重定义代码有时会导致错误,Clang的诊断会抛出错误。喜欢:
int main(){
int x;
int x; // error: redefinition
return 0;
}
从我的角度来看,clang 可能会保留一个动态变量 table 来检查新定义是否为 compatible/overloading/error。
我尝试深入研究 clang 源代码并探索了一些东西:
Identifiertable
,由预处理器保存,预处理器标记所有标识符,但不做语义合法检查。
DeclContext
,好像是给用户使用的接口,语义检查产生的产物
我的问题是:
- Clang 如何进行合法检查?
- 我能得到变量table(如果存在这样的东西)吗?
- 如果我不能得到这些东西,我怎么知道哪些变量可以从某个位置到达?
感谢您的建议!
TLDR;请参阅下面的答案。
讨论
你所有的问题都与 C99-6.2.1-p1 中的一个 C 标准术语标识符有关:
An identifier can denote an object; a function; a tag or a member of a structure, union, or
enumeration; a typedef name; a label name; a macro name; or a macro parameter.
每个标识符都有其自己的 范围,根据 C99-6.2.1-p2,以下范围之一:
For each different entity that an identifier designates, the identifier is visible (i.e., can be
used) only within a region of program text called its scope.
既然你感兴趣的是函数内部的变量(即int x
),那么它应该获得一个块作用域.
在同一个 scope 中,有一个名为 linkage 的进程用于 identifiers,到 C99-6.2.2-p2:
An identifier declared in different scopes or in the same scope more than once can be
made to refer to the same object or function by a process called linkage.
这恰恰是一个约束,即同一个 对象 应该只有一个 标识符,或者用你的话来说,定义合法校验。因此编译以下代码
/* file_1.c */
int a = 123;
/* file_2.c */
int a = 456;
会导致链接错误:
% clang file_*
...
ld: 1 duplicate symbol
clang: error: linker command failed with exit code 1
但是,在你的例子中,标识符在同一个函数体中,更可能是:
/* file.c */
int main(){
int b;
int b=1;
}
这里标识符 b
有一个块作用域,应该没有链接, 根据 C99-6.2.2-p6:
The following identifiers have no linkage: an identifier declared to be anything other than
an object or a function; an identifier declared to be a function parameter; a block scope
identifier for an object declared without the storage-class specifier extern.
没有链接意味着我们不能将上面提到的规则应用到它身上,也就是说,它不应该与链接错误类型有关。
自然认为是重定义错误。但是,虽然它确实是在 C++ 中定义的,但它被称为 One Definition Rule, it is NOT in C.(check this or 以获取更多详细信息)没有确切的定义来处理同一块范围内的那些重复标识符。因此,这是一个 实现定义的 行为。这可能就是为什么使用 clang,编译上述代码 (file.c) 后产生的错误与 gcc 不同的原因,如下图:
(注意 gcc 的术语 'with no linkage')
# ---
# GCC (gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04))
# ---
$ gcc file.c
file.c: In function ‘main’:
file.c:4:6: error: redeclaration of ‘b’ with no linkage
int b=1;
^
file.c:3:6: note: previous declaration of ‘b’ was here
int b;
^
# ---
# CLANG (Apple clang version 13.0.0 (clang-1300.0.29.3))
# ---
% clang file.c
file.c:4:6: error: redefinition of 'b'
int b;
^
file.c:3:6: note: previous definition is here
int b=1;
^
1 error generated.
答案
综上所述,我想可以回答你的问题了:
How clang perform the definition legally checking?
对于全局变量,无论是clang还是gcc都会遵循C标准规则,也就是说,他们处理so-称为 Linkage 的进程称为“重定义错误”。对于局部变量,它是未定义的行为,或者更准确地说,是实现定义的行为。
事实上,他们都认为“重新定义”是一个错误。虽然函数体内的变量名在编译后会消失(你可以在汇编输出中验证这一点),但让它们保持唯一无疑更自然也更有帮助。
Am I able to get the variable table(If there exists such kind of things)?
对 clang 内部知识了解不多,但根据上面引用的标准以及 an analysis of compiling, we can infer that IdentifierTable might not much fit your needs, since it exists in "preprocessing" stage, which is before "linking" stage. To take a look how clang compiler deals with duplicate variables (or more formally, symbols), and how to store them, you might want to check the whole project of lld, or in particular, SymbolTable.
我是 Clang 的新手,正在尝试编写一些 clang-tidy 检查。我想找到一些可以用作 "变量 table" 的东西,以检查某些名称是否格式正确。
我的直觉是这样的:
写重定义代码有时会导致错误,Clang的诊断会抛出错误。喜欢:
int main(){
int x;
int x; // error: redefinition
return 0;
}
从我的角度来看,clang 可能会保留一个动态变量 table 来检查新定义是否为 compatible/overloading/error。
我尝试深入研究 clang 源代码并探索了一些东西:
Identifiertable
,由预处理器保存,预处理器标记所有标识符,但不做语义合法检查。DeclContext
,好像是给用户使用的接口,语义检查产生的产物
我的问题是:
- Clang 如何进行合法检查?
- 我能得到变量table(如果存在这样的东西)吗?
- 如果我不能得到这些东西,我怎么知道哪些变量可以从某个位置到达?
感谢您的建议!
TLDR;请参阅下面的答案。
讨论
你所有的问题都与 C99-6.2.1-p1 中的一个 C 标准术语标识符有关:
An identifier can denote an object; a function; a tag or a member of a structure, union, or enumeration; a typedef name; a label name; a macro name; or a macro parameter.
每个标识符都有其自己的 范围,根据 C99-6.2.1-p2,以下范围之一:
For each different entity that an identifier designates, the identifier is visible (i.e., can be used) only within a region of program text called its scope.
既然你感兴趣的是函数内部的变量(即int x
),那么它应该获得一个块作用域.
在同一个 scope 中,有一个名为 linkage 的进程用于 identifiers,到 C99-6.2.2-p2:
An identifier declared in different scopes or in the same scope more than once can be made to refer to the same object or function by a process called linkage.
这恰恰是一个约束,即同一个 对象 应该只有一个 标识符,或者用你的话来说,定义合法校验。因此编译以下代码
/* file_1.c */
int a = 123;
/* file_2.c */
int a = 456;
会导致链接错误:
% clang file_*
...
ld: 1 duplicate symbol
clang: error: linker command failed with exit code 1
但是,在你的例子中,标识符在同一个函数体中,更可能是:
/* file.c */
int main(){
int b;
int b=1;
}
这里标识符 b
有一个块作用域,应该没有链接, 根据 C99-6.2.2-p6:
The following identifiers have no linkage: an identifier declared to be anything other than an object or a function; an identifier declared to be a function parameter; a block scope identifier for an object declared without the storage-class specifier extern.
没有链接意味着我们不能将上面提到的规则应用到它身上,也就是说,它不应该与链接错误类型有关。
自然认为是重定义错误。但是,虽然它确实是在 C++ 中定义的,但它被称为 One Definition Rule, it is NOT in C.(check this or
(注意 gcc 的术语 'with no linkage')
# ---
# GCC (gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04))
# ---
$ gcc file.c
file.c: In function ‘main’:
file.c:4:6: error: redeclaration of ‘b’ with no linkage
int b=1;
^
file.c:3:6: note: previous declaration of ‘b’ was here
int b;
^
# ---
# CLANG (Apple clang version 13.0.0 (clang-1300.0.29.3))
# ---
% clang file.c
file.c:4:6: error: redefinition of 'b'
int b;
^
file.c:3:6: note: previous definition is here
int b=1;
^
1 error generated.
答案
综上所述,我想可以回答你的问题了:
How clang perform the definition legally checking?
对于全局变量,无论是clang还是gcc都会遵循C标准规则,也就是说,他们处理so-称为 Linkage 的进程称为“重定义错误”。对于局部变量,它是未定义的行为,或者更准确地说,是实现定义的行为。
事实上,他们都认为“重新定义”是一个错误。虽然函数体内的变量名在编译后会消失(你可以在汇编输出中验证这一点),但让它们保持唯一无疑更自然也更有帮助。
Am I able to get the variable table(If there exists such kind of things)?
对 clang 内部知识了解不多,但根据上面引用的标准以及 an analysis of compiling, we can infer that IdentifierTable might not much fit your needs, since it exists in "preprocessing" stage, which is before "linking" stage. To take a look how clang compiler deals with duplicate variables (or more formally, symbols), and how to store them, you might want to check the whole project of lld, or in particular, SymbolTable.