Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions embed.fnc
Original file line number Diff line number Diff line change
Expand Up @@ -315,18 +315,20 @@
: like 'int' or 'char', and 'cast' by perhaps 'struct foo'.
:
: The complete list of conventions is:
: type the argument names a type
: cast the argument names a type which the macro casts to
: SP the argument is the stack pointer, SP
: block the argument is a C brace-enclosed block
: number the argument is a C numeric constant, like 3
: token the argument is a generic C preprocessor token, like abc
: "string" the argument is a literal C double-quoted string; what's important
: here are the quotes; for clarity, you can say whatever you want
: inside them
: block the argument is a C brace-enclosed block
: cast the argument names a type which the macro casts to
: const_expr the argument is an expression whose result is known at compile
: time
: number the argument is a C numeric constant, like 3
: SP the argument is the stack pointer, SP
: "string" the argument is a literal C double-quoted string; what's important
: here are the quotes; for clarity, you can say whatever you want
: inside them
: token the argument is a generic C preprocessor token, like abc
: type the argument names a type
:
: Unlike other arguments, none of these is of the form 'int name'. There is no
: name.
: 'name'.
:
: If any argument or return value is not one of the above, and isn't legal C
: language, the entry still can be specified, using the 'u' flag.
Expand Down
111 changes: 89 additions & 22 deletions perl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4250,18 +4250,42 @@ hint to the compiler that this condition is likely to be false.
/* placeholder */
#endif

/* STATIC_ASSERT_DECL/STATIC_ASSERT_STMT are like assert(), but for compile
time invariants. That is, their argument must be a constant expression that
can be verified by the compiler. This expression can contain anything that's
known to the compiler, e.g. #define constants, enums, or sizeof (...). If
the expression evaluates to 0, compilation fails.
Because they generate no runtime code (i.e. their use is "free"), they're
always active, even under non-DEBUGGING builds.
STATIC_ASSERT_DECL expands to a declaration and is suitable for use at
file scope (outside of any function).
STATIC_ASSERT_STMT expands to a statement and is suitable for use inside a
function.
/*
=for apidoc Am||STATIC_ASSERT_DECL|const_expr
=for apidoc_item STATIC_ASSERT_EXPR
=for apidoc_item STATIC_ASSERT_STMT

These are like assert(), but for compile time invariants. That is, their
argument must be a constant expression that can be verified by the compiler.
This expression can contain anything that's known to the compiler, e.g. #define
constants, enums, or sizeof (...). If the expression evaluates to 0,
compilation fails.

Because they generate no runtime code (i.e. their use is "free"), they're
always active, even under non-DEBUGGING builds.

C<STATIC_ASSERT_STMT> expands to a statement and is suitable for use inside a
function.

C<STATIC_ASSERT_DECL> expands to a declaration and is suitable for use inside a
function or at file scope (outside of any function).

C<STATIC_ASSERT_EXPR> expands to an expression and is suitable anywhere an
expression is usable. It has some limitations compared to the other two when
used on a platform without L</C<PERL_USE_GCC_BRACE_GROUPS>>. On those
platforms it expands to an ASSUME(). When called with a compile-time
expression, the compiler should optimize out the expression, so that the use of
this is "free", but constness is not enforced. On non-DEBUGGING builds, this
will expand to a no-op, so again it is "free", but no checking is done.

Thus code that uses this macro can be ported to all platforms without needing
to change, and it will work as well as is possible on that platform.
Presumably it will get compiled at some point before release on a platform
where it has parity with the other two forms.

=cut
*/

#if (! defined(__IBMC__) || __IBMC__ >= 1210) \
&& (( defined(static_assert) && ( defined(_ISOC11_SOURCE) \
|| (__STDC_VERSION__ - 0) >= 201101L)) \
Expand All @@ -4272,21 +4296,64 @@ hint to the compiler that this condition is likely to be false.
*/
# define STATIC_ASSERT_DECL(COND) static_assert(COND, #COND)
#else
/* We use a bit-field instead of an array because gcc accepts
'typedef char x[n]' where n is not a compile-time constant.
We want to enforce constantness.
*/
# define STATIC_ASSERT_2(COND, SUFFIX) \
typedef struct { \
unsigned int _static_assertion_failed_##SUFFIX : (COND) ? 1 : -1; \
} _static_assertion_failed_##SUFFIX PERL_UNUSED_DECL
# define STATIC_ASSERT_1(COND, SUFFIX) STATIC_ASSERT_2(COND, SUFFIX)
# define STATIC_ASSERT_DECL(COND) STATIC_ASSERT_1(COND, __LINE__)

/* This generates a struct with a bit field like so:
*
* struct { unsigned int name#: size; } name#
*
* 'name#' is the name followed by the line number this is used on. The
* name is 'static_assertion_failed_', so that a typical name would be
*
* static_assertion_failed_123
*
* The first name# will show up in the compiler's error message, clueing in the
* reader as to the problem and where. The second one makes sure that the
* struct name is unique to the compilation unit.
*
* 'size' is expressed as a ternary: '((cond) ? 1 : -1)'
*
* If 'cond' is true the size is 1, which is legal; if false, the size is -1.
* It is illegal to have a negatively-sized bit field, so the compilation will
* abort iff 'cond' is false, giving an appropriate error message.
*
* The basic struct is used in different ways depending on whether the
* application is for a declaration (typedef struct), or statement
* (STMT_START { struct } STMT_END).
*
* We use a bit-field instead of an array because gcc accepts
typedef char x[n]
where n is not a compile-time constant. We want to enforce constantness.
*
* We need the FOOL macros to get proper cpp parameter concatanation. */
# define STATIC_ASSERT_STRUCT_NAME_FOOL_CPP_2_(line) \
static_assertion_failed_##line
# define STATIC_ASSERT_STRUCT_NAME_FOOL_CPP_(line) \
STATIC_ASSERT_STRUCT_NAME_FOOL_CPP_2_(line)
/* Return the struct and element name */
# define STATIC_ASSERT_STRUCT_NAME_ \
STATIC_ASSERT_STRUCT_NAME_FOOL_CPP_(__LINE__)

/* Return the struct body */
# define STATIC_ASSERT_STRUCT_BODY_(COND, NAME) \
struct { unsigned NAME : (COND) ? 1 : -1; }

# define STATIC_ASSERT_DECL(COND) \
typedef STATIC_ASSERT_STRUCT_BODY_(COND, STATIC_ASSERT_STRUCT_NAME_) \
STATIC_ASSERT_STRUCT_NAME_ PERL_UNUSED_DECL

#endif

/* We need this wrapper even in C11 because 'case X: static_assert(...);' is an
error (static_assert is a declaration, and only statements can have labels).
*/
#define STATIC_ASSERT_STMT(COND) STMT_START { STATIC_ASSERT_DECL(COND); } STMT_END
#define STATIC_ASSERT_STMT(COND) \
STMT_START { STATIC_ASSERT_DECL(COND); } STMT_END

#ifdef PERL_USE_GCC_BRACE_GROUPS
# define STATIC_ASSERT_EXPR(COND) ({ STATIC_ASSERT_DECL(COND); })
#else
# define STATIC_ASSERT_EXPR(COND) ASSUME(COND)
#endif

#ifndef __has_builtin
# define __has_builtin(x) 0 /* not a clang style compiler */
Expand Down
2 changes: 1 addition & 1 deletion pp.h
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ Does not use C<TARG>. See also C<L</XPUSHu>>, C<L</mPUSHu>> and C<L</PUSHu>>.
#define mXPUSHi(i) STMT_START { EXTEND(sp,1); mPUSHi(i); } STMT_END
#define mXPUSHu(u) STMT_START { EXTEND(sp,1); mPUSHu(u); } STMT_END

#define SETs(s) (*sp = s)
#define SETs(s) (STATIC_ASSERT_EXPR(sizeof(*sp) == sizeof(s)), *sp = s)
#define SETTARG STMT_START { SvSETMAGIC(TARG); SETs(TARG); } STMT_END
#define SETp(p,l) STMT_START { sv_setpvn(TARG, (p), (l)); SETTARG; } STMT_END
#define SETn(n) STMT_START { TARGn(n,1); SETs(TARG); } STMT_END
Expand Down
Loading