Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Comments on How to use _Generic on restrict pointers like C23 tells me to do?

Post

How to use _Generic on restrict pointers like C23 tells me to do?

+4
−0

I just noticed that ISO 9899:2024 6.7.4 has this weird text added, which wasn't there previously:

The intended use of the restrict qualifier (like the register storage class) is to promote optimization, and deleting all instances of the qualifier from all preprocessing translation units composing a conforming program does not change its meaning (i.e. observable behavior), unless _Generic is used to distinguish whether or not a type has that qualifier.

Ok... But how exactly am I to use _Generic to do that? Notably, pointed-at data cannot be restrict qualified, only the pointer object itself.

C11 had an ambiguous wording where it wasn't clear if the first controlling expression in the _Generic association list would respect qualifiers or not, resulting in different compilers behaving differently.

C17 fixed this by adding this text to 6.5.1.1:

The type of the controlling expression is the type of the expression as if it had undergone an lvalue conversion

Lvalue conversion meaning that qualifiers are stripped. So for any type* restrict, lvalue conversion will always turn it into type*.

Example to illustrate:

#include <stdio.h>

int main() 
{
  char* restrict ptr = 
    _Generic(ptr,
             char*: "I'm a char*.",
             char* restrict: "Good luck getting this printed");
  puts(ptr);
}

Output on any C17/C23 compliant compiler:

I'm a char*.

clang helpfully gives extra diagnosis:

warning: due to lvalue conversion of the controlling expression, association of type 'char *restrict' will never be selected because it is qualified [-Wunreachable-code-generic-assoc]

I'm am missing something here or should this be posted as a Defect Report for C23?

History

1 comment thread

pointees can be restrict too (8 comments)
pointees can be restrict too
alx‭ wrote 2 days ago
char *restrict *p;

_Static_assert(_Generic(p, char *restrict *: 1, char **: 0));
$ gcc -Wall -Wextra -Wpedantic -S r.c 
$ 

Notably, pointed-at data cannot be restrict qualified, only the pointer object itself.

Is there such a constraint in the language? I've never read the specification of restrict because it's too weird, but I think the code above is valid.

Lundin‭ wrote 1 day ago · edited 1 day ago

alx‭ Yeah sure you can do pointer-to-pointers or function pointers but was that really the meaning of 6.7.4? It rather seems like the new text was added by someone who's slept to the changes of _Generic in C17 and then those who made said changes in C17 were asleep while voting in this text.

C23 6.7.4 Constraints: "Types other than pointer types whose referenced type is an object type and (possibly multidimensional) array types with such pointer types as element type shall not be restrict-qualified."

alx‭ wrote 1 day ago

Yeah sure you can do pointer-to-pointers or function pointers but was that really the meaning of 6.7.4?

The new versions of the standard don't come with a rationale, so we're a bit blind about what they did and why. I've only joined the committee after C23, so I don't know details of discussions prior to that.

However, I have access to the git(1) repository of the drafts, so I can use git-blame(1) to try to figure out why it was changed.

The commit that changed it has this commit message:

commit 6fd7efa3227c4fd51ce3d08d06cd3b879c118f17
Author: ThePhD <[email protected]>
Date:   2023-03-23 15:58:56 -0400

    ✨ All NB Comments from 102-150

It added , unless \code{_Generic} is used to distinguish whether or not a type has that qualifier before the period.

It isn't very helpful, as we have to search in too many comments. I wish we had one commit per NB comment...

I'll try to find the exact NB comment that mentions this.

alx‭ wrote 1 day ago · edited 1 day ago

Found. The comment seems to be this one:

Member body / National Committee: GB

Comment number: 112

Clause/subclause: 6.7.3

Paragraph: 9

Type of comment: ed (editorial)

Comments:

The statement about removing restrict not changing behavior of a conforming program is not strictly accurate for normative text, since _Generic can be used in a way that distinguishes between e.g. pointers to restrict-qualified and non-restrict-qualified types.

Proposed change:

At the end of paragraph 9, insert “, unless _Generic is used to distinguish whether or not a type has that qualifier”.

The comment is in N3067. https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3067.doc

N3108 contains the dispositions, which for comment GB-112 says Accepted.

The meeting that accepted it seems to be January 2023, and the minutes are in N3116 https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3116.pdf.

It's under 8.2 List of editorial comments for mass-approval.

Skipping 1 deleted comment.

Lundin‭ wrote 1 day ago

alx‭ It sounds like they were indeed just talking nonsense then, not aware of the C17 clarification to _Generic. Although it would technically be possible in contrived ways to write a _Generic checking if something is restrict... Since nothing like that is mentioned in the added text, it isn't very helpful, just confusing.

alx‭ wrote about 24 hours ago · edited about 23 hours ago

It sounds like they were indeed just talking nonsense then, not aware of the C17 clarification to _Generic.

Either that, or maybe they're pedantic enough that they cared about pointers-to-pointers (which doesn't even work well for restrict...).

Although it would technically be possible in contrived ways to write a _Generic checking if something is restrict...

Yup.

Since nothing like that is mentioned in the added text, it isn't very helpful, just confusing.

Also agree.

Interestingly, in C2y (most likely, C29), _Generic() will allow a type name as its first operand, and in that case, the type won't change as if by lvalue conversion. So this comment may become retroactively meaningful.

alx@devuan:~/tmp$ cat r.c 
int
main(void)
{
	return _Generic(char *restrict, char *: 0, char *restrict: 1);
}
alx@devuan:~/tmp$ gcc -Wall -Wextra -std=c2y r.c 
alx@devuan:~/tmp$ ./a.out; echo $?
1
Lundin‭ wrote about 5 hours ago

"which doesn't even work well for restrict" Indeed - first you promise "I will only change the object through this pointer" then suddenly you go "...but I might change the pointer itself, muahaha!". It might be difficult to even make sense of type*restrict* code.

alx‭ wrote about 4 hours ago

Lundin‭

And there are more issues.

If restrict were designed to enable safer code (which should be the goal), it would disallow pointing at an object from two different parameters. However, it is really an optimization-only feature (it's designed so that you can point from two parameters, as long as you only access through one of them).

Because of the current bogus design, strtol(3) can (and does) have restrict:

long strtol(const char *restrict s, char **restrict endp, int base);

However, this is a perfectly valid (and common) call to strtol(3):

n = strtol(s, &s, 10);

Which means compilers can't diagnose anything, because it would trigger false positives in many strtol(3) calls. Thus, restrict is a foot-gun: it enables aggressive optimizations, and doesn't enable diagnostics other than the trivial ones of memcpy(3), so if you write buggy code, you're out of luck.