So… I decided to go full retard, if anyone's interested:
# This patch adds an option the the C compiler: -fdefault-const
# When this is enabled, everything will be a constant by default, and only be mutable if specified with the "mut" or "mutable" keyword.
# I tried to implement this the easiest way possible, by just flipping the const_p bit, and adding the keyword (reusing "mutable" from C++).
# Unfortunately, the fact that const_p is a single bit makes it hard to get everything right, as some checks assume that a keyword was specified only if the bit is on.
# As such, I had to flip the bit to "fix" a warning, but with a consequence: If -fdefault-const is enabled, it will only warn about "mut" being specified at the wrong places, and if not, it will only warn about "const".
# This was only tested and works with regular C. I haven't tested it on C++, so the option is unavailable there.
# The only effect this has on C++ is a new keyword: "mut", that means the same as "mutable".
# Also untested is compatibility with library headers such as libc.
--- gcc/c/c-decl.c
+++ gcc/c/c-decl.c
@@ -6128,6 +6128,7 @@
/* Type qualifiers before the return type of the function
qualify the return type, not the function type. */
+ type_quals ^= TYPE_QUAL_CONST * flag_default_const; // Only has effect on the warnings given by qualifying a function.
if (type_quals)
{
/* Type qualifiers on a function return type are
@@ -9499,6 +9500,7 @@
ret->expr_const_operands = true;
ret->typespec_kind = ctsk_none;
ret->address_space = ADDR_SPACE_GENERIC;
+ ret->const_p = flag_default_const;
return ret;
}
@@ -9546,6 +9548,11 @@
specs->const_p = true;
specs->locations[cdw_const] = loc;
break;
+ case RID_MUTABLE:
+ dupe = !specs->const_p;
+ specs->const_p = false;
+ specs->locations[cdw_mutable] = loc;
+ break;
case RID_VOLATILE:
dupe = specs->volatile_p;
specs->volatile_p = true;
--- gcc/c/c-parser.c
+++ gcc/c/c-parser.c
@@ -562,6 +562,7 @@
case RID_UNION:
case RID_TYPEOF:
case RID_CONST:
+ case RID_MUTABLE:
case RID_ATOMIC:
case RID_VOLATILE:
case RID_RESTRICT:
@@ -647,6 +648,7 @@
switch (token->keyword)
{
case RID_CONST:
+ case RID_MUTABLE:
case RID_VOLATILE:
case RID_RESTRICT:
case RID_ATTRIBUTE:
@@ -723,6 +725,7 @@
case RID_UNION:
case RID_TYPEOF:
case RID_CONST:
+ case RID_MUTABLE:
case RID_VOLATILE:
case RID_RESTRICT:
case RID_ATTRIBUTE:
@@ -2595,6 +2598,7 @@
declspecs_add_qual (loc, specs, value);
break;
case RID_CONST:
+ case RID_MUTABLE:
case RID_VOLATILE:
case RID_RESTRICT:
attrs_ok = true;
@@ -3928,6 +3932,7 @@
case RID_UNSIGNED:
case RID_LONG:
case RID_CONST:
+ case RID_MUTABLE:
case RID_EXTERN:
case RID_REGISTER:
case RID_TYPEDEF:
@@ -5994,6 +5999,7 @@
c_parser_consume_token (parser);
}
else if (c_parser_next_token_is_keyword (parser, RID_CONST)
+ || c_parser_next_token_is_keyword (parser, RID_MUTABLE)
|| c_parser_next_token_is_keyword (parser, RID_RESTRICT))
{
warning_at (c_parser_peek_token (parser)->location,
--- gcc/c/c-tree.h
+++ gcc/c/c-tree.h
@@ -251,6 +251,7 @@
cdw_noreturn,
cdw_thread,
cdw_const,
+ cdw_mutable,
cdw_volatile,
cdw_restrict,
cdw_saturating,
--- gcc/c-family/c-common.c
+++ gcc/c-family/c-common.c
@@ -548,7 +548,8 @@
{ "inline", RID_INLINE, D_EXT89 },
{ "int", RID_INT, 0 },
{ "long", RID_LONG, 0 },
- { "mutable", RID_MUTABLE, D_CXXONLY | D_CXXWARN },
+ { "mut", RID_MUTABLE, 0 },
+ { "mutable", RID_MUTABLE, 0 },
{ "namespace", RID_NAMESPACE, D_CXXONLY | D_CXXWARN },
{ "new", RID_NEW, D_CXXONLY | D_CXXWARN },
{ "noexcept", RID_NOEXCEPT, D_CXXONLY | D_CXX11 | D_CXXWARN },
--- gcc/c-family/c.opt
+++ gcc/c-family/c.opt
@@ -1174,6 +1174,10 @@
C++ ObjC++ Var(flag_declone_ctor_dtor) Init(-1)
Factor complex constructors and destructors to favor space over speed.
+fdefault-const
+C Var(flag_default_const) Init(0)
+Make all declarations constant by default, unless it's explicity mutable.
+
fdefault-inline
C++ ObjC++ Ignore
Does nothing. Preserved for backward compatibility.
This will, of course, require anyone compiling your code to have this patch, unfortunately.