【问题标题】:What is the rationale for "semantics violation does not require diagnostics"?“语义违规不需要诊断”的理由是什么?
【发布时间】:2021-12-08 03:41:00
【问题描述】:

后续问题:If "shall / shall not" requirement is violated, then does it matter in which section (e.g. Semantics, Constraints) such requirement is located?

ISO/IEC 9899:202x (E) 工作草案 - 2020 年 12 月 11 日 N2596,5.1.1.3 诊断,1:

符合要求的实现应产生至少一个诊断消息(在 实现定义的方式)如果预处理翻译单元或翻译单元包含 违反任何语法规则或约束,即使行为也明确指定为未定义或实现定义。在其他情况下不需要生成诊断消息。

后果:语义违规不需要诊断。

问题:“语义违规不需要诊断”的(可能)基本原理是什么?

【问题讨论】:

  • 如果您尝试制定“语义违规”是什么,您会意识到它在很大程度上取决于 a) 实际输入和 b) 程序员的意图。编译器也无法确定。
  • @DevSolar 是否意味着在 ISO/IEC 9899:202x 中,在 6.9.2 外部对象定义中“如果对象标识符的声明是暂定定义并且具有内部链接,则声明的类型不应是不完整的类型”放在错误的部分:语义而不是约束?
  • 我会在你之前的问题中回答,因为我觉得这是一个错误答案的后续问题(XY问题)。
  • 完成其他答案。您可能会注意到,这里的所有答案(以及我最初的评论)都将“语义”误认为是完全不同的东西——程序的意图。

标签: c language-lawyer diagnostics standards-compliance c17


【解决方案1】:

Rice's theorem 给出了一个可能的理由:程序的非平凡语义属性是不可判定的

例如,除以零是违反语义的;而且您无法仅通过对 C 源代码的静态分析来决定它不会发生...

标准不能要求对此类未定义行为进行全面检测,即使某些工具(例如 Frama-C有时能够检测到它们。

另见halting problem。你不应该指望 C 编译器来解决它!

【讨论】:

    【解决方案2】:

    C99 基本原理 v5.10 给出了这样的解释:

    5.1.1.3 诊断

    通过对任何包含语法错误或 违反约束,标准执行两项重要服务。首先,它给牙齿 错误程序的概念,因为符合标准的实现必须将这样的程序与有效程序区分开来。其次,它严格限制了允许的扩展的性质 符合规范的实现。

    标准没有说明诊断消息的性质,这可能只是 “语法错误”,没有提示错误发生的位置。 (当然,实现必须 描述什么翻译器输出构成诊断消息,以便用户可以识别它。)C89 委员会最终决定,任何超出此级别的诊断活动都是 实施质量问题,市场力量将鼓励更有用的 诊断。尽管如此,C89 委员会认为至少有一些重大的错误 必须被诊断,并且指定的类应该被所有翻译者识别。

    【讨论】:

    • 是的,我在询问之前已经阅读过。基于此文本,我看不出“语义违规不需要诊断”的原因是什么?你能解释/详细说明吗?
    • @pmor 委员会不想向实施者提供详细的指示,如理由中所述,而是将其留给“市场力量”。与这些 ISO 标准一样,他们努力不给市场上任何现有产品带来不公平的优势。
    【解决方案3】:

    发生这种情况是因为 C 语言的语法是上下文相关的,对于所有使用上下文无关或更复杂语法定义的乔姆斯基层次结构的语言,必须在语言的语义及其力量。

    C 设计者选择为语言提供强大的功能,这就是为什么不可判定性问题在 C 中无处不在。

    有像 Coq 这样的语言试图消除不可判定的情况,它们限制了递归函数的语义(它们只允许 sigma(primitive) 递归)。

    【讨论】:

      【解决方案4】:

      实施是否在任何特定情况下提供任何有用诊断的问题是标准管辖范围之外的实施质量问题。如果一个实现无条件地输出“警告:这个程序没有输出任何有用的诊断”甚至“警告:水是湿的”,那么即使实现没有,这样的输出也将完全满足标准关于诊断的所有要求输出任何其他诊断信息。

      此外,该标准的作者将许多操作描述为“未定义的行为”,他们期望这些操作会被许多(如果不是大多数)实现以有意义和有用的方式处理。根据已发布的基本原理文档,未定义行为等“识别符合语言扩展的区域”,因为允许实现指定它们在标准未定义的情况下的行为方式。

      让实现发出关于不可移植的构造的警告,但它们会以有用的方式处理会很烦人。

      在标准之前,一些实现会有效地接受以下结构:

      struct foo {
        int *p;
        char pad [4-sizeof (int*)];
        int q,r;
      };
      

      对于不超过四个字节的所有大小的指针(当时还没有 8 字节指针),而不是在指针正好是四个字节时大喊大叫,但是委员会中的一些人反对接受声明的想法对于零大小的数组。因此,达成了一种妥协,编译器会抱怨这些事情,程序员会忽略无用的警告,而有用的构造在支持它们的实现上仍然可用。

      虽然有一种模糊的尝试来区分应该产生程序员可以忽略的警告的构造与可能使用太多以至于警告会令人讨厌的构造,但发出有用诊断的事实是标准管辖范围之外的实施质量问题,这意味着无需过多担心此类区别。

      【讨论】:

      • 回复:是一个实施质量问题:这正是 gcc 的开发人员所看到的:“这是在标准的语义部分,而不是约束,所以没有诊断是必需的,但作为实施质量问题,我们应该对其进行诊断。”。