diff --git a/embed.fnc b/embed.fnc index c35710d56423..27ddbe527081 100644 --- a/embed.fnc +++ b/embed.fnc @@ -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. diff --git a/perl.h b/perl.h index ee92716971ab..f17833089575 100644 --- a/perl.h +++ b/perl.h @@ -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 expands to a statement and is suitable for use inside a +function. + +C expands to a declaration and is suitable for use inside a +function or at file scope (outside of any function). + +C 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>. 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)) \ @@ -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 */ diff --git a/pp.h b/pp.h index 76d29202b227..c8aa27bb73d9 100644 --- a/pp.h +++ b/pp.h @@ -580,7 +580,7 @@ Does not use C. See also C>, C> and C>. #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