这种行为是否符合 clang 标准?

Is this behavior of clang standard compliant?

这将是一个很长的语言法律问题,所以我想快速说明一下为什么我觉得它很重要。我正在从事一个严格遵守标准至关重要的项目(编写一种编译为 C 的语言)。我要给出的示例似乎是 clang 的标准违规,因此,如果是这种情况,我想确认一下。

gcc 表示,带有指向 restrict 限定指针的指针的条件语句不能与带有 void 指针的条件语句共存。另一方面,clang 可以很好地编译这些东西。这是一个示例程序:

#include <stdlib.h>

int main(void){
   int* restrict* A = malloc(8);
   A ? A : malloc(8);
   return 0;
   }

对于 gcc,选项 -std=c11-pedantic 可以包含或不包含在任何组合中,同样对于 clang 以及选项 -std=c11-Weverything。在任何情况下,clang 编译都没有错误,gcc 给出以下内容:

tem-2.c: In function ‘main’:
tem-2.c:7:2: error: invalid use of ‘restrict’
  A ? A : malloc(8);
  ^

c11 标准对条件语句作了如下说明,强调已加:

6.5.15 Conditional operator

...

  1. One of the following shall hold for the second and third operands:

— both operands have arithmetic type;

— both operands have the same structure or union type;

— both operands have void type;

— both operands are pointers to qualified or unqualified versions of compatible types;

— one operand is a pointer and the other is a null pointer constant; or

one operand is a pointer to an object type and the other is a pointer to a qualified or unqualified version of void.

...

  1. If both the second and third operands are pointers or one is a null pointer constant and the other is a pointer, the result type is a pointer to a type qualified with all the type qualifiers of the types referenced by both operands. Furthermore, if both operands are pointers to compatible types or to differently qualified versions of compatible types, the result type is a pointer to an appropriately qualified version of the composite type; if one operand is a null pointer constant, the result has the type of the other operand; otherwise, one operand is a pointer to void or a qualified version of void, in which case the result type is a pointer to an appropriately qualified version of void.

...

在我看来,上面的第一个粗体部分表示这两种类型可以一起使用,第二个粗体部分将结果定义为指向 void 的限制限定版本的指针。但是,如下所述,该类型不能存在,因此表达式被 gcc 正确识别为错误:

6.7.3 Type qualifiers, paragraph 2

Types other than pointer types whose referenced type is an object type shall not be restrict-qualified.

现在,问题是此示例程序违反了 "shall not" 条件,因此需要通过以下方式产生错误:

5.1.1.3 Diagnostics, paragraph 1

A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint, even if the behavior is also explicitly specified as undefined or implementation-defined. Diagnostic messages need not be produced in other circumstances.

通过静默处理错误类型,clang 似乎不符合标准。这让我想知道 clang 还能默默地做什么。

我在 x86-64 Ubuntu 机器上使用 gcc 5.4.0 版和 clang 3.8.0 版。

是的,它看起来像一个错误。

您的问题更简单:void 可以 restrict 合格吗?由于 void 显然不是指针类型,所以答案是否定的。因为这违反了约束,编译器应该给出诊断。

我能够通过使用 _Generic 表达式

来欺骗 clang 忏悔它的罪行
puts(_Generic(A ? A : malloc(8), void* : "void*"));

clang告诉我

static.c:24:18: error: controlling expression type 'restrict void *' not compatible with any generic association type
     puts(_Generic(A ? A : malloc(8), void* : "void*"));

这表明 clang 确实试图匹配一个无意义的类型 restrict void*.

请向他们提交错误报告。

虽然编译器可以通过完全忽略限定符来满足围绕 restrict 的所有义务,但想要跟踪允许或不允许做什么的编译器需要跟踪哪些指针保存副本restrict 个指针。给出类似的东西:

int *foo;
int *bar;
int wow(int *restrict p)
{
  foo = p;
  ...
  *p = 123;
  *foo = 456;
  *p++;
  *bar = 890;
  return *p;
}

由于 foo 派生自 p,编译器必须允许通过 foo 通过 p 对访问进行别名访问。编译器不需要做出这样的允许 对于通过 bar 进行的访问,因为已知 而不是 来保存从 p.

派生的地址

围绕 restrict 的规则在指针可能或 可能不是从另一个派生的。肯定会允许编译器 如果 restrict 限定符无法跟踪所有的情况,则只需忽略它 从指针派生的指针;我不确定是否会有这样的情况 调用 UB,即使没有任何东西修改由 指针。如果句法构造在结构上保证调用 UB,让编译器 squawk 可能比让它在 任意方式(尽管让编译器简单地忽略任何 restrict 它无法完全处理的限定词可能更有用)。