diff --git a/share/libc/assert.h b/share/libc/assert.h
index 0886eac92d58bb02635d595448dcc83ba4f914bb..4e7618753ef4fa94d167e7b9e6211a34a5530cbf 100644
--- a/share/libc/assert.h
+++ b/share/libc/assert.h
@@ -34,6 +34,9 @@ __BEGIN_DECLS
 */
 extern void __FC_assert(int c, const char* file, int line, const char*expr);
 
+#define static_assert _Static_assert
+
+
 __END_DECLS
 __POP_FC_STDLIB
 #endif
diff --git a/src/kernel_internals/parsing/clexer.mll b/src/kernel_internals/parsing/clexer.mll
index 5745e3cc4ab7dedb8ed30f51c166cdbf558ab511..b0b9065d0ad1d66ccb4cc844c21e4b44e901fdeb 100644
--- a/src/kernel_internals/parsing/clexer.mll
+++ b/src/kernel_internals/parsing/clexer.mll
@@ -161,6 +161,16 @@ let init_lexicon _ =
                "_Noreturn is a C11 keyword, use -c11 option to enable it");
            IDENT "_Noreturn"
          end);
+      ("_Static_assert",
+       fun loc ->
+         if Kernel.C11.get () then STATIC_ASSERT loc
+         else begin
+           Kernel.(
+             warning
+               ~wkey:wkey_conditional_feature
+               "_Static_assert is a C11 keyword, use -c11 option to enable it");
+           IDENT "_Static_assert"
+         end);
       ("__attribute__", fun loc -> ATTRIBUTE loc);
       ("__attribute", fun loc -> ATTRIBUTE loc);
       ("__blockattribute__", fun _ -> BLOCKATTRIBUTE);
diff --git a/src/kernel_internals/parsing/cparser.mly b/src/kernel_internals/parsing/cparser.mly
index 11d9ae4dbcd3c6a7af92e7f44b7412d1a2585836..a026e11a65e3b1ba88b714d5dcac47287063a257 100644
--- a/src/kernel_internals/parsing/cparser.mly
+++ b/src/kernel_internals/parsing/cparser.mly
@@ -368,7 +368,7 @@ let in_ghost_block ?(battrs=[]) l =
 %token<Cabs.cabsloc> IF TRY EXCEPT FINALLY
 %token ELSE
 
-%token<Cabs.cabsloc> ATTRIBUTE INLINE NORETURN ASM TYPEOF FUNCTION__ PRETTY_FUNCTION__
+%token<Cabs.cabsloc> ATTRIBUTE INLINE NORETURN STATIC_ASSERT ASM TYPEOF FUNCTION__ PRETTY_FUNCTION__
 %token LABEL__
 %token<Cabs.cabsloc> BUILTIN_VA_ARG
 %token BLOCKATTRIBUTE
@@ -1080,6 +1080,14 @@ declaration:                                /* ISO 6.7.*/
           { doDeclaration (Some $1) ((snd $2)) (fst $2) $3 }
 |   SPEC decl_spec_list SEMICOLON
       { doDeclaration (Some $1) ((snd $2)) (fst $2) [] }
+|   STATIC_ASSERT LPAREN expression RPAREN
+      {
+        STATIC_ASSERT ($3, "", $1)
+      }
+|   STATIC_ASSERT LPAREN expression COMMA string_constant RPAREN
+      {
+        STATIC_ASSERT ($3, fst $5, $1)
+      }
 ;
 
 init_declarator_list:                       /* ISO 6.7 */
diff --git a/src/kernel_internals/typing/cabs2cil.ml b/src/kernel_internals/typing/cabs2cil.ml
index 75665dec0631f0844c434e7dacb8be41d85acee8..9ca6025872c6f0272e67affc9c0a6a730f306c33 100644
--- a/src/kernel_internals/typing/cabs2cil.ml
+++ b/src/kernel_internals/typing/cabs2cil.ml
@@ -9192,6 +9192,56 @@ and doDecl local_env (isglobal: bool) : Cabs.definition -> chunk = function
       | _ -> Kernel.fatal ~current:true "Too many attributes in pragma"
     end
 
+  | Cabs.STATIC_ASSERT (e, s, loc) -> begin
+      let make_static_assert_vi_and_init ~global cond_exp =
+        (* pick a different name per call, to avoid typing issues;
+           the actual name is never used anyway *)
+        let vname, _ = newAlphaName false global "" "_Static_assert" in
+        let vi =
+          if global then
+            makeGlobalVar ~temp:false ~referenced:true vname intType
+          else
+            makeVarinfo ~source:false ~referenced:true false false vname intType
+        in
+        let attr name = Attr (name, []) in
+        vi.vattr <-
+          Cil.addAttributes [attr "FC_BUILTIN"; attr "_STATIC_ASSERT"] vi.vattr;
+        let exp =
+          (* create an arbitrary expression using a binary operator and
+             SizeOfStr for typing purposes only; the pretty-printer in
+             Cil_printer will desugar it as intended. *)
+          new_exp ~loc
+            (BinOp (PlusA, cond_exp, new_exp ~loc (SizeOfStr s), voidType))
+        in
+        vi, SingleInit exp
+      in
+      CurrentLoc.set (convLoc loc);
+      let (_, _, cond_exp, _) = doExp local_env CConst e ADrop in
+      begin
+        match Cil.constFoldToInt ~machdep:true cond_exp with
+        | Some i ->
+          if Integer.(equal i zero) then
+            Kernel.error ~current:true "static assertion failed%s%s@."
+              (if s <> "" then ": " else "") s
+        | None ->
+          Kernel.error ~current:true
+            "failed to evaluate constant expression in static assertion:@ \
+             @[%a@]"
+            Cprint.print_expression e
+      end;
+      if isglobal then begin
+        let vi, init = make_static_assert_vi_and_init true cond_exp in
+        cabsPushGlobal
+          (GVar (vi, { init = Some init}, loc));
+        empty
+      end
+      else
+        let vi, init = make_static_assert_vi_and_init false cond_exp in
+        let instr = mkStmtOneInstr (Local_init (vi, AssignInit init, loc)) in
+        !currentFunctionFDEC.slocals <- !currentFunctionFDEC.slocals @ [vi];
+        local_var_chunk (i2c (instr,[],[],[])) vi
+    end
+
   | Cabs.FUNDEF (spec,((specs,(n,dt,a, _)) : Cabs.single_name),
                  (body : Cabs.block), loc1, loc2) when isglobal ->
     begin
diff --git a/src/kernel_services/ast_printing/cabs_debug.ml b/src/kernel_services/ast_printing/cabs_debug.ml
index fbcdd4854b795ce496e5ba8bc6a0563d77bb1fa2..e337f84b87d1eadee03de099f4f2314347e842e0 100644
--- a/src/kernel_services/ast_printing/cabs_debug.ml
+++ b/src/kernel_services/ast_printing/cabs_debug.ml
@@ -182,6 +182,8 @@ and pp_def fmt = function
     fprintf fmt "@[<hov 2>GLOBASM %s, loc(%a)@]" s pp_cabsloc loc
   |     PRAGMA (exp, loc) ->
     fprintf  fmt "@[<hov 2>PRAGMA exp(%a, loc(%a))@]" pp_exp exp pp_cabsloc loc
+  |     STATIC_ASSERT (exp, s, loc) ->
+    fprintf  fmt "@[<hov 2>STATIC_ASSERT exp(%a, %s, loc(%a))@]" pp_exp exp s pp_cabsloc loc
   |     LINKAGE (s, loc, defs) ->
     fprintf  fmt "@[<hov 2>LINKAGE %s, loc(%a), defs(" s pp_cabsloc loc;
     List.iter (fun def -> fprintf fmt ",@ def(%a)" pp_def def) defs;
diff --git a/src/kernel_services/ast_printing/cil_printer.ml b/src/kernel_services/ast_printing/cil_printer.ml
index 2afe7bfc31ffbf04f057b23e73ad4d9f9cccd5cd..efa88062efff9de170623a371a19c1e85f5f8884 100644
--- a/src/kernel_services/ast_printing/cil_printer.ml
+++ b/src/kernel_services/ast_printing/cil_printer.ml
@@ -1043,6 +1043,16 @@ class cil_printer () = object (self)
             instr_terminator
 
       end
+    | Local_init(vi, AssignInit (SingleInit {
+        enode = BinOp (_, cond_exp, {enode = SizeOfStr s}, _)}), _) when
+        Cil.hasAttribute "_STATIC_ASSERT" vi.vattr ->
+      (* Hijack pretty-printer for a local _Static_assert *)
+      if s = "" then
+        fprintf fmt "@[<2>_Static_assert(%a)%s@]@\n"
+          self#exp cond_exp instr_terminator
+      else
+        fprintf fmt "@[<2>_Static_assert(%a, \"%s\")%s@]@\n"
+          self#exp cond_exp s instr_terminator
     | Local_init(vi, AssignInit i, _) ->
       Format.fprintf fmt "@[<2>%a =@ %a%s@]"
         self#vdecl vi self#init i instr_terminator
@@ -1380,7 +1390,10 @@ class cil_printer () = object (self)
       (* [JS 2012/12/07] could directly call self#attributesGen whenever we are
          sure than it puts its printing material inside a box *)
       fprintf fmt "@[%a@]" (self#attributesGen true) blk.battrs;
-    let locals_decl = List.filter (fun v -> not v.vdefined) blk.blocals in
+    let visible_decl v =
+      not (v.vdefined || Cil.hasAttribute "_STATIC_ASSERT" v.vattr)
+    in
+    let locals_decl = List.filter (fun v -> visible_decl v) blk.blocals in
     if locals_decl <> [] then
       Pretty_utils.pp_list ~pre:"@[<v>" ~sep:"@;" ~suf:"@]@ "
         self#vdecl_complete fmt locals_decl;
@@ -1778,6 +1791,20 @@ class cil_printer () = object (self)
         fprintf fmt "%a %a;@\n"
           self#pp_keyword (if comp.cstruct then "struct" else "union")
           self#varname comp.cname
+      | GVar (vi, {init = Some (SingleInit {
+          enode = BinOp (_, cond_exp, { enode = SizeOfStr s }, _) })}, l)
+        when Cil.hasAttribute "_STATIC_ASSERT" vi.vattr ->
+        (* Hijack pretty-printer for a global _Static_assert *)
+        self#line_directive ~forcefile:true fmt l;
+        Format.fprintf fmt "@[<hov 2>";
+        if s = "" then
+          fprintf fmt "_Static_assert(%a)"
+            self#exp cond_exp
+        else
+          fprintf fmt "_Static_assert(%a, \"%s\")"
+            self#exp cond_exp s;
+        fprintf fmt ";";
+        fprintf fmt "@]@\n";
       | GVar (vi, io, l) ->
         self#line_directive ~forcefile:true fmt l;
         Format.fprintf fmt "@[<hov 2>";
diff --git a/src/kernel_services/ast_printing/cprint.ml b/src/kernel_services/ast_printing/cprint.ml
index 3633b1c43ff50c5db28a42b111ddadfcdb753917..af3a8d8bc22123af81f03646bfb1e09484738eba 100644
--- a/src/kernel_services/ast_printing/cprint.ml
+++ b/src/kernel_services/ast_printing/cprint.ml
@@ -624,6 +624,9 @@ and print_def fmt def =
   | PRAGMA (a,_) ->
     fprintf fmt "@[#pragma %a@]@\n" print_expression a
 
+  | STATIC_ASSERT (e, s, _) ->
+    fprintf fmt "@[_Static_assert (%a, %s)@]@\n" print_expression e s
+
   | LINKAGE (n, _, dl) ->
     fprintf fmt "@[<2>extern@ %s@ {%a@;}@]" n (pp_list print_def) dl
 
diff --git a/src/kernel_services/parsetree/cabs.mli b/src/kernel_services/parsetree/cabs.mli
index 9294f5b774a046acd314a3f31fe882fd28e07fb2..bed5b26fa88f783bee4086049161e886a340f62f 100644
--- a/src/kernel_services/parsetree/cabs.mli
+++ b/src/kernel_services/parsetree/cabs.mli
@@ -169,6 +169,7 @@ and definition =
   | ONLYTYPEDEF of specifier * cabsloc
   | GLOBASM of string * cabsloc
   | PRAGMA of expression * cabsloc
+  | STATIC_ASSERT of expression * string * cabsloc
   | LINKAGE of string * cabsloc * definition list (* extern "C" { ... } *)
   | GLOBANNOT of Logic_ptree.decl list
   (** Logical declaration (axiom, logic, etc.)*)
diff --git a/src/kernel_services/parsetree/cabshelper.ml b/src/kernel_services/parsetree/cabshelper.ml
index 6ceeedb4f04e8a71592880ba5ac3cd10a2f01561..14b0e66acbc617f9be372a6d9cc3eeef6287c106 100644
--- a/src/kernel_services/parsetree/cabshelper.ml
+++ b/src/kernel_services/parsetree/cabshelper.ml
@@ -156,6 +156,7 @@ let get_definitionloc (d : definition) : cabsloc =
   | ONLYTYPEDEF(_, l) -> l
   | GLOBASM(_, l) -> l
   | PRAGMA(_, l) -> l
+  | STATIC_ASSERT (_, _, l) -> l
   | LINKAGE (_, l, _) -> l
   | GLOBANNOT({Logic_ptree.decl_loc = l }::_) -> l
   | GLOBANNOT [] -> assert false
diff --git a/src/kernel_services/visitors/cabsvisit.ml b/src/kernel_services/visitors/cabsvisit.ml
index 1783feb0a22c113b7f41dc73a786a520562b507c..626bd95333d302625f998deb0655dc1cd604f3e9 100644
--- a/src/kernel_services/visitors/cabsvisit.ml
+++ b/src/kernel_services/visitors/cabsvisit.ml
@@ -257,6 +257,9 @@ and childrenDefinition vis d =
   | PRAGMA (e, l) ->
     let e' = visitCabsExpression vis e in
     if e' != e then PRAGMA (e', l) else d
+  | STATIC_ASSERT (e, s, l) ->
+    let e' = visitCabsExpression vis e in
+    if e' != e then STATIC_ASSERT (e', s, l) else d
   | LINKAGE (n, l, dl) ->
     let dl' = mapNoCopyList (visitCabsDefinition vis) dl in
     if dl' != dl then LINKAGE (n, l, dl') else d
diff --git a/src/plugins/metrics/metrics_cabs.ml b/src/plugins/metrics/metrics_cabs.ml
index 5f7929d605735f42fd156d47c60b687ab3f8e295..315698ea973cbc168f0fd59e634f9b33257602a9 100644
--- a/src/plugins/metrics/metrics_cabs.ml
+++ b/src/plugins/metrics/metrics_cabs.ml
@@ -114,6 +114,7 @@ class metricsCabsVisitor = object(self)
     | ONLYTYPEDEF _
     | GLOBASM _
     | PRAGMA _
+    | STATIC_ASSERT _
     | LINKAGE _
     | CUSTOM _
     | GLOBANNOT _ -> Cil.DoChildren;
diff --git a/tests/syntax/oracle/static_assert.0.res.oracle b/tests/syntax/oracle/static_assert.0.res.oracle
new file mode 100644
index 0000000000000000000000000000000000000000..0a7afa853d393a80053219c13c434eb461c32f4c
--- /dev/null
+++ b/tests/syntax/oracle/static_assert.0.res.oracle
@@ -0,0 +1,21 @@
+[kernel] Parsing tests/syntax/static_assert.c (with preprocessing)
+/* Generated by Frama-C */
+#include "assert.h"
+_Static_assert(1, "string");
+_Static_assert(2);
+int main(void)
+{
+  int __retres;
+  _Static_assert(sizeof(int) > sizeof(char), "int must be greater than char");
+  
+  int ret = 42;
+  _Static_assert(3);
+  
+  __retres = ret;
+  goto return_label;
+  _Static_assert(4, "after return");
+  
+  return_label: return __retres;
+}
+
+
diff --git a/tests/syntax/oracle/static_assert.1.res.oracle b/tests/syntax/oracle/static_assert.1.res.oracle
new file mode 100644
index 0000000000000000000000000000000000000000..ccc5ea39b44fd70bb24ca71d78f991b6d0c654c1
--- /dev/null
+++ b/tests/syntax/oracle/static_assert.1.res.oracle
@@ -0,0 +1,10 @@
+[kernel] Parsing tests/syntax/static_assert.c (with preprocessing)
+[kernel] tests/syntax/static_assert.c:12: User Error: 
+  static assertion failed: fail
+[kernel] tests/syntax/static_assert.c:22: User Error: 
+  failed to evaluate constant expression in static assertion:
+  a == 42
+[kernel] tests/syntax/static_assert.c:35: User Error: static assertion failed
+[kernel] User Error: stopping on file "tests/syntax/static_assert.c" that has errors. Add
+  '-kernel-msg-key pp' for preprocessing command.
+[kernel] Frama-C aborted: invalid user input.
diff --git a/tests/syntax/static_assert.c b/tests/syntax/static_assert.c
new file mode 100644
index 0000000000000000000000000000000000000000..5d856c29e48456c03c34ee6c0aa796978997100f
--- /dev/null
+++ b/tests/syntax/static_assert.c
@@ -0,0 +1,36 @@
+/* run.config
+   STDOPT: #"-c11"
+   EXIT: 1
+   STDOPT: #"-c11 -cpp-extra-args=-DFAIL"
+*/
+
+_Static_assert(1, "string");
+
+#include <assert.h>
+
+#ifdef FAIL
+static_assert(0, "fail");
+#endif
+
+_Static_assert(2); // without message string
+
+int main() {
+  static_assert(sizeof(int) > sizeof(char), "int must be greater than char");
+
+#ifdef FAIL
+  int a = 42;
+  static_assert(a == 42, "non-static condition");
+#endif
+
+  int ret = 42;
+
+  static_assert(3); // between statements
+
+  return ret;
+
+  _Static_assert(4, "after return");
+}
+
+#ifdef FAIL
+static_assert(0); // fail without message string
+#endif