I am new to MISRA rules concepts. I have a rule 12.2 warning saying
The value of an expression shall be the same under any order of
evaluation that the standard permits (MISRA C 2004)
on the following C code:
PtToStack->Entry[PtToStack->top] = e ;
where PtToStack is pointer to stack, Entry is array in the stack structure and top variable is a field of the stack structure.
e has the same type of Entry.
Could any one help me to understand the warning?
This rule from MISRA-C:2004 (older standard) is concerning the order of evaluation of operands, in expressions where the order is not specified. There are plenty of examples and education material regarding the issue, below rule 12.2.
In your expression, there are no issues with unspecified order of evaluation. Therefore, the warning is incorrectly generated by your tool. Your static analyser is bad, file a bug report with the tool vendor.
Related
Follow-up question for: What is the rationale for "semantics violation does not require diagnostics"?.
N2596 working draft — December 11, 2020 ISO/IEC 9899:202x (E), 6.9.2 External object definitions, Semantics, 3:
If the declaration of an identifier for an object is a tentative definition and has internal linkage, the declared type shall not be an incomplete type.
This looks like a constraint — why is it placed in the section "Semantics" (a violation of the requirement does not require a diagnostic) rather than in the section "Constraints"?
UPD. Similar question: Understanding IEEE 754: why convertFromInt and convertToIntegerXXX are categorized as arithmetic operations and not conversion operations?.
This looks like a constraint
I agree.
— why is it placed in the section "Semantics" (a violation of the requirement does not require a diagnostic) rather than in the section "Constraints"?
With regard to the parenthetical, I do not take the question of whether a diagnostic is required to be a key distinction between a language constraint and a semantic rule. A constraint is a
restriction, either syntactic or semantic, by which the exposition of language elements is to be interpreted
(C17, 3.8/1)
That's a mouthful, to be sure, but it comes down to constraints being rules for what code matching the C lexical grammar in fact conforms overall to the language specification, and what doesn't. As a consequence, adherence to language constraint can always be evaluated at translation time, and code failing to satisfy a language constraint effectively is not expressed in C. That is, constraints are rules applying to source code. This is the context for the requirement that implementations diagnose constraint violations.
Semantic rules, on the other hand, explain what C code means or what program behavior is associated, if any. These are rules for the runtime behavior of programs and C language implementations.
Which is all a long way back around to: I agree, despite being listed among the semantic rules, paragraph 6.9.2/3 is by nature a language constraint.
I doubt whether anyone will be able to respond with authority on why it is placed among the semantic rules, but it is perhaps relevant here that exactly the same wording appears as a semantic rule in every published version of the language specification, all the way back to C89, which positioned "incomplete types" a bit differently than C11 and later do.
It might be that the original ANSI C committee took a liberty here in order to excuse compilers from diagnosing this issue, perhaps because they saw an ambiguity in whether that restriction should apply to incomplete types that are completed later in the translation unit. I'm not sure about that myself, today. It's entirely possible that this represented a compromise position.
When compiling this code in latest verson of gcc (or clang) with -std=c17 -pedantic-errors -Wall -Wextra
static const int y = 1;
static int x = y;
then I get no compiler diagnostic message even though I'm fairly sure that this is not valid C but a constraint violation. We can prove that it is non-conforming by taking look at C17 6.7.9/4:
Constraints
...
All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals.
Then the definition about constant expressions, in this case an integer constant expression (6.6):
An integer constant expression shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, _Alignof expressions, and floating constants that are the immediate operands of casts.
And then finally the definition about integer constants (6.4.4.1/2):
An integer constant begins with a digit, but has no period or exponent part. It may have a prefix that specifies its base and a suffix that specifies its type.
Thus a const int variable is not an integer constant nor is it an integer constant expression. And therefore not a valid initializer. This has been discussed before (for example here) and I think it's already established that this is non-conforming. However, my question is:
Why did gcc chose to be non-compliant even in strict mode?
clang has apparently always been non-compliant, but gcc changed from being compliant in version 7.3 to non-compliant in version 8.0 and above. gcc 7.3 and earlier gives "error: initializer element is not constant" even in default mode without -pedantic-errors.
Some sort of active, conscious decision seems to have been made regarding this message. Why was it removed entirely in gcc and why didn't they leave it as it was when compiling in strict mode -std=c17 -pedantic-errors?
Why did gcc chose to be non-compliant even in strict mode?
Inasmuch as the question as posed is directed to the motivation of the developers, the only information we have to go on as third parties comes from the public development artifacts, such as GCC bugzilla, repository commit messages, and actual code. As was pointed out in comments, the matter is discussed in the Bugzilla comment thread associated with the change.
The Bugzilla discussion appears to show that the developers considered the standard's requirements in this area, albeit in a somewhat perfunctory manner. See in particular comments 9 and 10. They raise paragraph 6.6/10 of the language specification:
An implementation may accept other forms of constant expressions.
They do not subject this to any particular scrutiny, and I read the comments more as seeking a justification for the change than as a thoughtful inquiry into GCC conformance considerations.
Thus, they made the change because they wanted to implement the feature request, and they found sufficient (for them) justification in the language of the standard to consider the altered behavior to be consistent with language constraints, therefore not requiring a diagnostic.
There is also an implied question of whether recent GCC's silent acceptance of the declaration forms presented in fact violates conforming processors' obligation to diagnose constraint violations.
Although it is possible to interpret 6.6/10 as allowing implementations to accept any expressions they choose as conforming to the requirements for any kind of constant expression, that seems fraught. Whether a given piece of code satisfies the language's constraints should not be implementation dependent. Either of these points of interpretation, if accepted, would resolve that problem:
6.6/10 should be interpreted as expressing a specific case of the general rule that a conforming implementation may accept non-conforming code, without implying that doing so entitles the processor to treat the code as conforming.
6.6/10 should be interpreted as permitting processors to interpret more expressions as "constant expressions" than those described in the preceding paragraphs, but that has no bearing on the definitions of the specific kinds of constant expressions defined in those paragraphs ("integer constant expressions" and "arithmetic constant expressions").
Those are not mutually exclusive. I subscribe to the latter, as I have written previously, and I tend to favor the former as well.
Now think about the following piece of code being executed in a safety critical embedded software where gcc_optimization_level is Level2 for size. Say a function, void foo(float v); is invoked as foo(a/b); where say a and b are also float. Is evaluating a C expression a/b when invoking the function foo violating any MISRA C guideline?
ps: This question is revised from the previous version that was weakly formulated last week. Now this question ensures that it is a single question, not a homework question and hopefully not driving an opinion.
There is nothing in MISRA C that prevents calling foo( a / b ) as long as the types are correct.
Within MISRA C the concept of essential types is an attempt to introduce stronger typing to the C language - this is detailed in Appendix D of MISRA C:2012, with the Rule 10.x guidelines being the enforcement.
Equally, there is nothing in MISRA C that discusses optimization levels, beyond section 4.2 "Understanding the compiler" and section 5.3.1 "Compiler configuration" which discuss requisite knowledge (not detailing any guidelines). These sections now form part of the (freely downloadable) MISRA Compliance.
Disclaimer: see profile.
This question already has answers here:
1998 vintage C code now fails to compile under gcc
(2 answers)
Closed 2 years ago.
This line-palindromic entry from the 1987 IOCCC:
https://www.ioccc.org/years.html#1987_westley
...is causing TCC 0.9.27 no issues during default compilation and works as intended.
However, GCC 9.3.0, even in -std=c89 mode, complains that the following instances of (int) (tni) are not lvalues:
for (; (int) (tni);)
(int) (tni) = reviled;
^
(lvalue required as left operand of assignment)
...
for ((int) (tni)++, ++reviled; reviled * *deliver; deliver++, ++(int) (tni))
^~
(lvalue required as increment operand)
(code beautified for better context)
My current thoughts:
In the = case, I suspect that the use of (int) (tni) as a condition in the for loop is disqualifying it as a lvalue, but I am not sure.
In the ++ case, I can see later in that code how its palindromic nature forces the author to use a -- operator between (int) and (tni) which is not considered as an issue. So GCC requires the ++ operator just before the variable, not before its casting, but hints at this requirement with a lvalue complaint.
Is there a definitive answer to these GCC complaints? Is TCC too lax in letting these off the hook?
Thanks in advance!
EDIT: I was kindly pointed towards a similar question which answers the casting issue here - please see my comment below for the solution!
TCC is not a conforming C implementation as is well known - TCC tries to be small and fast compiler that attempts to compile correct C code, and it often does not produce diagnostics that would be required by the standard. And as is known even more widely is that the first C standard came into being in 1989, and most widely known is that year 1987 preceded 1989.
C11 6.5.4p5:
Preceding an expression by a parenthesized type name converts the value of the expression to the named type. This construction is called a cast. 104) A cast that specifies no conversion has no effect on the type or value of an expression.
The footnote 104 notes that:
A cast does not yield an lvalue. Thus, a cast to a qualified type has the same effect as a cast to the unqualified version of the type.
For assignment operator, 6.5.16p2 says:
An assignment operator shall have a modifiable lvalue as its left operand.
6.5.16p2 is in constraint section, so violations must be diagnosed.
My CCS 6.1 ARM compiler (for LM3Sxxxx Stellaris) throws a warning :
"MISRA Rule 12.2. The value of an expression shall be the same under any order of evaluation that the standard permits"
for following code:
typedef struct {
...
uint32_t bufferCnt;
uint8_t buffer[100];
...
} DIAG_INTERFACE_T;
static DIAG_INTERFACE_T diagInterfaces[1];
...
DIAG_INTERFACE_T * diag = &diagInterfaces[0];
uint8_t data = 0;
diag->bufferCnt = 0;
diag->buffer[diag->bufferCnt++] = data; // line where warning is issued
...
I don't see a problem in my code. Is it false positive or my bug?
Put diag->bufferCnt++ in a separate statement (as it is also advised by Hans in OP comments) and the warning should not appear.
But regarding MISRA rule 12.2 I see no violation of 12.2 (there is a single sequence point in your statement and no unspecified behavior) in your program and I think it's a bug in your MISRA software.
For information there is also an advisory 12.13 rule in MISRA that says:
(MISRA-C:2004, 12.13) "The increment (++) and decrement (--) operators should not be mixed with other operators in an expression"
The problem with MISRA is their terminology use is far from perfect, for 12.3, while -> or = are C operators, in the explanation they then seem to talk only about arithmetic operators...
Although you don’t indicate it, this is MISRA-C:2004, Rule 12.2, and is now MISRA-C:2012 Rule 13.2. As oauh says, this has nothing to do with "order of evaluation”.
I highly recommend referring to MISRA-C:2012 even if you are required to be MISRA-C:2004 compliant, having MISRA-C:2012 around helps, because it has clarified many of the guidelines, including additional rationale, explanations and examples.
You should not be using a compiler to solely check for MISRA-C compliancy, its nice, but compilers #1 goal is not to warn you about all the traps and pitfalls of the language it is dedicated to take advantage of (optimization). They're not very precise either, as in this case. Also, there are many undefined behaviors across translation units, compilers cannot warn about. Its best to also use a dedicated MISRA Static analysis tool, one that is not compiler specific, but that warns about all unpredictable constructs from the ISO C standards point of view, not a particular implementation.
As oauh also said, this is a violation of MISRA-C:Rule 12.13, which is now MISRA-C:2012 Rule 13.3 which has been relaxed to permit ++ and -- to be mixed with other operators, provided that the ++ or -- is the only source of side-effects (in your case the assignment is also a side effect in C terminology).
The Rule is not critical, i.e. its well defined behavior, but the different values resulting from the prefix version and the postfix version can cause confusion, thus it is “advisory” meaning no formal deviation is required (again, a decent MISRA-C tool would allow you to suppress this particular violation).