about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorrajdakin <rajdakin@gmail.com>2024-09-08 16:43:16 +0200
committerGitHub <noreply@github.com>2024-09-08 16:43:16 +0200
commitc87a56ef37e34f2f239a8df9e4d08afd0334ddaf (patch)
tree50b4c7fc6a0cdc6e2c30f16ed1747cc28d6616a3
parent2e04ff93cabdbdcef060ce5e40f544f4dd6f93bb (diff)
downloadbox64-c87a56ef37e34f2f239a8df9e4d08afd0334ddaf.tar.gz
box64-c87a56ef37e34f2f239a8df9e4d08afd0334ddaf.zip
[WRAPPERHELPER] Major enhancements in output quality, allow for generic type mapping (#1807)
-rwxr-xr-xwrapperhelper/Makefile2
-rw-r--r--wrapperhelper/README.md12
-rw-r--r--wrapperhelper/example-libc.h17
-rw-r--r--wrapperhelper/src/generator.c478
-rw-r--r--wrapperhelper/src/generator.h15
-rw-r--r--wrapperhelper/src/lang.c15
-rw-r--r--wrapperhelper/src/lang.h4
-rw-r--r--wrapperhelper/src/main.c23
-rw-r--r--wrapperhelper/src/parse.c111
-rw-r--r--wrapperhelper/src/preproc.c140
10 files changed, 483 insertions, 334 deletions
diff --git a/wrapperhelper/Makefile b/wrapperhelper/Makefile
index a4222bee..31a3e7b0 100755
--- a/wrapperhelper/Makefile
+++ b/wrapperhelper/Makefile
@@ -238,7 +238,7 @@ $(call wrapperhelper_o,,parse,parse): CFLAGS+= -fno-analyzer
 
 src/machine.gen:
 	$(call colorize,96,GEN,33,Generating $@)
-	$(SILENCER)echo | LC_ALL=C LANG=C $(CC) $(CPPFLAGS) -E -v - |& sed ':l; $$ ! { N; b l }; s/.*#include <...> search starts here:\n//; s/End of search list.*//; s/^ /DO_PATH("/; s/\n /")\nDO_PATH("/g; s/\n$$/")/' >src/machine.gen
+	$(SILENCER)echo | LC_ALL=C LANG=C $(CC) $(CPPFLAGS) -E -v - 2>&1 | sed ':l; $$ ! { N; b l }; s/.*#include <...> search starts here:\n//; s/End of search list.*//; s/^ /DO_PATH("/; s/\n /")\nDO_PATH("/g; s/\n$$/")/' >src/machine.gen
 
 #$(eval $(call compile_test_cxx,core/number))
 
diff --git a/wrapperhelper/README.md b/wrapperhelper/README.md
index c2599cdf..65754036 100644
--- a/wrapperhelper/README.md
+++ b/wrapperhelper/README.md
@@ -31,9 +31,11 @@ The first file is a `C` file containing every declaration required. The second f
 
 The support file may contain pragma declarations of the form
 ```c
-#pragma wrappers explicit_simple TYPE
+#pragma wrappers type-letters c TYPE
 ```
-where `TYPE` is a `typedef` to a structure. This marks the structure pointed to by `TYPE` as "simple", which means that functions taking such structures are not required to be `GOM`-like.
+where `TYPE` is a `type-name`. This marks the *exact* type `TYPE` as being a complex type though with a conversion as `c` (which may be multiple characters). Meaning:
+- if a parameter has type `TYPE`, the character output will be `c`;
+- if a parameter has a pointer to `TYPE`, or a structure containing `TYPE`, the output will be a `GOM` function.
 
 System headers included (directly or indirectly) by the support file are overriden by the files in `include-fixed`.
 
@@ -94,9 +96,7 @@ This project only works for `box64`; more work is required for this to be compat
 
 Only native structures are read. This means that the current version of `wrapperhelper` does not detect an issue when a structure has different members or alignments in two different architectures.
 
-Structure letters (i.e. `S` for `struct _IO_FILE*`) are hard-coded. A pragma should be used instead (`#pragma wrappers type_letter IDENT type-name`, parsed as `PTOK_PRAGMA: Type is letter: <char>`, followed by `type-name`, followed by `PTOK_NEWLINE`).
-
-Conditionals in the `_private.h` files are ignored, except for taking only the negative branch. Manual cleanup of the output is required.
+No checking of signatures under `#ifdef`s is made.
 
 Line numbers are missing entirely. For most errors, the corresponding file is not written with the error message.
 
@@ -114,8 +114,8 @@ The following features are missing from the preprocessor:
 - Proper out-of-memory error handling
 
 The following features are missing from the parser:
-- `inline` and `_Noreturn`
 - `_Atomic(type-name)`
 - `_Alignas(type-name)` and `_Alignas(constant-expression)`
 - `(type-name){initializer-list}`
+- Old style function declarations
 - Function definitions are ignored, not parsed
diff --git a/wrapperhelper/example-libc.h b/wrapperhelper/example-libc.h
index 81850c51..d17d475b 100644
--- a/wrapperhelper/example-libc.h
+++ b/wrapperhelper/example-libc.h
@@ -2,9 +2,6 @@
 #define __USE_MISC 1
 #define PORTMAP
 
-// TODO
-#define inline
-
 #include <stdint.h>
 
 #include <aliases.h>
@@ -132,7 +129,13 @@
 #include <wctype.h>
 #include <wordexp.h>
 
-#pragma wrappers explicit_simple FTS
-#pragma wrappers explicit_simple FTS64
-#pragma wrappers explicit_simple glob_t
-#pragma wrappers explicit_simple glob64_t
+#pragma wrappers type_letters S FILE*
+#pragma wrappers type_letters S const FILE*
+#pragma wrappers type_letters p FTS*
+#pragma wrappers type_letters p const FTS*
+#pragma wrappers type_letters p FTS64*
+#pragma wrappers type_letters p const FTS64*
+#pragma wrappers type_letters p glob_t*
+#pragma wrappers type_letters p const glob_t*
+#pragma wrappers type_letters p glob64_t*
+#pragma wrappers type_letters p const glob64_t*
diff --git a/wrapperhelper/src/generator.c b/wrapperhelper/src/generator.c
index 98e254ed..e162d0e4 100644
--- a/wrapperhelper/src/generator.c
+++ b/wrapperhelper/src/generator.c
@@ -6,149 +6,173 @@
 static const char *rft2str[8] = {
 	[RQT_FUN] = "",
 	[RQT_FUN_2] = "",
-	[RQT_FUN_MY] = "(my) ",
-	[RQT_FUN_D] = "(D) ",
+	[RQT_FUN_MY] = " (my)",
+	[RQT_FUN_D] = " (D)",
 	[RQT_DATA] = "",
-	[RQT_DATAV] = "(V) ",
-	[RQT_DATAB] = "(B) ",
-	[RQT_DATAM] = "(my) ",
+	[RQT_DATAV] = " (V)",
+	[RQT_DATAB] = " (B)",
+	[RQT_DATAM] = " (my)",
 };
 #define IS_RQT_FUN2(rty) (((rty) == RQT_FUN_2) || ((rty) == RQT_FUN_D))
 #define IS_RQT_FUNCTION(rty) ((rty) < RQT_DATA)
 
-void request_print(request_t *req) {
-	printf("%s", string_content(req->obj_name));
-	if (req->has_default && req->has_val && (IS_RQT_FUNCTION(req->def.rty) != IS_RQT_FUNCTION(req->val.rty))) {
-		printf(" => conflict: was/is data, is/was function\n");
-	}
-	int is_fun;
-	if (req->has_default) is_fun = IS_RQT_FUNCTION(req->def.rty);
-	else if (req->has_val) is_fun = IS_RQT_FUNCTION(req->val.rty);
-	else {
-		printf(" => (no value)\n");
-		return;
-	}
-	
-	if (is_fun) {
-		printf(" => %sfunction", req->weak ? "weak " : "");
-		if (req->has_default) {
-			printf(" with %sdefault %s%s%s%s",
-				req->default_comment ? "commented " : "",
+void request_print(const request_t *req) {
+	printf("%s%s: %sdefault", string_content(req->obj_name), req->weak ? " (weak)" : "", req->default_comment ? "commented " : "");
+	switch (req->def.rty) {
+	case RQT_FUN:
+	case RQT_FUN_2:
+	case RQT_FUN_MY:
+	case RQT_FUN_D:
+		if (req->def.fun.typ) {
+			printf(" function%s %s%s%s",
 				rft2str[req->def.rty],
 				string_content(req->def.fun.typ),
-				(req->def.rty == RQT_FUN_2) ? " -> " : "",
-				(req->def.rty == RQT_FUN_2) ? string_content(req->def.fun.fun2) : "");
-		}
-		if (req->has_val) {
-			printf(" with solved %s%s%s%s",
-				rft2str[req->val.rty],
-				string_content(req->val.fun.typ),
-				(req->val.rty == RQT_FUN_2) ? " -> " : "",
-				(req->val.rty == RQT_FUN_2) ? string_content(req->val.fun.fun2) : "");
+				req->def.fun.fun2 ? " -> " : "",
+				req->def.fun.fun2 ? string_content(req->def.fun.fun2) : "");
+		} else {
+			printf(" untyped function%s", rft2str[req->def.rty]);
 		}
-	} else {
-		printf(" => %sdata", req->weak ? "weak " : "");
-		if (req->has_default) {
-			if (req->def.dat.has_size) printf(" with default %zu", req->def.dat.sz);
-			else printf(" with no default");
+		break;
+	case RQT_DATA:
+	case RQT_DATAV:
+	case RQT_DATAB:
+	case RQT_DATAM:
+		if (req->def.dat.has_size) {
+			printf(" data%s %zu", rft2str[req->def.rty], req->def.dat.sz);
+		} else {
+			printf(" unsized data%s", rft2str[req->def.rty]);
 		}
-		if (req->has_val) {
-			if (req->def.dat.has_size) printf(" with solved %zu", req->def.dat.sz);
-			else printf(" with no solved");
+		break;
+	}
+	if (req->has_val) {
+		printf(" => solved");
+		switch (req->val.rty) {
+		case RQT_FUN:
+		case RQT_FUN_2:
+		case RQT_FUN_MY:
+		case RQT_FUN_D:
+			if (req->val.fun.typ) {
+				printf(" function%s %s%s%s",
+					rft2str[req->val.rty],
+					string_content(req->val.fun.typ),
+					req->val.fun.fun2 ? " -> " : "",
+					req->val.fun.fun2 ? string_content(req->val.fun.fun2) : "");
+			} else {
+				printf(" untyped function%s", rft2str[req->val.rty]);
+			}
+			break;
+		case RQT_DATA:
+		case RQT_DATAV:
+		case RQT_DATAB:
+		case RQT_DATAM:
+			if (req->val.dat.has_size) {
+				printf(" data%s %zu", rft2str[req->val.rty], req->val.dat.sz);
+			} else {
+				printf(" unsized data%s", rft2str[req->val.rty]);
+			}
+			break;
 		}
 	}
 	printf("\n");
 }
-void request_print_check(request_t *req) {
-	if (req->has_default && req->has_val && (IS_RQT_FUNCTION(req->def.rty) != IS_RQT_FUNCTION(req->val.rty))) {
-		printf("%s => conflict: was/is data, is/was function\n", string_content(req->obj_name));
+void request_print_check(const request_t *req) {
+	if (req->ignored) {
+		return;
 	}
-	int is_fun;
-	if (req->has_default) is_fun = IS_RQT_FUNCTION(req->def.rty);
-	else if (req->has_val) is_fun = IS_RQT_FUNCTION(req->val.rty);
-	else {
-		printf(" => (no value)\n");
+	if (!req->has_val) {
+		// printf("%s: no value\n", string_content(req->obj_name));
 		return;
 	}
-	if (is_fun) {
-		if (req->has_val) {
-			if (req->has_default) {
-				int similar = 1;
-				// if (req->def.rty != req->val.rty) similar = 0;
-				if (similar && strcmp(string_content(req->def.fun.typ), string_content(req->val.fun.typ))) {
-					// similar = 0;
-					// TODO
-					if (req->def.rty == RQT_FUN_MY) {
-						similar =
-							string_len(req->def.fun.typ) >= 3 &&
-							string_content(req->def.fun.typ)[2] == 'E' &&
-							!strncmp(string_content(req->def.fun.typ), string_content(req->val.fun.typ), 2) &&
-							!strcmp(string_content(req->def.fun.typ) + 3, string_content(req->val.fun.typ) + 2);
-						if (similar) {
-							// We need to add the 'E' back here
-						}
-					} else {
-						similar = 0;
-					}
-				}
-				if (!similar) {
-					printf("%s => %sfunction with %sdefault %s%s%s%s and different solved %s%s%s%s\n",
-						string_content(req->obj_name),
-						req->weak ? "weak " : "",
-						req->default_comment ? "commented " : "",
-						rft2str[req->def.rty],
-						string_content(req->def.fun.typ),
-						(req->def.rty == RQT_FUN_2) ? " -> " : "",
-						(req->def.rty == RQT_FUN_2) ? string_content(req->def.fun.fun2) : "",
-						rft2str[req->val.rty],
-						string_content(req->val.fun.typ),
-						(req->val.rty == RQT_FUN_2) ? " -> " : "",
-						(req->val.rty == RQT_FUN_2) ? string_content(req->val.fun.fun2) : "");
-				}
-			} else {
-				printf("%s => %sfunction with solved %s%s%s%s\n",
-					string_content(req->obj_name),
-					req->weak ? "weak " : "",
-					rft2str[req->val.rty],
-					string_content(req->val.fun.typ),
-					(req->val.rty == RQT_FUN_2) ? " -> " : "",
-					(req->val.rty == RQT_FUN_2) ? string_content(req->val.fun.fun2) : "");
-			}
-		} else if (req->has_default) {
-			/* printf("%s => unsolved %sfunction with %sdefault %s%s%s%s\n",
+	if ((IS_RQT_FUNCTION(req->def.rty) != IS_RQT_FUNCTION(req->val.rty))) {
+		printf("%s: conflict: %s data, %s function\n",
+			string_content(req->obj_name),
+			IS_RQT_FUNCTION(req->def.rty) ? "is" : "was",
+			IS_RQT_FUNCTION(req->def.rty) ? "was" : "is");
+		return;
+	}
+	if (IS_RQT_FUNCTION(req->def.rty) && !req->def.fun.typ) return;       // No default (function)
+	if (!IS_RQT_FUNCTION(req->def.rty) && !req->def.dat.has_size) return; // No default (data)
+	// We have a default and a value, both are functions or data
+	int similar;
+	switch (req->def.rty) {
+	case RQT_FUN:
+	case RQT_FUN_2:
+	case RQT_FUN_MY:
+	case RQT_FUN_D:
+		similar = !req->default_comment || (req->val.rty != RQT_FUN); // From comment to no comment is dissimilar
+		if (similar && (req->def.rty != req->val.rty)) similar = 0;
+		if (similar && strcmp(string_content(req->def.fun.typ), string_content(req->val.fun.typ))) {
+			similar = 0;
+		}
+		if (!similar) {
+			printf("%s%s: function with %s%sdefault%s%s%s%s%s and dissimilar %ssolved%s%s%s%s%s\n",
 				string_content(req->obj_name),
-				req->weak ? "weak " : "",
+				req->weak ? " (weak)" : "",
 				req->default_comment ? "commented " : "",
+				req->def.fun.typ ? "" : "untyped ",
 				rft2str[req->def.rty],
+				req->def.fun.typ ? " " : "",
 				string_content(req->def.fun.typ),
-				(req->def.rty == RQT_FUN_2) ? " -> " : "",
-				(req->def.rty == RQT_FUN_2) ? string_content(req->def.fun.fun2) : ""); */
-		}
-	} else {
-		printf("%s => %sdata", string_content(req->obj_name), req->weak ? "weak " : "");
-		if (req->has_default) {
-			if (req->def.dat.has_size) printf(" with default %zu", req->def.dat.sz);
-			else printf(" with no default");
+				req->def.fun.fun2 ? " -> " : "",
+				req->def.fun.fun2 ? string_content(req->def.fun.fun2) : "",
+				req->val.fun.typ ? "" : "untyped ",
+				rft2str[req->val.rty],
+				req->val.fun.typ ? " " : "",
+				string_content(req->val.fun.typ),
+				req->val.fun.fun2 ? " -> " : "",
+				req->val.fun.fun2 ? string_content(req->val.fun.fun2) : "");
 		}
-		if (req->has_val) {
-			if (req->def.dat.has_size) printf(" with solved %zu", req->def.dat.sz);
-			else printf(" with no solved");
+		break;
+	case RQT_DATA:
+	case RQT_DATAV:
+	case RQT_DATAB:
+	case RQT_DATAM:
+		similar = 1;
+		if (similar && (req->def.rty != req->val.rty)) similar = 0;
+		if (similar && (!!req->def.dat.has_size != !!req->val.dat.has_size)) similar = 0;
+		if (similar && req->def.dat.has_size && (req->def.dat.sz != req->val.dat.sz)) similar = 0;
+		if (!similar) {
+			printf("%s%s: data with %s%sdefault%s",
+				string_content(req->obj_name),
+				req->weak ? " (weak)" : "",
+				req->default_comment ? "commented " : "",
+				req->def.fun.typ ? "" : "untyped ",
+				rft2str[req->def.rty]);
+			if (req->def.dat.has_size) {
+				printf(" %zu", req->def.dat.sz);
+			} else {
+				printf(" unsized");
+			}
+			printf(" and dissimilar %ssolved%s",
+				req->val.fun.typ ? "" : "untyped ",
+				rft2str[req->val.rty]);
+			if (req->val.dat.has_size) {
+				printf(" %zu", req->val.dat.sz);
+			} else {
+				printf(" unsized");
+			}
+			printf("\n");
 		}
+		break;
+	}
+}
+void references_print_check(const VECTOR(references) *refs) {
+	vector_for(references, ref, refs) {
+		if (ref->typ == REF_REQ) request_print_check(&ref->req);
 	}
 }
+
 static void request_del(request_t *req) {
 	string_del(req->obj_name);
-	if (req->has_default) {
-		switch (req->def.rty) {
-		case RQT_FUN:    string_del(req->def.fun.typ);                                break;
-		case RQT_FUN_2:  string_del(req->def.fun.typ); string_del(req->def.fun.fun2); break;
-		case RQT_FUN_MY: string_del(req->def.fun.typ);                                break;
-		case RQT_FUN_D:  string_del(req->def.fun.typ); string_del(req->def.fun.fun2); break;
-		case RQT_DATA:   break;
-		case RQT_DATAV:  break;
-		case RQT_DATAB:  break;
-		case RQT_DATAM:  break;
-		}
+	switch (req->def.rty) {
+	case RQT_FUN:    if (req->def.fun.typ) string_del(req->def.fun.typ);                                                       break;
+	case RQT_FUN_2:  if (req->def.fun.typ) string_del(req->def.fun.typ); if (req->def.fun.fun2) string_del(req->def.fun.fun2); break;
+	case RQT_FUN_MY: if (req->def.fun.typ) string_del(req->def.fun.typ);                                                       break;
+	case RQT_FUN_D:  if (req->def.fun.typ) string_del(req->def.fun.typ); if (req->def.fun.fun2) string_del(req->def.fun.fun2); break;
+	case RQT_DATA:   break;
+	case RQT_DATAV:  break;
+	case RQT_DATAB:  break;
+	case RQT_DATAM:  break;
 	}
 	if (req->has_val) {
 		switch (req->val.rty) {
@@ -169,8 +193,13 @@ static void reference_del(reference_t *ref) {
 		request_del(&ref->req);
 		break;
 	case REF_LINE:
+	case REF_IFDEF:
+	case REF_IFNDEF:
 		string_del(ref->line);
 		break;
+	case REF_ELSE:
+	case REF_ENDIF:
+		break;
 	}
 }
 
@@ -194,31 +223,33 @@ static const char *rqt_suffix[8] = {
 	[RQT_DATAM] = "M",
 };
 
-static void request_output(FILE *f, request_t *req) {
+static void request_output(FILE *f, const request_t *req) {
 	if (!req->has_val) {
-		if (!req->has_default) {
-			// printf("Warning: %s has no value and no default, assuming function\n", string_content(req->obj_name));
-			fprintf(f, "//GO%s(%s, \n", req->weak ? "W" : "", string_content(req->obj_name));
-		} else if (IS_RQT_FUNCTION(req->def.rty)) {
-			fprintf(f, "%sGO%s%s(%s, %s%s%s%s%s)%s\n",
-				req->default_comment ? "//" : "",
-				req->weak ? "W" : "",
-				rqt_suffix[req->def.rty],
-				string_content(req->obj_name),
-				valid_reqtype(req->def.fun.typ) ? "" : "\"",
-				string_content(req->def.fun.typ),
-				valid_reqtype(req->def.fun.typ) ? "" : "\"",
-				IS_RQT_FUN2(req->def.rty) ? ", " : "",
-				IS_RQT_FUN2(req->def.rty) ? string_content(req->def.fun.fun2) : "",
-				req->default_comment ? "" : " // Warning: failed to confirm");
+		if (IS_RQT_FUNCTION(req->def.rty)) {
+			if (!req->def.fun.typ) {
+				fprintf(f, "//GO%s%s(%s, \n", req->weak ? "W" : "", rqt_suffix[req->def.rty], string_content(req->obj_name));
+			} else {
+				fprintf(f, "%sGO%s%s(%s, %s%s%s%s%s)%s\n",
+					req->default_comment ? "//" : "",
+					req->weak ? "W" : "",
+					rqt_suffix[req->def.rty],
+					string_content(req->obj_name),
+					valid_reqtype(req->def.fun.typ) ? "" : "\"",
+					string_content(req->def.fun.typ),
+					valid_reqtype(req->def.fun.typ) ? "" : "\"",
+					IS_RQT_FUN2(req->def.rty) ? ", " : "",
+					IS_RQT_FUN2(req->def.rty) ? string_content(req->def.fun.fun2) : "",
+					(req->ignored || req->default_comment) ? "" : " // Warning: failed to confirm");
+			}
 		} else {
 			if (req->def.dat.has_size) {
-				fprintf(f, "%sDATA%s%s(%s, %zu) // Warning: failed to confirm\n",
+				fprintf(f, "%sDATA%s%s(%s, %zu)%s\n",
 					req->default_comment ? "//" : "",
 					req->weak ? "W" : "",
 					rqt_suffix[req->def.rty],
 					string_content(req->obj_name),
-					req->def.dat.sz);
+					req->def.dat.sz,
+					(req->ignored || req->default_comment) ? "" : " // Warning: failed to confirm");
 			} else {
 				fprintf(f, "//DATA%s%s(%s, \n",
 					req->weak ? "W" : "",
@@ -229,7 +260,8 @@ static void request_output(FILE *f, request_t *req) {
 	} else {
 		if (IS_RQT_FUNCTION(req->val.rty)) {
 			int is_comment =
-				(req->has_default && !req->default_comment) ? (req->val.rty != req->def.rty) : (req->val.rty != RQT_FUN);
+				(IS_RQT_FUNCTION(req->def.rty) && req->def.fun.typ && !req->default_comment)
+				  ? (req->val.rty != req->def.rty) : (req->val.rty != RQT_FUN);
 			fprintf(f, "%sGO%s%s(%s, %s%s%s)\n",
 				is_comment ? "//" : "",
 				req->weak ? "W" : "",
@@ -240,7 +272,7 @@ static void request_output(FILE *f, request_t *req) {
 				IS_RQT_FUN2(req->val.rty) ? req->val.fun.fun2 ? string_content(req->val.fun.fun2) : "<error: no val>" : "");
 		} else {
 			if (req->val.dat.has_size) {
-				int is_comment = !req->has_default || req->default_comment || (req->def.rty != req->val.rty);
+				int is_comment = IS_RQT_FUNCTION(req->def.rty) || !req->def.dat.has_size || req->default_comment || (req->def.rty != req->val.rty);
 				fprintf(f, "%sDATA%s(%s, %zu)\n",
 					is_comment ? "//" : "",
 					rqt_suffix[req->val.rty],
@@ -254,7 +286,7 @@ static void request_output(FILE *f, request_t *req) {
 		}
 	}
 }
-static void reference_output(FILE *f, reference_t *ref) {
+static void reference_output(FILE *f, const reference_t *ref) {
 	switch (ref->typ) {
 	case REF_REQ:
 		request_output(f, &ref->req);
@@ -263,12 +295,24 @@ static void reference_output(FILE *f, reference_t *ref) {
 		fputs(string_content(ref->line), f);
 		fputc('\n', f);
 		break;
+	case REF_IFDEF:
+		fprintf(f, "#ifdef %s\n", string_content(ref->line));
+		break;
+	case REF_IFNDEF:
+		fprintf(f, "#ifndef %s\n", string_content(ref->line));
+		break;
+	case REF_ELSE:
+		fputs("#else\n", f);
+		break;
+	case REF_ENDIF:
+		fputs("#endif\n", f);
+		break;
 	}
 }
-void output_from_references(FILE *f, VECTOR(references) *reqs) {
-	fprintf(f, "#if !(defined(GO) && defined(GOM) && defined(GO2) && defined(DATA))\n#error Meh...\n#endif\n\n");
-	vector_for(references, req, reqs) {
-		reference_output(f, req);
+void output_from_references(FILE *f, const VECTOR(references) *refs) {
+	fprintf(f, "#if !(defined(GO) && defined(GOM) && defined(GO2) && defined(DATA))\n#error Meh...\n#endif\n");
+	vector_for(references, ref, refs) {
+		reference_output(f, ref);
 	}
 }
 
@@ -350,14 +394,13 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 			}
 		}
 		if ((tok.tokt == PPTOK_SYM) && (tok.tokv.sym == SYM_HASH)) {
-			ADD_CHAR('#', 0, "start of preprocessor command")
+			string_clear(line);
 			tok = pre_next_token(prep, 0);
 			if (tok.tokt != PPTOK_IDENT) {
 				printf("Error: invalid reference file: invalid preprocessor line\n");
 				preproc_token_del(&tok);
 				goto failed;
 			}
-			ADD_STR(tok.tokv.str, 1, "preprocessor command")
 			if (!strcmp(string_content(tok.tokv.str), "ifdef")) {
 				string_del(tok.tokv.str);
 				tok = pre_next_token(prep, 0);
@@ -366,10 +409,12 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 					preproc_token_del(&tok);
 					goto failed;
 				}
-				ADD_CHAR(' ', 1, "preprocessor command")
-				ADD_STR(tok.tokv.str, 1, "preprocessor command")
 				++if_depth;
-				string_del(tok.tokv.str);
+				if (!vector_push(references, ret, ((reference_t){.typ = REF_IFDEF, .line = tok.tokv.str}))) {
+					printf("Error: failed to memorize reference line %d\n", lineno);
+					string_del(tok.tokv.str);
+					goto failed;
+				}
 				tok = pre_next_token(prep, 0);
 			} else if (!strcmp(string_content(tok.tokv.str), "ifndef")) {
 				string_del(tok.tokv.str);
@@ -379,22 +424,32 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 					preproc_token_del(&tok);
 					goto failed;
 				}
-				ADD_CHAR(' ', 1, "preprocessor command")
-				ADD_STR(tok.tokv.str, 1, "preprocessor command")
 				if (if_depth == entered_depth) ++entered_depth;
 				++if_depth;
-				string_del(tok.tokv.str);
+				if (!vector_push(references, ret, ((reference_t){.typ = REF_IFNDEF, .line = tok.tokv.str}))) {
+					printf("Error: failed to memorize reference line %d\n", lineno);
+					string_del(tok.tokv.str);
+					goto failed;
+				}
 				tok = pre_next_token(prep, 0);
 			} else if (!strcmp(string_content(tok.tokv.str), "else")) {
 				string_del(tok.tokv.str);
 				tok = pre_next_token(prep, 0);
 				if (if_depth == entered_depth + 1) ++entered_depth;
 				else if (if_depth == entered_depth) --entered_depth;
+				if (!vector_push(references, ret, ((reference_t){.typ = REF_ELSE}))) {
+					printf("Error: failed to memorize reference line %d\n", lineno);
+					goto failed;
+				}
 			} else if (!strcmp(string_content(tok.tokv.str), "endif")) {
 				string_del(tok.tokv.str);
 				tok = pre_next_token(prep, 0);
 				if (if_depth == entered_depth) --entered_depth;
 				--if_depth;
+				if (!vector_push(references, ret, ((reference_t){.typ = REF_ENDIF}))) {
+					printf("Error: failed to memorize reference line %d\n", lineno);
+					goto failed;
+				}
 			} else {
 				printf("Error: invalid reference file: invalid preprocessor command '%s'\n", string_content(tok.tokv.str));
 				string_del(tok.tokv.str);
@@ -404,7 +459,6 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 				preproc_token_del(&tok);
 				tok = pre_next_token(prep, 0);
 			}
-			PUSH_LINE(0)
 			++lineno;
 			if (preproc_token_isend(&tok)) {
 				if (tok.tokt == PPTOK_EOF) goto success;
@@ -431,9 +485,9 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 			if (is_comment) prepare_mark_nocomment(prep);
 			int isweak = (string_content(tok.tokv.str)[2] == 'W');
 			request_t req = {
-				.has_default = 0,
 				.default_comment = is_comment,
 				.has_val = 0,
+				.ignored = 0,
 				.obj_name = NULL,
 				.weak = isweak,
 				.def = {
@@ -472,7 +526,6 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 			tok = pre_next_token(prep, 0);
 			if ((tok.tokt == PPTOK_IDENT) || (tok.tokt == PPTOK_STRING)) {
 				req.def.fun.typ = (tok.tokt == PPTOK_STRING) ? tok.tokv.sstr : tok.tokv.str;
-				req.has_default = 1;
 				// Token moved
 				tok = pre_next_token(prep, 0);
 				if ((req.def.rty == RQT_FUN_2) || (req.def.rty == RQT_FUN_D)) {
@@ -515,17 +568,13 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 				preproc_token_del(&tok);
 				goto failed;
 			}
-			if (if_depth == entered_depth) {
-				if (!vector_push(references, ret, ((reference_t){.typ = REF_REQ, .req = req}))) {
-					printf("Error: failed to add reference for %s\n", string_content(req.obj_name));
-					string_del(req.obj_name);
-					if (req.def.fun.typ) string_del(req.def.fun.typ);
-					if (req.def.fun.fun2) string_del(req.def.fun.fun2);
-					// Empty destructor
-					goto failed;
-				}
-			} else {
-				request_del(&req);
+			if (!vector_push(references, ret, ((reference_t){.typ = REF_REQ, .req = req}))) {
+				printf("Error: failed to add reference for %s\n", string_content(req.obj_name));
+				string_del(req.obj_name);
+				if (req.def.fun.typ) string_del(req.def.fun.typ);
+				if (req.def.fun.fun2) string_del(req.def.fun.fun2);
+				// Empty destructor
+				goto failed;
 			}
 			++lineno;
 		} else if ((tok.tokt == PPTOK_IDENT)
@@ -536,9 +585,9 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 			string_clear(line);
 			if (is_comment) prepare_mark_nocomment(prep);
 			request_t req = {
-				.has_default = 1,
 				.default_comment = is_comment,
 				.has_val = 0,
+				.ignored = 0,
 				.obj_name = NULL,
 				.weak = (string_content(tok.tokv.str)[4] == 'V'),
 				.def = {
@@ -614,15 +663,11 @@ VECTOR(references) *references_from_file(const char *filename, FILE *f) {
 				preproc_token_del(&tok);
 				goto failed;
 			}
-			if (if_depth == entered_depth) {
-				if (!vector_push(references, ret, ((reference_t){.typ = REF_REQ, .req = req}))) {
-					printf("Error: failed to add reference for %s\n", string_content(req.obj_name));
-					request_del(&req);
-					// Empty destructor
-					goto failed;
-				}
-			} else {
+			if (!vector_push(references, ret, ((reference_t){.typ = REF_REQ, .req = req}))) {
+				printf("Error: failed to add reference for %s\n", string_content(req.obj_name));
 				request_del(&req);
+				// Empty destructor
+				goto failed;
 			}
 			++lineno;
 		} else if (is_comment) {
@@ -663,6 +708,10 @@ success:
 }
 
 static int is_simple_type_ptr_to(type_t *typ, int *needs_D, int *needs_my) {
+	if (typ->converted) {
+		// printf("Warning: %s uses a converted type but is not the converted type\n", string_content(obj_name));
+		*needs_my = 1;
+	}
 	switch (typ->typ) {
 	case TYPE_BUILTIN:
 		return 1; // Assume pointers to builtin are simple
@@ -670,7 +719,6 @@ static int is_simple_type_ptr_to(type_t *typ, int *needs_D, int *needs_my) {
 		if (typ->val.array.array_sz == (size_t)-1) return 0; // VLA are not simple
 		return is_simple_type_ptr_to(typ->val.array.typ, needs_D, needs_my);
 	case TYPE_STRUCT_UNION:
-		if (typ->val.st->explicit_simple) return 1;
 		if (typ->_internal_use) return 1; // Recursive structures are OK as long as every other members are OK
 		if (!typ->val.st->is_defined) return 1; // Undefined structures are OK since they are opaque
 		typ->_internal_use = 1;
@@ -696,6 +744,10 @@ static int is_simple_type_ptr_to(type_t *typ, int *needs_D, int *needs_my) {
 	}
 }
 static int is_simple_type(type_t *typ, int *needs_D, int *needs_my) {
+	if (typ->converted) {
+		// printf("Warning: %s uses a converted type but is not the converted type\n", string_content(obj_name));
+		*needs_my = 1;
+	}
 	switch (typ->typ) {
 	case TYPE_BUILTIN:
 		return 1; // Assume pointers to builtin are simple
@@ -703,7 +755,6 @@ static int is_simple_type(type_t *typ, int *needs_D, int *needs_my) {
 		if (typ->val.array.array_sz == (size_t)-1) return 0; // VLA are not simple
 		return is_simple_type_ptr_to(typ->val.array.typ, needs_D, needs_my);
 	case TYPE_STRUCT_UNION:
-		if (typ->val.st->explicit_simple) return 1;
 		if (typ->_internal_use) return 1; // Recursive structures are OK as long as every other members are OK
 		// if (!typ->val.st->is_defined) return 1; // Undefined structures are OK since they are opaque
 		// To be safe, don't allow opaque structures
@@ -732,6 +783,13 @@ static int is_simple_type(type_t *typ, int *needs_D, int *needs_my) {
 }
 
 static int convert_type(string_t *dest, type_t *typ, int is_ret, int *needs_D, int *needs_my, string_t *obj_name) {
+	if (typ->converted) {
+		if (!string_add_string(dest, typ->converted)) {
+			printf("Error: failed to add explicit type char\n");
+			return 0;
+		}
+		return 1;
+	}
 	if (typ->is_atomic) {
 		printf("TODO: convert_type for atomic types\n");
 		return 0;
@@ -831,13 +889,6 @@ static int convert_type(string_t *dest, type_t *typ, int is_ret, int *needs_D, i
 	case TYPE_ENUM:
 		return convert_type(dest, typ->val.typ, is_ret, needs_D, needs_my, obj_name);
 	case TYPE_PTR:
-		if ((typ->val.typ->typ == TYPE_STRUCT_UNION) && typ->val.typ->val.st->tag && !strcmp(string_content(typ->val.typ->val.st->tag), "_IO_FILE")) {
-			if (!string_add_char(dest, 'S')) {
-				printf("Error: failed to add type char for %s\n", builtin2str[typ->val.builtin]);
-				return 0;
-			}
-			return 1;
-		}
 		if (is_simple_type_ptr_to(typ->val.typ, needs_D, needs_my)) {
 			if (!string_add_char(dest, 'p')) {
 				printf("Error: failed to add type char for simple pointer\n");
@@ -861,6 +912,7 @@ static int convert_type(string_t *dest, type_t *typ, int is_ret, int *needs_D, i
 	}
 }
 static int convert_type_post(string_t *dest, type_t *typ, string_t *obj_name) {
+	if (typ->converted) return 1;
 	if (typ->is_atomic) {
 		printf("TODO: convert_type_post for atomic types\n");
 		return 0;
@@ -893,7 +945,7 @@ static int convert_type_post(string_t *dest, type_t *typ, string_t *obj_name) {
 
 int solve_request(request_t *req, type_t *typ) {
 	if (typ->typ == TYPE_FUNCTION) {
-		int needs_D = 0, needs_my = req->has_default && (req->def.rty == RQT_FUN_MY), needs_2 = 0;
+		int needs_D = 0, needs_my = req->def.fun.typ && (req->def.rty == RQT_FUN_MY), needs_2 = 0;
 		int convert_post;
 		req->val.fun.typ = string_new();
 		if (!req->val.fun.typ) {
@@ -905,7 +957,7 @@ int solve_request(request_t *req, type_t *typ) {
 			printf("Error: failed to add convention char\n");
 			goto fun_fail;
 		}
-		if (req->has_default && (req->def.rty == RQT_FUN_MY) && (string_content(req->def.fun.typ)[2] == 'E')) {
+		if (req->def.fun.typ && (req->def.rty == RQT_FUN_MY) && (string_content(req->def.fun.typ)[2] == 'E')) {
 			if (!string_add_char(req->val.fun.typ, 'E')) {
 				printf("Error: failed to add emu char\n");
 				goto fun_fail;
@@ -933,23 +985,36 @@ int solve_request(request_t *req, type_t *typ) {
 				if (!convert_type(req->val.fun.typ, typ->val.fun.args[i], 0, &needs_D, &needs_my, req->obj_name)) goto fun_fail;
 			}
 			if (typ->val.fun.has_varargs) {
-				needs_my = 1;
-				if (!string_add_char(req->val.fun.typ, 'V')) {
-					printf("Error: failed to add type char for %s\n", builtin2str[typ->val.builtin]);
-					goto fun_fail;
+				if (req->def.fun.typ
+				      && (string_len(req->def.fun.typ) == string_len(req->val.fun.typ) + 1)
+				      && !strncmp(string_content(req->def.fun.typ), string_content(req->val.fun.typ), string_len(req->val.fun.typ))
+				      && ((string_content(req->def.fun.typ)[string_len(req->val.fun.typ)] == 'M')
+				       || (string_content(req->def.fun.typ)[string_len(req->val.fun.typ)] == 'N'))) {
+					if (!string_add_char(req->val.fun.typ, string_content(req->def.fun.typ)[string_len(req->val.fun.typ)])) {
+						printf("Error: failed to add type char '%c' for %s\n",
+							string_content(req->def.fun.typ)[string_len(req->val.fun.typ)],
+							builtin2str[typ->val.builtin]);
+						goto fun_fail;
+					}
+				} else {
+					needs_my = 1;
+					if (!string_add_char(req->val.fun.typ, 'V')) {
+						printf("Error: failed to add type char 'V' for %s\n", builtin2str[typ->val.builtin]);
+						goto fun_fail;
+					}
 				}
 			}
 		}
 		
 	// fun_succ:
-		if (req->has_default && (req->def.rty == RQT_FUN_2) && !needs_my) {
+		if (req->def.fun.typ && (req->def.rty == RQT_FUN_2) && !needs_my) {
 			needs_2 = 1;
 			req->val.fun.fun2 = string_dup(req->def.fun.fun2);
 			if (!req->val.fun.fun2) {
 				printf("Error: failed to duplicate string (request for function %s with default redirection)\n", string_content(req->obj_name));
 				return 0;
 			}
-		} else if (req->has_default && (req->def.rty == RQT_FUN_D) && !needs_my) {
+		} else if (req->def.fun.typ && (req->def.rty == RQT_FUN_D) && !needs_my) {
 			needs_2 = 0;
 			req->val.fun.fun2 = string_dup(req->def.fun.fun2);
 			if (!req->val.fun.fun2) {
@@ -974,10 +1039,10 @@ int solve_request(request_t *req, type_t *typ) {
 		string_del(req->val.fun.typ);
 		return 0;
 	} else {
-		int needs_D = 0, needs_my = req->has_default && (req->def.rty == RQT_FUN_MY);
+		int needs_D = 0, needs_my = req->def.dat.has_size && (req->def.rty == RQT_DATAM);
 		if (is_simple_type(typ, &needs_D, &needs_my)) {
 			// TODO: Hmm...
-			req->val.rty = needs_my ? RQT_DATAM : req->has_default ? req->def.rty : req->weak ? RQT_DATAV : RQT_DATA;
+			req->val.rty = needs_my ? RQT_DATAM : req->def.dat.has_size ? req->def.rty : req->weak ? RQT_DATAV : RQT_DATA;
 			req->val.dat.has_size = 1;
 			req->val.dat.sz = typ->szinfo.size;
 			req->has_val = 1;
@@ -1002,9 +1067,34 @@ int solve_request_map(request_t *req, khash_t(type_map) *decl_map) {
 }
 int solve_references(VECTOR(references) *refs, khash_t(type_map) *decl_map) {
 	int ret = 1;
+	int cond_depth = 0, ok_depth = 0;
 	vector_for(references, ref, refs) {
-		if (ref->typ != REF_REQ) continue;
-		if (!solve_request_map(&ref->req, decl_map)) ret = 0;
+		switch (ref->typ) {
+		case REF_REQ:
+			if (ok_depth == cond_depth) {
+				if (!solve_request_map(&ref->req, decl_map)) ret = 0;
+			} else {
+				ref->req.ignored = 1;
+			}
+			break;
+		case REF_LINE:
+			break;
+		case REF_IFDEF:
+			++cond_depth;
+			break;
+		case REF_IFNDEF:
+			if (cond_depth == ok_depth) ++ok_depth;
+			++cond_depth;
+			break;
+		case REF_ELSE:
+			if (cond_depth == ok_depth) --ok_depth;
+			else if (cond_depth == ok_depth + 1) ++ok_depth;
+			break;
+		case REF_ENDIF:
+			if (cond_depth == ok_depth) --ok_depth;
+			--cond_depth;
+			break;
+		}
 	}
 	return ret;
 }
diff --git a/wrapperhelper/src/generator.h b/wrapperhelper/src/generator.h
index fd41862d..5e08577d 100644
--- a/wrapperhelper/src/generator.h
+++ b/wrapperhelper/src/generator.h
@@ -10,8 +10,8 @@
 
 typedef struct request_s {
 	string_t *obj_name;
-	_Bool has_default, default_comment;
-	_Bool has_val;
+	_Bool default_comment;
+	_Bool has_val, ignored;
 	_Bool weak;
 	struct {
 		enum request_type_e {
@@ -41,6 +41,10 @@ typedef struct reference_s {
 	enum {
 		REF_REQ,
 		REF_LINE,
+		REF_IFDEF,
+		REF_IFNDEF,
+		REF_ELSE,
+		REF_ENDIF,
 	} typ;
 	union {
 		request_t req;
@@ -48,9 +52,10 @@ typedef struct reference_s {
 	};
 } reference_t;
 VECTOR_DECLARE(references, reference_t)
-void request_print(request_t *req);
-void request_print_check(request_t *req);
-void output_from_references(FILE *f, VECTOR(references) *reqs);
+void request_print(const request_t *req);
+void request_print_check(const request_t *req);
+void references_print_check(const VECTOR(references) *refs);
+void output_from_references(FILE *f, const VECTOR(references) *reqs);
 
 VECTOR(references) *references_from_file(const char *filename, FILE *f); // Takes ownership of f
 int solve_request(request_t *req, type_t *typ);
diff --git a/wrapperhelper/src/lang.c b/wrapperhelper/src/lang.c
index 3e9f2a78..ddc711fe 100644
--- a/wrapperhelper/src/lang.c
+++ b/wrapperhelper/src/lang.c
@@ -84,7 +84,7 @@ void proc_token_del(proc_token_t *tok) {
 		break;
 	case PTOK_PRAGMA:
 		switch (tok->tokv.pragma.typ) {
-		case PRAGMA_MARK_SIMPLE:
+		case PRAGMA_EXPLICIT_CONV:
 			string_del(tok->tokv.pragma.val);
 			break;
 		case PRAGMA_ALLOW_INTS:
@@ -288,8 +288,8 @@ void proc_token_print(const proc_token_t *tok) {
 		case PRAGMA_ALLOW_INTS:
 			printf("Token: %7s Allow ints\n", "PRAGMA");
 			break;
-		case PRAGMA_MARK_SIMPLE:
-			printf("Token: %7s Mark simple: %s\n", "PRAGMA", string_content(tok->tokv.pragma.val));
+		case PRAGMA_EXPLICIT_CONV:
+			printf("Token: %7s Explicit conversion: destination is %s\n", "PRAGMA", string_content(tok->tokv.pragma.val));
 			break;
 		default:
 			printf("Token: %7s ???\n", "PRAGMA");
@@ -585,6 +585,7 @@ void struct_del_weak(struct_t *st) {
 }
 void type_del(type_t *typ) {
 	if (--typ->nrefs) return;
+	if (typ->converted) string_del(typ->converted);
 	switch (typ->typ) {
 	case TYPE_BUILTIN:
 		break;
@@ -670,6 +671,7 @@ type_t *type_new(void) {
 	}
 	ret->szinfo.align = ret->szinfo.size = 0;
 	ret->is_atomic = ret->is_const = ret->is_restrict = ret->is_volatile = ret->is_incomplete = ret->is_validated = ret->_internal_use = 0;
+	ret->converted = NULL;
 	ret->nrefs = 1;
 	ret->typ = TYPE_BUILTIN;
 	ret->val.builtin = BTT_INT;
@@ -683,6 +685,7 @@ type_t *type_new_ptr(type_t *target) {
 	}
 	ret->szinfo.align = ret->szinfo.size = 0;
 	ret->is_atomic = ret->is_const = ret->is_restrict = ret->is_volatile = ret->is_incomplete = ret->is_validated = ret->_internal_use = 0;
+	ret->converted = NULL;
 	ret->nrefs = 1;
 	ret->typ = TYPE_PTR;
 	ret->val.typ = target;
@@ -789,7 +792,6 @@ struct_t *struct_new(int is_struct, string_t *tag) {
 	ret->tag = tag;
 	ret->is_defined = 0;
 	ret->nrefs = 1;
-	ret->explicit_simple = 0;
 	return ret;
 }
 
@@ -973,6 +975,7 @@ void type_print(type_t *typ) {
 	if (!typ->is_validated) printf("!<not validated> ");
 	if (typ->is_incomplete) printf("<incomplete> ");
 	if (typ->is_validated && !typ->is_incomplete) printf("<size=%zu align=%zu> ", typ->szinfo.size, typ->szinfo.align);
+	if (typ->converted) printf("<converted: %s> ", string_content(typ->converted));
 	if (typ->is_const) printf("const ");
 	if (typ->is_restrict) printf("restrict ");
 	if (typ->is_volatile) printf("volatile ");
@@ -1019,9 +1022,6 @@ void type_print(type_t *typ) {
 }
 void struct_print(const struct_t *st) {
 	printf("<" DISP_ADDR_FMT "n_uses=%zu> ", DISP_ADDR_ARG(st) st->nrefs);
-	if (st->explicit_simple) {
-		printf("<explicitly simple> ");
-	}
 	if (st->is_defined) {
 		printf(
 			"%s %s <with %zu members%s> { ",
@@ -1157,6 +1157,7 @@ file_t *file_new(void) {
 		}
 		t->is_atomic = t->is_const = t->is_restrict = t->is_volatile = t->_internal_use = 0;
 		t->is_incomplete = (i == BTT_VOID);
+		t->converted = NULL; // Maybe should be something else?
 		t->is_validated = 1;
 		t->nrefs = 2;
 		t->typ = TYPE_BUILTIN;
diff --git a/wrapperhelper/src/lang.h b/wrapperhelper/src/lang.h
index 2f1cf811..682bbdda 100644
--- a/wrapperhelper/src/lang.h
+++ b/wrapperhelper/src/lang.h
@@ -162,7 +162,7 @@ typedef struct proc_token_s {
 		struct {
 			enum proc_pragma_e {
 				PRAGMA_ALLOW_INTS,
-				PRAGMA_MARK_SIMPLE,
+				PRAGMA_EXPLICIT_CONV,
 			} typ;
 			string_t *val;
 		} pragma;
@@ -368,6 +368,7 @@ typedef struct type_s {
 		} fun;
 	} val;
 	size_info_t szinfo;
+	string_t *converted; // NULL for default behavior
 } type_t;
 void type_del(type_t *typ);
 KHASH_MAP_DECLARE_STR(type_map, type_t*)
@@ -387,7 +388,6 @@ typedef struct struct_s {
 	size_t nrefs;
 	int is_struct; // 0 = union, 1 = struct
 	int has_incomplete; // 1 if the last element of the structure is a VLA or if an element of the union recursively contains a VLA
-	int explicit_simple;
 	size_t nmembers;
 	st_member_t *members;
 } struct_t;
diff --git a/wrapperhelper/src/main.c b/wrapperhelper/src/main.c
index 1b5b2db2..37786f68 100644
--- a/wrapperhelper/src/main.c
+++ b/wrapperhelper/src/main.c
@@ -131,31 +131,31 @@ int main(int argc, char **argv) {
 			del_str2kw();
 			return 2;
 		}
-		VECTOR(references) *reqs = references_from_file(ref_file, ref);
-		if (!reqs) {
+		VECTOR(references) *refs = references_from_file(ref_file, ref);
+		if (!refs) {
 			file_del(content);
 			del_machines();
 			del_str2kw();
 			return 2;
 		}
-		// vector_for(references, req, reqs) request_print(req);
-		if (!solve_references(reqs, content->decl_map)) {
+		// vector_for(references, req, refs) request_print(req);
+		if (!solve_references(refs, content->decl_map)) {
 			printf("Warning: failed to solve all default requests\n");
 		}
-		// vector_for(references, req, reqs) request_print(req);
-		//vector_for(references, req, reqs) request_print_check(req);
+		// vector_for(references, req, refs) request_print(req);
+		references_print_check(refs);
 		FILE *out = fopen(out_file, "w");
 		if (!out) {
 			err(2, "Error: failed to open %s", ref_file);
 			file_del(content);
-			vector_del(references, reqs);
+			vector_del(references, refs);
 			del_machines();
 			del_str2kw();
 			return 2;
 		}
-		output_from_references(out, reqs);
+		output_from_references(out, refs);
 		fclose(out);
-		vector_del(references, reqs);
+		vector_del(references, refs);
 		file_del(content);
 		del_machines();
 		del_str2kw();
@@ -183,6 +183,11 @@ int main(int argc, char **argv) {
 			struct_print(st);
 			printf("\n")
 		)
+		kh_foreach_key(content->type_set, typ,
+			printf("Type: %p = ", typ);
+			type_print(typ);
+			printf("\n")
+		)
 		kh_foreach(content->type_map, name, typ,
 			printf("Typedef: %s -> %p = ", name, typ);
 			type_print(typ);
diff --git a/wrapperhelper/src/parse.c b/wrapperhelper/src/parse.c
index cce0a115..379df988 100644
--- a/wrapperhelper/src/parse.c
+++ b/wrapperhelper/src/parse.c
@@ -67,6 +67,12 @@ enum decl_spec {
 	SPEC_BUILTIN_NOINT,
 	SPEC_TYPE,
 };
+enum fun_spec {
+	FSPEC_NONE            = 0b00,
+	FSPEC_INLINE          = 0b01,
+	FSPEC_NORETURN        = 0b10,
+	FSPEC_INLINE_NORETURN = 0b11,
+};
 VECTOR_DECLARE_STATIC(size_t, size_t)
 VECTOR_IMPL_STATIC(size_t, (void))
 
@@ -453,14 +459,14 @@ struct parse_declarator_dest_s {
 #define PDECL_TYPE_SET ((is_init && is_list) ? dest->f->type_set : (!is_init && is_list) ? dest->structms.type_set : dest->argt.type_set)
 #define PDECL_BUILTINS ((is_init && is_list) ? &dest->f->builtins : (!is_init && is_list) ? dest->structms.builtins : dest->argt.builtins)
 #define PDECL_CONST_MAP ((is_init && is_list) ? dest->f->const_map : (!is_init && is_list) ? dest->structms.const_map : dest->argt.const_map)
-static int parse_declarator(struct parse_declarator_dest_s *dest, preproc_t *prep, proc_token_t *tok, enum decl_storage storage, type_t *base_type,
-      int is_init, int is_list, int allow_decl, int allow_abstract);
+static int parse_declarator(struct parse_declarator_dest_s *dest, preproc_t *prep, proc_token_t *tok, enum decl_storage storage, enum fun_spec fspec,
+      type_t *base_type, int is_init, int is_list, int allow_decl, int allow_abstract);
 
 // declaration-specifier with storage != NULL
 // specifier-qualifier-list + static_assert-declaration with storage == NULL
 static int parse_declaration_specifier(khash_t(struct_map) *struct_map, khash_t(type_map) *type_map, khash_t(type_map) *enum_map,
         type_t *(*builtins)[LAST_BUILTIN + 1], khash_t(const_map) *const_map,
-        khash_t(type_set) *type_set, preproc_t *prep, proc_token_t *tok, enum decl_storage *storage, enum decl_spec *spec, type_t *typ);
+        khash_t(type_set) *type_set, preproc_t *prep, proc_token_t *tok, enum decl_storage *storage, enum fun_spec *fspec, enum decl_spec *spec, type_t *typ);
 
 
 void expr_print(expr_t *e) {
@@ -582,7 +588,7 @@ static int parse_type_name(khash_t(struct_map) *struct_map, khash_t(type_map) *t
         type_t *(*builtins)[LAST_BUILTIN + 1], khash_t(const_map) *const_map,
         khash_t(type_set) *type_set, preproc_t *prep, proc_token_t *tok, enum token_sym_type_e end_sym, type_t **typ) {
 	enum decl_spec spec = SPEC_NONE;
-	if (!parse_declaration_specifier(struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, NULL, &spec, *typ)) {
+	if (!parse_declaration_specifier(struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, NULL, NULL, &spec, *typ)) {
 		type_del(*typ);
 		goto failed;
 	}
@@ -598,7 +604,7 @@ static int parse_type_name(khash_t(struct_map) *struct_map, khash_t(type_map) *t
 	dest2.argt.type_set = type_set;
 	dest2.argt.builtins = builtins;
 	dest2.argt.const_map = const_map;
-	if (!parse_declarator(&dest2, prep, tok, STORAGE_NONE, *typ, 0, 0, 0, 1)) {
+	if (!parse_declarator(&dest2, prep, tok, STORAGE_NONE, FSPEC_NONE, *typ, 0, 0, 0, 1)) {
 		// Token is deleted
 		type_del(*typ);
 		goto failed;
@@ -914,11 +920,11 @@ expr_new_token:
 				goto failed;
 			}
 			if (!parse_type_name(struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, SYM_RPAREN, &typ)) {
-				proc_token_del(tok);
 				goto failed;
 			}
 			if (!typ->is_validated || typ->is_incomplete) {
 				printf("Error: cannot get the size of an incomplete type\n");
+				type_del(typ);
 				proc_token_del(tok);
 				goto failed;
 			}
@@ -1521,7 +1527,8 @@ static int eval_expression(expr_t *e, khash_t(const_map) *const_map, num_constan
 // specifier-qualifier-list + static_assert-declaration with storage == NULL
 static int parse_declaration_specifier(khash_t(struct_map) *struct_map, khash_t(type_map) *type_map, khash_t(type_map) *enum_map,
         type_t *(*builtins)[LAST_BUILTIN + 1], khash_t(const_map) *const_map,
-        khash_t(type_set) *type_set, preproc_t *prep, proc_token_t *tok, enum decl_storage *storage, enum decl_spec *spec, type_t *typ) {
+        khash_t(type_set) *type_set, preproc_t *prep, proc_token_t *tok, enum decl_storage *storage,
+        enum fun_spec *fspec, enum decl_spec *spec, type_t *typ) {
 	if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_STATIC_ASSERT)) {
 		// _Static_assert ( constant-expression , string-literal ) ;
 		// Empty destructor
@@ -1653,6 +1660,17 @@ parse_cur_token_decl:
 		goto parse_cur_token_decl;
 	}
 	
+	// Function specifier
+	if (fspec && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_INLINE)) {
+		*fspec |= FSPEC_INLINE;
+		*tok = proc_next_token(prep);
+		goto parse_cur_token_decl;
+	} else if (fspec && (tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_NORETURN)) {
+		*fspec |= FSPEC_NORETURN;
+		*tok = proc_next_token(prep);
+		goto parse_cur_token_decl;
+	}
+	
 	// Qualifier
 	if ((tok->tokt == PTOK_KEYWORD) && (tok->tokv.kw == KW_ATOMIC)) {
 		// Empty destructor
@@ -1992,7 +2010,7 @@ parse_cur_token_decl:
 			*tok = proc_next_token(prep);
 			while (!proc_token_isend(tok) && ((tok->tokt != PTOK_SYM) || (tok->tokv.sym != SYM_RBRACKET))) {
 				enum decl_spec spec2 = SPEC_NONE;
-				if (!parse_declaration_specifier(struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, NULL, &spec2, typ2)) {
+				if (!parse_declaration_specifier(struct_map, type_map, enum_map, builtins, const_map, type_set, prep, tok, NULL, NULL, &spec2, typ2)) {
 					vector_del(st_members, members);
 					type_del(typ2);
 					goto failed;
@@ -2039,7 +2057,7 @@ parse_cur_token_decl:
 				dest2.structms.builtins = builtins;
 				dest2.structms.const_map = const_map;
 				dest2.structms.dest = members;
-				if (!parse_declarator(&dest2, prep, tok, STORAGE_NONE, typ2, 0, 1, 1, 1)) {
+				if (!parse_declarator(&dest2, prep, tok, STORAGE_NONE, FSPEC_NONE, typ2, 0, 1, 1, 1)) {
 					printf("Error parsing struct-declarator-list\n");
 					vector_del(st_members, members);
 					type_del(typ2);
@@ -2348,8 +2366,8 @@ failed:
 	return 0;
 }
 
-static int parse_declarator(struct parse_declarator_dest_s *dest, preproc_t *prep, proc_token_t *tok, enum decl_storage storage, type_t *base_type,
-      int is_init, int is_list, int allow_decl, int allow_abstract) {
+static int parse_declarator(struct parse_declarator_dest_s *dest, preproc_t *prep, proc_token_t *tok, enum decl_storage storage, enum fun_spec fspec,
+      type_t *base_type, int is_init, int is_list, int allow_decl, int allow_abstract) {
 	int has_list = 0, has_ident = 0;
 	// TODO: allow_abstract and 'direct-abstract-declarator(opt) ( parameter-type-list(opt) )'
 	
@@ -2459,7 +2477,7 @@ static int parse_declarator(struct parse_declarator_dest_s *dest, preproc_t *pre
 							enum decl_storage storage2 = STORAGE_NONE;
 							enum decl_spec spec2 = SPEC_NONE;
 							if (!parse_declaration_specifier(PDECL_STRUCT_MAP, PDECL_TYPE_MAP, PDECL_ENUM_MAP, PDECL_BUILTINS,
-							         PDECL_CONST_MAP, PDECL_TYPE_SET, prep, tok, &storage2, &spec2, typ2)) {
+							         PDECL_CONST_MAP, PDECL_TYPE_SET, prep, tok, &storage2, NULL, &spec2, typ2)) {
 								// Token is deleted
 								vector_del(types, args);
 								type_del(typ2);
@@ -2504,7 +2522,7 @@ static int parse_declarator(struct parse_declarator_dest_s *dest, preproc_t *pre
 							dest2.argt.type_set = PDECL_TYPE_SET;
 							dest2.argt.builtins = PDECL_BUILTINS;
 							dest2.argt.const_map = PDECL_CONST_MAP;
-							if (!parse_declarator(&dest2, prep, tok, STORAGE_NONE, typ2, 0, 0, 1, 1)) {
+							if (!parse_declarator(&dest2, prep, tok, STORAGE_NONE, FSPEC_NONE, typ2, 0, 0, 1, 1)) {
 								// Token is deleted
 								vector_del(types, args);
 								type_del(typ2);
@@ -2799,6 +2817,11 @@ static int parse_declarator(struct parse_declarator_dest_s *dest, preproc_t *pre
 					printf("Error: unexpected token in function body\n");
 					goto failed;
 				}
+				if (fspec != FSPEC_NONE) {
+					printf("Error: unexpected function specifier\n");
+					// Empty destructor
+					goto failed;
+				}
 				
 				if (storage == STORAGE_TYPEDEF) {
 					if (!is_init || !is_list) {
@@ -2965,6 +2988,11 @@ static int parse_declarator(struct parse_declarator_dest_s *dest, preproc_t *pre
 					// Empty destructor
 					goto failed;
 				}
+				if (fspec != FSPEC_NONE) {
+					printf("Error: unexpected function specifier\n");
+					// Empty destructor
+					goto failed;
+				}
 				
 				size_t width;
 				switch (eval.typ) {
@@ -3163,19 +3191,31 @@ file_t *parse_file(machine_t *target, const char *filename, FILE *file) {
 					}
 				}
 				break; }
-			case PRAGMA_MARK_SIMPLE: {
-				khiter_t it = kh_get(type_map, ret->type_map, string_content(tok.tokv.pragma.val));
-				string_del(tok.tokv.pragma.val);
-				if (it == kh_end(ret->type_map)) {
-					printf("Invalid explicit_simple pragma: ident is not a typedef\n");
+			case PRAGMA_EXPLICIT_CONV: {
+				string_t *converted = tok.tokv.pragma.val;
+				type_t *typ2 = type_new();
+				if (!typ2) {
+					printf("Error: failed to create new type info structure\n");
+					string_del(converted);
+					type_del(typ2);
 					goto failed;
 				}
-				type_t *typ0 = kh_val(ret->type_map, it);
-				if (typ0->typ != TYPE_STRUCT_UNION) {
-					printf("Invalid explicit_simple pragma: ident is not a typedef to a structure or union\n");
+				tok = proc_next_token(prep);
+				if (!parse_type_name(ret->struct_map, ret->type_map, ret->enum_map, &ret->builtins, ret->const_map, ret->type_set,
+				                     prep, &tok, SYM_SEMICOLON, &typ2)) {
+					string_del(converted);
 					goto failed;
 				}
-				typ0->val.st->explicit_simple = 1;
+				type_del(typ2); // typ2 is in the type set, so it is already used
+				if (typ2->converted) {
+					printf("Error: type already has a conversion\n");
+					string_del(converted);
+					// Empty destructor
+					goto failed;
+				}
+				string_trim(converted);
+				typ2->converted = converted;
+				// Empty destructor
 				break; }
 			}
 		} else if (proc_token_iserror(&tok)) {
@@ -3184,30 +3224,31 @@ file_t *parse_file(machine_t *target, const char *filename, FILE *file) {
 			goto failed;
 		} else {
 			enum decl_storage storage = STORAGE_NONE;
+			enum fun_spec fspec = FSPEC_NONE;
 			enum decl_spec spec = SPEC_NONE;
 			if (!parse_declaration_specifier(ret->struct_map, ret->type_map, ret->enum_map, &ret->builtins, ret->const_map,
-			                                 ret->type_set, prep, &tok, &storage, &spec, typ)) {
+			                                 ret->type_set, prep, &tok, &storage, &fspec, &spec, typ)) {
 				goto failed;
 			}
 			if (spec == SPEC_NONE) continue; // Declaration was an assert, typ is unchanged
-			int ok;
 			typ = type_try_merge(typ, ret->type_set);
 			if ((tok.tokt != PTOK_SYM) || (tok.tokv.sym != SYM_SEMICOLON)) {
-			    ok = parse_declarator(&(struct parse_declarator_dest_s){.f = ret}, prep, &tok, storage, typ, 1, 1, 1, 0);
+			    if (!parse_declarator(&(struct parse_declarator_dest_s){.f = ret}, prep, &tok, storage, fspec, typ, 1, 1, 1, 0)) goto failed;
 			} else {
-				ok = validate_storage_type(storage, &ret->builtins, typ, tok.tokv.sym);
-			}
-			if (!ok) {
-				goto failed;
-			} else {
-				// Current token is ';' (or '}' for functions), ie. end of declaration
-				type_del(typ);
-				typ = type_new();
-				if (!typ) {
-					printf("Failed to create a type info structure\n");
+				if (validate_storage_type(storage, &ret->builtins, typ, tok.tokv.sym) != VALIDATION_LAST_DECL) goto failed;
+				if (fspec != FSPEC_NONE) {
+					printf("Error: unexpected function specifier\n");
+					// Empty destructor
 					goto failed;
 				}
 			}
+			// Current token is ';' (or '}' for functions), ie. end of declaration
+			type_del(typ);
+			typ = type_new();
+			if (!typ) {
+				printf("Failed to create a type info structure\n");
+				goto failed;
+			}
 		}
 	}
 	
diff --git a/wrapperhelper/src/preproc.c b/wrapperhelper/src/preproc.c
index fddf2d1c..00b34e4d 100644
--- a/wrapperhelper/src/preproc.c
+++ b/wrapperhelper/src/preproc.c
@@ -230,6 +230,7 @@ struct preproc_s {
 	enum preproc_state_e {
 		PPST_NONE,
 		PPST_NL,
+		PPST_PRAGMA_EXPLICIT,
 	} st;
 	khash_t(macros_map) *macros_map;
 	khash_t(string_set) *macros_defined, *macros_used;
@@ -1917,7 +1918,7 @@ int proc_unget_token(preproc_t *src, proc_token_t *tok) {
 }
 static proc_token_t proc_next_token_aux(preproc_t *src) {
 	if (!vector_size(ppsource, src->prep)) {
-		return (proc_token_t){ .tokt = PTOK_EOF, .tokv = (union proc_token_val_u){.c = (char)EOF} };
+		return (proc_token_t){ .tokt = PTOK_EOF, .tokv = {.c = (char)EOF} };
 	}
 	if (vector_last(ppsource, src->prep).srct == PPSRC_PTOKEN) {
 		proc_token_t ret = vector_last(ppsource, src->prep).srcv.ptok;
@@ -1927,7 +1928,7 @@ static proc_token_t proc_next_token_aux(preproc_t *src) {
 check_if_depth:
 	{
 		if (!vector_size(ppsource, src->prep)) {
-			return (proc_token_t){ .tokt = PTOK_EOF, .tokv = (union proc_token_val_u){.c = (char)EOF} };
+			return (proc_token_t){ .tokt = PTOK_EOF, .tokv = {.c = (char)EOF} };
 		}
 		ppsource_t *ppsrc = &vector_last(ppsource, src->prep);
 		if ((ppsrc->srct == PPSRC_PREPARE) && (ppsrc->srcv.prep.ok_depth != ppsrc->srcv.prep.cond_depth)) {
@@ -1944,7 +1945,7 @@ check_if_depth:
 					src->st = PPST_NONE;
 					proc_token_t ret;
 					ret.tokt = PTOK_INVALID;
-					ret.tokv = (union proc_token_val_u){.c = tok.tokv.c};
+					ret.tokv.c = tok.tokv.c;
 					return ret;
 				} else if (tok.tokt == PPTOK_NEWLINE) {
 					src->st = PPST_NL;
@@ -1988,7 +1989,7 @@ check_if_depth:
 							if (!cond) {
 								printf("Error: failed to allocate #elif condition vector\n");
 								src->st = PPST_NONE;
-								return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = (union proc_token_val_u){.c = '\0'} };
+								return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = {.c = '\0'} };
 							}
 							tok = ppsrc_next_token(src);
 							while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) {
@@ -1996,7 +1997,7 @@ check_if_depth:
 									printf("Error: failed to add token to #elif condition vector\n");
 									vector_del(preproc, cond);
 									src->st = PPST_NONE;
-									return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = (union proc_token_val_u){.c = '\0'} };
+									return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = {.c = '\0'} };
 								}
 								tok = ppsrc_next_token(src);
 							}
@@ -2006,7 +2007,7 @@ check_if_depth:
 								printf("Error: failed to allocate #elif solved_macros set\n");
 								vector_del(preproc, cond);
 								src->st = PPST_NONE;
-								return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = (union proc_token_val_u){.c = '\0'} };
+								return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = {.c = '\0'} };
 							}
 							
 							VECTOR(preproc) *expanded = proc_do_expand(src->macros_map, cond, solved_macros, src->macros_used);
@@ -2015,7 +2016,7 @@ check_if_depth:
 							if (!expanded) {
 								printf("Error: failed to expand #elif condition\n");
 								src->st = PPST_NONE;
-								return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = (union proc_token_val_u){.c = '\0'} };
+								return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = {.c = '\0'} };
 							}
 							
 							// Now we need to compute what is pointed by expanded, and increase cond_depth and ok_depth as needed
@@ -2025,7 +2026,7 @@ check_if_depth:
 							if (!st) {
 								printf("Error: failed to evaluate #elif condition in (%s)\n", src->cur_file);
 								src->st = PPST_NONE;
-								return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = (union proc_token_val_u){.c = '\0'} };
+								return (proc_token_t){ .tokt = PTOK_INVALID, .tokv = {.c = '\0'} };
 							}
 							if (res) {
 								vector_last(ppsource, src->prep).srcv.prep.entered_next_ok_cond = 1;
@@ -2142,7 +2143,7 @@ check_if_depth:
 check_next_token:
 	if (!vector_size(ppsource, src->prep)) {
 		ret.tokt = PTOK_EOF;
-		ret.tokv = (union proc_token_val_u){.c = (char)EOF};
+		ret.tokv.c = (char)EOF;
 		return ret;
 	}
 start_next_token:
@@ -2152,10 +2153,10 @@ start_cur_token:
 	case PPTOK_INVALID:
 		src->st = PPST_NONE;
 		ret.tokt = PTOK_INVALID;
-		ret.tokv = (union proc_token_val_u){.c = tok.tokv.c};
+		ret.tokv.c = tok.tokv.c;
 		return ret;
 	case PPTOK_IDENT:
-		src->st = PPST_NONE;
+		src->st = (src->st == PPST_PRAGMA_EXPLICIT) ? PPST_PRAGMA_EXPLICIT : PPST_NONE;
 		{
 			khiter_t it = kh_get(macros_map, src->macros_map, string_content(tok.tokv.str));
 			if (it != kh_end(src->macros_map)) {
@@ -2203,7 +2204,7 @@ start_cur_token:
 							string_del(tok.tokv.str);
 							src->st = PPST_NONE;
 							ret.tokt = PTOK_INVALID;
-							ret.tokv = (union proc_token_val_u){.c = tok.tokv.c};
+							ret.tokv.c = tok.tokv.c;
 							return ret;
 						}
 						// margs finishes with a SYM_RPAREN token
@@ -2221,7 +2222,7 @@ start_cur_token:
 							preproc_token_del(&tok2);
 							src->st = PPST_NONE;
 							ret.tokt = PTOK_INVALID;
-							ret.tokv = (union proc_token_val_u){.c = '\0'};
+							ret.tokv.c = '\0';
 							return ret;
 						}
 						vector_push(ppsource, src->prep, ((ppsource_t){.srct = PPSRC_PPTOKEN, .srcv.pptok = tok2}));
@@ -2245,7 +2246,7 @@ start_cur_token:
 					if (!solved) {
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
-						ret.tokv = (union proc_token_val_u){.c = '\0'};
+						ret.tokv.c = '\0';
 						return ret;
 					}
 					// If the expansion is empty, don't push it
@@ -2255,7 +2256,7 @@ start_cur_token:
 							vector_del(preproc, solved);
 							src->st = PPST_NONE;
 							ret.tokt = PTOK_INVALID;
-							ret.tokv = (union proc_token_val_u){.c = '\0'};
+							ret.tokv.c = '\0';
 							return ret;
 						}
 					} else {
@@ -2274,7 +2275,7 @@ start_cur_token:
 					string_del(tok.tokv.str);
 					src->st = PPST_NONE;
 					ret.tokt = PTOK_INVALID;
-					ret.tokv = (union proc_token_val_u){.c = '\0'};
+					ret.tokv.c = '\0';
 					return ret;
 				}
 			}
@@ -2284,27 +2285,27 @@ start_cur_token:
 		khiter_t it = kh_get(str2kw, str2kw, string_content(tok.tokv.str));
 		if (it == kh_end(str2kw)) {
 			ret.tokt = PTOK_IDENT;
-			ret.tokv = (union proc_token_val_u){.str = tok.tokv.str};
+			ret.tokv.str = tok.tokv.str;
 		} else {
 			string_del(tok.tokv.str);
 			ret.tokt = PTOK_KEYWORD;
-			ret.tokv = (union proc_token_val_u){.kw = kh_val(str2kw, it)};
+			ret.tokv.kw = kh_val(str2kw, it);
 		}
 		return ret; }
 	case PPTOK_NUM:
-		src->st = PPST_NONE;
+		src->st = (src->st == PPST_PRAGMA_EXPLICIT) ? PPST_PRAGMA_EXPLICIT : PPST_NONE;
 		ret.tokt = PTOK_NUM;
-		ret.tokv = (union proc_token_val_u){.str = tok.tokv.str};
+		ret.tokv.str = tok.tokv.str;
 		return ret;
 	case PPTOK_STRING:
-		src->st = PPST_NONE;
+		src->st = (src->st == PPST_PRAGMA_EXPLICIT) ? PPST_PRAGMA_EXPLICIT : PPST_NONE;
 		ret.tokt = PTOK_STRING;
 		ret.tokv = (union proc_token_val_u){.sstr = tok.tokv.sstr, .sisstr = tok.tokv.sisstr};
 		return ret;
 	case PPTOK_INCL:
 		src->st = PPST_NONE;
 		ret.tokt = PTOK_INVALID;
-		ret.tokv = (union proc_token_val_u){.c = tok.tokv.sisstr ? '<' : '"'};
+		ret.tokv.c = tok.tokv.sisstr ? '<' : '"';
 		string_del(tok.tokv.sstr);
 		return ret;
 	case PPTOK_SYM:
@@ -2314,7 +2315,7 @@ start_cur_token:
 				// Empty preprocessor command
 				if (tok.tokt == PPTOK_NEWLINE) {
 					ret.tokt = PTOK_EOF;
-					ret.tokv = (union proc_token_val_u){.c = tok.tokv.c};
+					ret.tokv.c = tok.tokv.c;
 					return ret;
 				} else {
 					goto check_next_token;
@@ -2344,7 +2345,7 @@ start_cur_token:
 						printf("Error: failed to allocate #include tokens vector (%s)\n", src->cur_file);
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
-						ret.tokv = (union proc_token_val_u){.c = '\0'};
+						ret.tokv.c = '\0';
 						return ret;
 					}
 					while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) {
@@ -2353,7 +2354,7 @@ start_cur_token:
 							vector_del(preproc, incl);
 							src->st = PPST_NONE;
 							ret.tokt = PTOK_INVALID;
-							ret.tokv = (union proc_token_val_u){.c = '\0'};
+							ret.tokv.c = '\0';
 							return ret;
 						}
 						tok = ppsrc_next_token(src);
@@ -2365,7 +2366,7 @@ start_cur_token:
 						vector_del(preproc, incl);
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
-						ret.tokv = (union proc_token_val_u){.c = '\0'};
+						ret.tokv.c = '\0';
 						return ret;
 					}
 					
@@ -2376,7 +2377,7 @@ start_cur_token:
 						printf("Error: failed to expand #include tokens (%s)\n", src->cur_file);
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
-						ret.tokv = (union proc_token_val_u){.c = '\0'};
+						ret.tokv.c = '\0';
 						return ret;
 					}
 					
@@ -2385,7 +2386,7 @@ start_cur_token:
 						printf("Error: missing #include name (%s)\n", src->cur_file);
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
-						ret.tokv = (union proc_token_val_u){.c = '\0'};
+						ret.tokv.c = '\0';
 						return ret;
 					}
 					if (vector_content(preproc, expanded)[0].tokt == PPTOK_STRING) {
@@ -2397,7 +2398,7 @@ start_cur_token:
 						vector_del(preproc, expanded);
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
-						ret.tokv = (union proc_token_val_u){.c = '\0'};
+						ret.tokv.c = '\0';
 						return ret;
 					} else if ((vector_content(preproc, expanded)[0].tokt == PPTOK_SYM) && (vector_content(preproc, expanded)[0].tokv.sym == SYM_LT)
 					        && (vector_last(preproc, expanded).tokt == PPTOK_SYM) && (vector_last(preproc, expanded).tokv.sym == SYM_GT)
@@ -2415,7 +2416,7 @@ start_cur_token:
 									string_del(incl_file);
 									src->st = PPST_NONE;
 									ret.tokt = PTOK_INVALID;
-									ret.tokv = (union proc_token_val_u){.c = '\0'};
+									ret.tokv.c = '\0';
 									return ret;
 								}
 								break;
@@ -2427,7 +2428,7 @@ start_cur_token:
 										string_del(incl_file);
 										src->st = PPST_NONE;
 										ret.tokt = PTOK_INVALID;
-										ret.tokv = (union proc_token_val_u){.c = '\0'};
+										ret.tokv.c = '\0';
 										return ret;
 									}
 								}
@@ -2447,7 +2448,7 @@ start_cur_token:
 								string_del(incl_file);
 								src->st = PPST_NONE;
 								ret.tokt = PTOK_INVALID;
-								ret.tokv = (union proc_token_val_u){.c = '\0'};
+								ret.tokv.c = '\0';
 								return ret;
 							}
 						}
@@ -2457,7 +2458,7 @@ start_cur_token:
 						vector_del(preproc, expanded);
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
-						ret.tokv = (union proc_token_val_u){.c = '\0'};
+						ret.tokv.c = '\0';
 						return ret;
 					}
 				}
@@ -2467,7 +2468,7 @@ start_cur_token:
 					string_del(incl_file);
 					src->st = PPST_NONE;
 					ret.tokt = PTOK_INVALID;
-					ret.tokv = (union proc_token_val_u){.c = tok.tokv.sisstr ? '<' : '"'};
+					ret.tokv.c = tok.tokv.sisstr ? '<' : '"';
 					return ret;
 				}
 				string_del(incl_file);
@@ -2486,7 +2487,7 @@ start_cur_token:
 					printf("Failed to allocate token vector for macro %s, returning EOF\n", string_content(defname));
 					string_del(defname); // Token is now freed
 					ret.tokt = PTOK_EOF;
-					ret.tokv = (union proc_token_val_u){.c = (char)EOF};
+					ret.tokv.c = (char)EOF;
 					return ret;
 				}
 				khash_t(argid_map) *args = NULL;
@@ -2499,7 +2500,7 @@ start_cur_token:
 						string_del(defname); // Token is now freed
 						vector_del(mtoken, m.toks);
 						ret.tokt = PTOK_EOF;
-						ret.tokv = (union proc_token_val_u){.c = (char)EOF};
+						ret.tokv.c = (char)EOF;
 						return ret;
 					}
 					m.nargs = 0;
@@ -2522,7 +2523,7 @@ start_cur_token:
 									vector_del(mtoken, m.toks);
 									argid_map_del(args);
 									ret.tokt = PTOK_EOF;
-									ret.tokv = (union proc_token_val_u){.c = (char)EOF};
+									ret.tokv.c = (char)EOF;
 									return ret;
 								}
 								if (kh_ret == 0) {
@@ -2555,7 +2556,7 @@ start_cur_token:
 									vector_del(mtoken, m.toks);
 									argid_map_del(args);
 									ret.tokt = PTOK_EOF;
-									ret.tokv = (union proc_token_val_u){.c = (char)EOF};
+									ret.tokv.c = (char)EOF;
 									return ret;
 								}
 								if (kh_ret == 0) {
@@ -2718,7 +2719,7 @@ start_cur_token:
 						macro_del(&m);
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
-						ret.tokv = (union proc_token_val_u){.c = tok.tokv.c};
+						ret.tokv.c = tok.tokv.c;
 						return ret;
 					} else if (iret == 0) {
 						// Ignore
@@ -2790,7 +2791,7 @@ start_cur_token:
 				printf("\n");
 				vector_clear(ppsource, src->prep);
 				ret.tokt = PTOK_INVALID;
-				ret.tokv = (union proc_token_val_u){.c = (char)EOF};
+				ret.tokv.c = (char)EOF;
 				return ret;
 			} else if (!strcmp(string_content(tok.tokv.str), "warning")) {
 				printf("Warning: #warning command (%s):", src->cur_file);
@@ -2844,7 +2845,7 @@ start_cur_token:
 							ret.tokv.pragma.typ = PRAGMA_ALLOW_INTS;
 							return ret;
 						}
-					} else if (!strcmp(string_content(tok.tokv.str), "explicit_simple")) {
+					} else if (!strcmp(string_content(tok.tokv.str), "type_letters")) {
 						string_del(tok.tokv.str);
 						tok = ppsrc_next_token(src);
 						if ((tok.tokt != PPTOK_IDENT) && (tok.tokt != PPTOK_IDENT_UNEXP)) {
@@ -2852,20 +2853,11 @@ start_cur_token:
 							goto preproc_hash_err;
 						}
 						string_t *id = tok.tokv.str;
-						tok = ppsrc_next_token(src);
-						while ((tok.tokt != PPTOK_NEWLINE) && (tok.tokt != PPTOK_EOF) && (tok.tokt != PPTOK_INVALID)) {
-							preproc_token_del(&tok);
-							tok = ppsrc_next_token(src);
-						}
-						if (tok.tokt == PPTOK_INVALID) {
-							string_del(id);
-							goto start_cur_token;
-						} else {
-							ret.tokt = PTOK_PRAGMA;
-							ret.tokv.pragma.typ = PRAGMA_MARK_SIMPLE;
-							ret.tokv.pragma.val = id;
-							return ret;
-						}
+						src->st = PPST_PRAGMA_EXPLICIT;
+						ret.tokt = PTOK_PRAGMA;
+						ret.tokv.pragma.typ = PRAGMA_EXPLICIT_CONV;
+						ret.tokv.pragma.val = id;
+						return ret;
 					} else {
 						printf("Unknown pragma wrappers directive '%s', skipping until EOL\n", string_content(tok.tokv.str));
 						goto preproc_hash_err;
@@ -2879,7 +2871,7 @@ start_cur_token:
 					printf("Error: invalid #if source type %u\n", vector_last(ppsource, src->prep).srct);
 					src->st = PPST_NONE;
 					ret.tokt = PTOK_INVALID;
-					ret.tokv = (union proc_token_val_u){.c = '\0'};
+					ret.tokv.c = '\0';
 					return ret;
 				}
 				string_del(tok.tokv.str);
@@ -2888,7 +2880,7 @@ start_cur_token:
 					printf("Error: failed to allocate #if condition vector\n");
 					src->st = PPST_NONE;
 					ret.tokt = PTOK_INVALID;
-					ret.tokv = (union proc_token_val_u){.c = '\0'};
+					ret.tokv.c = '\0';
 					return ret;
 				}
 				tok = ppsrc_next_token(src);
@@ -2898,7 +2890,7 @@ start_cur_token:
 						vector_del(preproc, cond);
 						src->st = PPST_NONE;
 						ret.tokt = PTOK_INVALID;
-						ret.tokv = (union proc_token_val_u){.c = '\0'};
+						ret.tokv.c = '\0';
 						return ret;
 					}
 					tok = ppsrc_next_token(src);
@@ -2910,7 +2902,7 @@ start_cur_token:
 					vector_del(preproc, cond);
 					src->st = PPST_NONE;
 					ret.tokt = PTOK_INVALID;
-					ret.tokv = (union proc_token_val_u){.c = '\0'};
+					ret.tokv.c = '\0';
 					return ret;
 				}
 				
@@ -2921,7 +2913,7 @@ start_cur_token:
 					printf("Error: failed to expand #if condition\n");
 					src->st = PPST_NONE;
 					ret.tokt = PTOK_INVALID;
-					ret.tokv = (union proc_token_val_u){.c = '\0'};
+					ret.tokv.c = '\0';
 					return ret;
 				}
 				
@@ -2933,7 +2925,7 @@ start_cur_token:
 					printf("Error: failed to evaluate #if condition (%s)\n", src->cur_file);
 					src->st = PPST_NONE;
 					ret.tokt = PTOK_INVALID;
-					ret.tokv = (union proc_token_val_u){.c = '\0'};
+					ret.tokv.c = '\0';
 					return ret;
 				}
 				++vector_last(ppsource, src->prep).srcv.prep.cond_depth;
@@ -3116,22 +3108,28 @@ start_cur_token:
 			if (tok.tokt == PPTOK_NEWLINE) goto check_if_depth;
 			else goto start_cur_token; // Returns immediately
 		}
-		src->st = PPST_NONE;
+		src->st = (src->st == PPST_PRAGMA_EXPLICIT) ? PPST_PRAGMA_EXPLICIT : PPST_NONE;
 		ret.tokt = PTOK_SYM;
-		ret.tokv = (union proc_token_val_u){.sym = tok.tokv.sym};
+		ret.tokv.sym = tok.tokv.sym;
 		return ret;
 	case PPTOK_NEWLINE:
+		if (src->st == PPST_PRAGMA_EXPLICIT) {
+			src->st = PPST_NL;
+			ret.tokt = PTOK_SYM;
+			ret.tokv.sym = SYM_SEMICOLON;
+			return ret;
+		}
 		src->st = PPST_NL;
 		goto check_next_token;
 	case PPTOK_BLANK:
 		src->st = PPST_NONE;
 		ret.tokt = PTOK_INVALID;
-		ret.tokv = (union proc_token_val_u){.c = tok.tokv.c};
+		ret.tokv.c = tok.tokv.c;
 		return ret;
 	case PPTOK_START_LINE_COMMENT:
 		src->st = PPST_NONE;
 		ret.tokt = PTOK_INVALID;
-		ret.tokv = (union proc_token_val_u){.c = tok.tokv.c};
+		ret.tokv.c = tok.tokv.c;
 		return ret;
 	case PPTOK_EOF:
 		if ((vector_last(ppsource, src->prep).srct == PPSRC_PREPARE) && vector_last(ppsource, src->prep).srcv.prep.cond_depth) {
@@ -3148,6 +3146,12 @@ start_cur_token:
 			vector_last(ppsource, src->prep).srcv.prep.old_filename = NULL;
 		}
 		vector_pop(ppsource, src->prep);
+		if (src->st == PPST_PRAGMA_EXPLICIT) {
+			src->st = PPST_NL;
+			ret.tokt = PTOK_SYM;
+			ret.tokv.sym = SYM_SEMICOLON;
+			return ret;
+		}
 		src->st = PPST_NL; // Should be redundant since TOK_NEWLINE is added before TOK_EOF if required
 		// EOF has an empty destructor
 		// Note that since we have opened the file, the previous file also had ok_depth == cond_depth
@@ -3156,7 +3160,7 @@ start_cur_token:
 	default:
 		printf("Unknown next pp token type %u, sending EOF\n", tok.tokt);
 		ret.tokt = PTOK_EOF;
-		ret.tokv = (union proc_token_val_u){.c = (char)EOF};
+		ret.tokv.c = (char)EOF;
 		return ret;
 	}
 }
@@ -3172,7 +3176,7 @@ proc_token_t proc_next_token(preproc_t *src) {
 					string_del(ret2.tokv.sstr);
 					src->st = PPST_NONE;
 					ret.tokt = PTOK_INVALID;
-					ret.tokv = (union proc_token_val_u){.c = '\0'};
+					ret.tokv.c = '\0';
 					return ret;
 				}
 				string_del(ret2.tokv.sstr);
@@ -3183,7 +3187,7 @@ proc_token_t proc_next_token(preproc_t *src) {
 					proc_token_del(&ret2);
 					src->st = PPST_NONE;
 					ret.tokt = PTOK_INVALID;
-					ret.tokv = (union proc_token_val_u){.c = '\0'};
+					ret.tokv.c = '\0';
 					return ret;
 				}
 				return ret;