diff --git a/tools/qfcc/include/Makemodule.am b/tools/qfcc/include/Makemodule.am index 689ebad68..f6158b5c4 100644 --- a/tools/qfcc/include/Makemodule.am +++ b/tools/qfcc/include/Makemodule.am @@ -1,4 +1,5 @@ EXTRA_DIST += \ + tools/qfcc/include/attribute.h \ tools/qfcc/include/class.h \ tools/qfcc/include/codespace.h \ tools/qfcc/include/cpp.h \ diff --git a/tools/qfcc/include/attribute.h b/tools/qfcc/include/attribute.h new file mode 100644 index 000000000..2f47642e6 --- /dev/null +++ b/tools/qfcc/include/attribute.h @@ -0,0 +1,43 @@ +/* + attribute.h + + Attribute list handling + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/02/02 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef attribute_h +#define attribute_h + +typedef struct attribute_s { + struct attribute_s *next; + const char *name; + struct ex_value_s *value; +} attribute_t; + +struct expr_s; +attribute_t *new_attribute(const char *name, struct expr_s *value); + +#endif//attribute_h diff --git a/tools/qfcc/include/type.h b/tools/qfcc/include/type.h index 2062933d6..7a213dd98 100644 --- a/tools/qfcc/include/type.h +++ b/tools/qfcc/include/type.h @@ -39,6 +39,7 @@ typedef struct ty_func_s { struct type_s *type; int num_params; struct type_s **param_types; + int no_va_list; ///< don't inject va_list for ... function } ty_func_t; typedef struct ty_fldptr_s { @@ -97,6 +98,7 @@ typedef struct { unsigned is_typedef:1; unsigned is_overload:1; unsigned nosave:1; + unsigned no_va_list:1; } specifier_t; #define EV_TYPE(type) extern type_t type_##type; diff --git a/tools/qfcc/source/Makemodule.am b/tools/qfcc/source/Makemodule.am index 76e3f73d8..d7d85e597 100644 --- a/tools/qfcc/source/Makemodule.am +++ b/tools/qfcc/source/Makemodule.am @@ -7,6 +7,7 @@ bin_PROGRAMS += @QFCC_TARGETS@ bin_SCRIPTS += tools/qfcc/source/qfpreqcc qfcc_SOURCES = \ + tools/qfcc/source/attribute.c \ tools/qfcc/source/class.c \ tools/qfcc/source/codespace.c \ tools/qfcc/source/constfold.c \ diff --git a/tools/qfcc/source/attribute.c b/tools/qfcc/source/attribute.c new file mode 100644 index 000000000..b821124c1 --- /dev/null +++ b/tools/qfcc/source/attribute.c @@ -0,0 +1,55 @@ +/* + attribute.c + + Attribute list handling + + Copyright (C) 2022 Bill Currie + + Author: Bill Currie + Date: 2022/02/02 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/alloc.h" + +#include "tools/qfcc/include/attribute.h" +#include "tools/qfcc/include/diagnostic.h" +#include "tools/qfcc/include/expr.h" +#include "tools/qfcc/include/strpool.h" + +static attribute_t *attributes_freelist; + +attribute_t *new_attribute(const char *name, expr_t *value) +{ + if (value && value->type != ex_value) { + error (value, "not a literal constant"); + return 0; + } + + attribute_t *attr; + ALLOC (16384, attribute_t, attributes, attr); + attr->name = save_string (name); + attr->value = value ? value->e.value : 0; + return attr; +} diff --git a/tools/qfcc/source/class.c b/tools/qfcc/source/class.c index d90ecc062..42f613c9e 100644 --- a/tools/qfcc/source/class.c +++ b/tools/qfcc/source/class.c @@ -90,7 +90,7 @@ type_t type_IMP = { .alignment = 1, .width = 1, .meta = ty_basic, - {{&type_id, -3, IMP_params}}, + {{&type_id, -3, IMP_params, 1}}, }; type_t type_super = { .type = ev_invalid, diff --git a/tools/qfcc/source/expr.c b/tools/qfcc/source/expr.c index 7af72d5a1..3364bedbf 100644 --- a/tools/qfcc/source/expr.c +++ b/tools/qfcc/source/expr.c @@ -2155,7 +2155,7 @@ build_function_call (expr_t *fexpr, const type_t *ftype, expr_t *params) warning (fexpr, "too few arguments"); } param_count = -ftype->t.func.num_params - 1; - emit_args = 1; + emit_args = !ftype->t.func.no_va_list; } else if (ftype->t.func.num_params >= 0) { if (arg_count > ftype->t.func.num_params) { return error (fexpr, "too many arguments"); diff --git a/tools/qfcc/source/qc-lex.l b/tools/qfcc/source/qc-lex.l index c2db6db0c..3c8e28ec9 100644 --- a/tools/qfcc/source/qc-lex.l +++ b/tools/qfcc/source/qc-lex.l @@ -367,7 +367,6 @@ static keyword_t at_keywords[] = { {"extern", EXTERN }, {"static", STATIC }, {"sizeof", SIZEOF }, - {"nosave", NOSAVE }, {"not", NOT }, }; @@ -391,7 +390,7 @@ static keyword_t qf_keywords[] = { {"@dot", DOT, 0 }, }; -// These keywors are always available. Other than @system and @overload, they +// These keywors are always available. Other than the @ keywords, they // form traditional QuakeC. static keyword_t keywords[] = { {"void", TYPE, &type_void }, @@ -407,6 +406,7 @@ static keyword_t keywords[] = { {"else", ELSE, 0 }, {"@system", SYSTEM, 0 }, {"@overload", OVERLOAD, 0 }, + {"@attribute", ATTRIBUTE, 0 }, }; static const char * diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index 3ab14cbe2..99006c68e 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -44,6 +44,7 @@ #include #include +#include "tools/qfcc/include/attribute.h" #include "tools/qfcc/include/class.h" #include "tools/qfcc/include/debug.h" #include "tools/qfcc/include/def.h" @@ -112,6 +113,7 @@ int yylex (void); struct methodlist_s *methodlist; struct symbol_s *symbol; struct symtab_s *symtab; + struct attribute_s *attribute; } // these tokens are common between qc and qp @@ -149,7 +151,7 @@ int yylex (void); %token LOCAL RETURN WHILE DO IF ELSE FOR BREAK CONTINUE ELLIPSIS %token NIL GOTO SWITCH CASE DEFAULT ENUM -%token ARGS TYPEDEF EXTERN STATIC SYSTEM NOSAVE OVERLOAD NOT +%token ARGS TYPEDEF EXTERN STATIC SYSTEM OVERLOAD NOT ATTRIBUTE %token UNSIGNED SIGNED LONG SHORT %token STRUCT %token TYPE @@ -162,6 +164,8 @@ int yylex (void); %type type_specifier type_specifier_or_storage_class %type type +%type attribute_list attribute + %type function_params var_list param_declaration %type qc_func_params qc_var_list qc_param_decl %type new_name @@ -238,6 +242,22 @@ make_spec (type_t *type, storage_class_t storage, int is_typedef, return spec; } +static specifier_t +parse_attributes (attribute_t *attr_list) +{ + specifier_t spec = {}; + for (attribute_t *attr = attr_list; attr; attr = attr->next) { + if (!strcmp (attr->name, "no_va_list")) { + spec.no_va_list = 1; + } else if (!strcmp (attr->name, "nosave")) { + spec.nosave = 1; + } else { + warning (0, "skipping unknown attribute '%s'", attr->name); + } + } + return spec; +} + static specifier_t spec_merge (specifier_t spec, specifier_t new) { @@ -276,6 +296,7 @@ spec_merge (specifier_t spec, specifier_t new) spec.is_long |= new.is_long; spec.is_overload |= new.is_overload; spec.nosave |= new.nosave; + spec.no_va_list |= new.no_va_list; return spec; } @@ -471,6 +492,7 @@ external_def ret_type = *type; *type = 0; *type = parse_params (0, $2); + (*type)->t.func.no_va_list = $1.no_va_list; $$.type = find_type (append_type ($1.type, ret_type)); if ($$.type->type != ev_field) $$.params = $2; @@ -511,6 +533,7 @@ function_body symbol_t *sym = $0; specifier_t spec = default_type ($-1, sym); + sym->type->t.func.no_va_list = spec.no_va_list; sym->type = find_type (append_type (sym->type, spec.type)); $$ = function_symbol (sym, spec.is_overload, 1); } @@ -534,6 +557,7 @@ function_body symbol_t *sym = $0; specifier_t spec = default_type ($-1, sym); + sym->type->t.func.no_va_list = spec.no_va_list; sym->type = find_type (append_type (sym->type, spec.type)); sym = function_symbol (sym, spec.is_overload, 1); build_builtin_function (sym, $3, 0, spec.storage); @@ -582,6 +606,7 @@ external_decl | function_decl { specifier_t spec = default_type ($0, $1); + $1->type->t.func.no_va_list = spec.no_va_list; $1->type = find_type (append_type ($1->type, spec.type)); if (spec.is_typedef) { $1->sy_type = sy_type; @@ -599,13 +624,30 @@ storage_class | SYSTEM { $$ = make_spec (0, sc_system, 0, 0); } | TYPEDEF { $$ = make_spec (0, sc_global, 1, 0); } | OVERLOAD { $$ = make_spec (0, current_storage, 0, 1); } - | NOSAVE + | ATTRIBUTE '(' attribute_list ')' { - $$ = make_spec (0, current_storage, 0, 0); - $$.nosave = 1; + $$ = parse_attributes ($3); } ; +attribute_list + : attribute + | attribute_list ',' attribute + { + if ($3) { + $3->next = $1; + $$ = $3; + } else { + $$ = $1; + } + } + ; + +attribute + : NAME { $$ = new_attribute ($1->name, 0); } + | NAME '(' expr_list ')' { $$ = new_attribute ($1->name, $3); } + ; + optional_specifiers : specifiers { @@ -1053,6 +1095,7 @@ qc_param_decl type = &(*type)->t.fldptr.type) ; *type = parse_params (*type, $2); + (*type)->t.func.no_va_list = $1.no_va_list; $3->type = find_type ($1.type); if ($3->type->type != ev_field) $3->params = $2; @@ -1131,6 +1174,7 @@ local_decl_list type = &(*type)->t.fldptr.type) ; *type = parse_params (*type, $1); + (*type)->t.func.no_va_list = spec.no_va_list; spec.type = find_type (spec.type); $$ = spec; } diff --git a/tools/qfcc/source/type.c b/tools/qfcc/source/type.c index 7e73fc99d..9f94affae 100644 --- a/tools/qfcc/source/type.c +++ b/tools/qfcc/source/type.c @@ -381,7 +381,8 @@ types_same (type_t *a, type_t *b) return 0; case ev_func: if (a->t.func.type != b->t.func.type - || a->t.func.num_params != b->t.func.num_params) + || a->t.func.num_params != b->t.func.num_params + || a->t.func.no_va_list != b->t.func.no_va_list) return 0; count = a->t.func.num_params; if (count < 0) diff --git a/tools/qfcc/test/chewed-alias.r b/tools/qfcc/test/chewed-alias.r index 08c060534..409254c39 100644 --- a/tools/qfcc/test/chewed-alias.r +++ b/tools/qfcc/test/chewed-alias.r @@ -18,7 +18,7 @@ int main () { return 0; // test succeeds if compile succeeds } -id obj_msgSend (id receiver, SEL op, ...) = #0; +@attribute(no_va_list) id obj_msgSend (id receiver, SEL op, ...) = #0; void __obj_exec_class (struct obj_module *msg) = #0; @implementation Object @end diff --git a/tools/qfcc/test/methodparams.r b/tools/qfcc/test/methodparams.r index b29e6954b..d2e71591b 100644 --- a/tools/qfcc/test/methodparams.r +++ b/tools/qfcc/test/methodparams.r @@ -15,7 +15,7 @@ typedef struct { int x, y; } Point; [textContext mvvprintf: pos, fmt, @args]; } @end -id obj_msgSend (id receiver, SEL op, ...) = #0; +@attribute(no_va_list) id obj_msgSend (id receiver, SEL op, ...) = #0; void __obj_exec_class (struct obj_module *msg) = #0; @interface Object @end diff --git a/tools/qfcc/test/sendv.r b/tools/qfcc/test/sendv.r index aa2cb6665..24929992a 100644 --- a/tools/qfcc/test/sendv.r +++ b/tools/qfcc/test/sendv.r @@ -54,5 +54,5 @@ main () } @end -id (id receiver, SEL op, ...) obj_msgSend = #0; +@attribute(no_va_list) id (id receiver, SEL op, ...) obj_msgSend = #0; void __obj_exec_class (struct obj_module *msg) = #0;