Re: [PATCH] Various pages: SYNOPSIS: Use VLA syntax in function parameters

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 11/13/22 17:56, Alejandro Colomar wrote:>>> On 11/13/22 17:28, Alejandro Colomar wrote:
SYNOPSIS:

unary-operator:  . identifier


DESCRIPTION:

-  It is not an lvalue.

    -  This means sizeof() and _Lengthof() cannot be applied to them.

Sorry, the above is a thinko.

I wanted to say that, like sizeof() and _Lengthof(), you can't assign to it.

    -  This prevents ambiguity with a designator in an initializer-list within a nested braced-initializer.

-  The type of a .identifier is always an incomplete type.

Or rather, more easily prohibit explicitly using typeof(), sizeof(), and _Lengthof() to it.

Hmm, this is not enough.  Pointer arithmetics are interesting, and for that, you need to implicitly know the sizeof(*.p).

How about allowing only integral types or pointers to integral types?

I've been thinking about keeping the number of passes as low as possible, while allowing most useful expressions:

Maybe forcing some ordering can help:

- The type of a .initializer is complete after the opening parenthesis of the function-declarator (if it refers to a parameter) or after the opening brace of a braced-initializer, if it refers to a struct/union member, except when the type is a variably-modified type, which will be complete after the closing parenthesis or brace respectively.

I'm not sure I got the wording precisely, or if I covered all cases (like types that cannot be completed for other reasons, even after the closing ')' or '}'.




    -  This prevents circular dependencies involving sizeof() or _Lengthof().

-  Shadowing rules apply.

    -  This prevents ambiguity.


EXAMPLES:


-  Valid examples (libc):

        int
        strncmp(const char s1[.n],
                const char s2[.n],
                size_t n);

        int
        cacheflush(void addr[.nbytes],
                   int nbytes,
                   int cache);

        long
        mbind(void addr[.len],
              unsigned long len,
              int mode,
              const unsigned long nodemask[(.maxnode + ULONG_WIDTH ‐ 1)
                                           / ULONG_WIDTH],
              unsigned long maxnode, unsigned int flags);

        void *
        bsearch(const void key[.size],
                const void base[.size * .nmemb],
                size_t nmemb,
                size_t size,
                int (*compar)(const void [.size], const void [.size]));

-  Valid examples (my own):

        void
        ustr2str(char dst[restrict .len + 1],
                 const char src[restrict .len],
                 size_t len);

        char *
        stpecpy(char dst[.end - .dst + 1],
                char *restrict src,
                char end[1]);

-  Valid examples (from this thread):

    -
        struct s { int a; };
        void f(int a, int b[((struct s) { .a = 1 }).a]);

        Explanation:
        -  Because of shadowing rules, .a=1 refers to the struct member.
           -  Also, if .a referred to the parameter, it would be an rvalue, so it wouldn't be valid to assign to it.         -  (...).a refers to the struct member too, since otherwise an rvalue is not expected there.

    -
        void foo(struct bar { int x; char c[.x] } a, int x);

        Explanation:
        -  Because of shadowing rules, [.x] refers to the struct member.

    -
        struct bar { int y; };
        void foo(char p[((struct bar){ .y = .x }).y], int x);

        Explanation:
        -  .x unambiguously refers to the parameter.

-  Undefined behavior:

    -
        struct bar { int y; };
        void foo(char p[((struct bar){ .y = .y }).y], int y);

        Explanation:
        -  Because of shadowing rules, =.y refers to the struct member.
        -  .y=.y means initialize the member with itself (uninitialized use).
        -  (...).y refers to the struct member, since otherwise an rvalue is not expected there.

-  Constraint violations:

    -
        void foo(char (*a)[sizeof *.b], char (*b)[sizeof *.a]);

        Explanation:
        -  sizeof(*.b): Cannot get size of incomplete type.
        -  sizeof(*.a): Cannot get size of incomplete type.

    -
        void f(size_t s, int a[sizeof(1) = 1]);

        Explanation:
        -  Cannot assign to rvalue.

    -
        void f(size_t s, int a[.s = 1]);

        Explanation:
        -  Cannot assign to rvalue.

    -
        void f(size_t s, int a[sizeof(.s)]);

This should actually be valid.


        Explanation:
        -  sizeof(.s): Cannot get size of incomplete type.


Does this idea make sense to you?


Cheers,
Alex




--
<http://www.alejandro-colomar.es/>

Attachment: OpenPGP_signature
Description: OpenPGP digital signature


[Index of Archives]     [Kernel Documentation]     [Netdev]     [Linux Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux