diff --git a/README.md b/README.md index 703d3f284..b8761c944 100644 --- a/README.md +++ b/README.md @@ -79,13 +79,13 @@ import stack; // Define our new types, the first will implicitly create // a complete copy of the entire Stack module with "Type" set to "int" -def IntStack = Stack; +def IntStack = Stack(); // The second creates another copy with "Type" set to "double" -def DoubleStack = Stack; +def DoubleStack = Stack(); -// If we had added "define IntStack2 = Stack" +// If we had added "define IntStack2 = Stack()" // no additional copy would have been made (since we already -// have an parameterization of Stack) so it would +// have an parameterization of Stack()) so it would // be same as declaring IntStack2 an alias of IntStack // Importing an external C function is straightforward diff --git a/lib/std/collections/enummap.c3 b/lib/std/collections/enummap.c3 index ce980fc02..7d84b5969 100644 --- a/lib/std/collections/enummap.c3 +++ b/lib/std/collections/enummap.c3 @@ -1,4 +1,4 @@ -module std::collections::enummap; +module std::collections::enummap(); struct EnumMap { diff --git a/lib/std/collections/enumset.c3 b/lib/std/collections/enumset.c3 index 2465e5ff5..80beb06e1 100644 --- a/lib/std/collections/enumset.c3 +++ b/lib/std/collections/enumset.c3 @@ -5,7 +5,7 @@ /** * @require Enum.kindof == TypeKind.ENUM : "Only enums maybe be used with an enumset" **/ -module std::collections::enumset; +module std::collections::enumset(); def EnumSetType = $typefrom(private::type_for_enum_elements(Enum.elements)) @private ; diff --git a/lib/std/collections/linkedlist.c3 b/lib/std/collections/linkedlist.c3 index e7bec6878..040ea4c00 100644 --- a/lib/std/collections/linkedlist.c3 +++ b/lib/std/collections/linkedlist.c3 @@ -1,7 +1,7 @@ // Copyright (c) 2021 Christoffer Lerno. All rights reserved. // Use of self source code is governed by the MIT license // a copy of which can be found in the LICENSE_STDLIB file. -module std::collections::linkedlist; +module std::collections::linkedlist(); struct Node @private { diff --git a/lib/std/collections/list.c3 b/lib/std/collections/list.c3 index ac5332561..f16a4c7a4 100644 --- a/lib/std/collections/list.c3 +++ b/lib/std/collections/list.c3 @@ -1,7 +1,7 @@ // Copyright (c) 2021 Christoffer Lerno. All rights reserved. // Use of self source code is governed by the MIT license // a copy of which can be found in the LICENSE_STDLIB file. -module std::collections::list; +module std::collections::list(); import std::math; def ElementPredicate = fn bool(Type *type); diff --git a/lib/std/collections/map.c3 b/lib/std/collections/map.c3 index 4435007db..f797f5471 100644 --- a/lib/std/collections/map.c3 +++ b/lib/std/collections/map.c3 @@ -1,7 +1,7 @@ // Copyright (c) 2023 Christoffer Lerno. All rights reserved. // Use of this source code is governed by the MIT license // a copy of which can be found in the LICENSE_STDLIB file. -module std::collections::map; +module std::collections::map(); import std::math; const uint DEFAULT_INITIAL_CAPACITY = 16; diff --git a/lib/std/collections/object.c3 b/lib/std/collections/object.c3 index 725f3da4f..2ad164c11 100644 --- a/lib/std/collections/object.c3 +++ b/lib/std/collections/object.c3 @@ -465,7 +465,7 @@ fn Object* Object.get_or_create_obj(&self, String key) return container; } -def ObjectInternalMap = HashMap @private; -def ObjectInternalList = List @private; -def ObjectInternalMapEntry = Entry @private; +def ObjectInternalMap = HashMap() @private; +def ObjectInternalList = List() @private; +def ObjectInternalMapEntry = Entry() @private; diff --git a/lib/std/collections/priorityqueue.c3 b/lib/std/collections/priorityqueue.c3 index 5c03766ef..af7d59cd5 100644 --- a/lib/std/collections/priorityqueue.c3 +++ b/lib/std/collections/priorityqueue.c3 @@ -20,10 +20,10 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -module std::collections::priorityqueue; +module std::collections::priorityqueue(); import std::collections::list; -def Heap = List; +def Heap = List(); struct PriorityQueue { diff --git a/lib/std/collections/range.c3 b/lib/std/collections/range.c3 index 361a33e64..20d0590f7 100644 --- a/lib/std/collections/range.c3 +++ b/lib/std/collections/range.c3 @@ -2,7 +2,7 @@ * @checked Type{} < Type{} : "The type must be comparable" * @checked Type{} + (Type)1 : "The type must be possible to add to" **/ -module std::collections::range; +module std::collections::range(); struct Range { diff --git a/lib/std/core/allocators/tracking_allocator.c3 b/lib/std/core/allocators/tracking_allocator.c3 index a5d60f9f8..63098db10 100644 --- a/lib/std/core/allocators/tracking_allocator.c3 +++ b/lib/std/core/allocators/tracking_allocator.c3 @@ -5,7 +5,7 @@ module std::core::mem::allocator; import std::collections::map; -def PtrMap = HashMap; +def PtrMap = HashMap(); // A simple tracking allocator. // It tracks allocations using a hash map but diff --git a/lib/std/io/path.c3 b/lib/std/io/path.c3 index 8a20454a7..6eb6835b2 100644 --- a/lib/std/io/path.c3 +++ b/lib/std/io/path.c3 @@ -6,7 +6,7 @@ const char PREFERRED_SEPARATOR_WIN32 = '\\'; const char PREFERRED_SEPARATOR_POSIX = '/'; const char PREFERRED_SEPARATOR = env::WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX; -def PathList = List; +def PathList = List(); fault PathResult { diff --git a/lib/std/math/math.c3 b/lib/std/math/math.c3 index 890e3cd38..7771e3494 100644 --- a/lib/std/math/math.c3 +++ b/lib/std/math/math.c3 @@ -85,33 +85,33 @@ fault MatrixError MATRIX_INVERSE_DOESNT_EXIST, } -def Complexf = Complex; -def Complex = Complex; -def complexf_identity = complex::identity; -def complex_identity = complex::identity; +def Complexf = Complex(); +def Complex = Complex(); +def complexf_identity = complex::identity(); +def complex_identity = complex::identity(); -def Quaternionf = Quaternion; -def Quaternion = Quaternion; -def quaternionf_identity = quaternion::identity; -def quaternion_identity = quaternion::identity; +def Quaternionf = Quaternion(); +def Quaternion = Quaternion(); +def quaternionf_identity = quaternion::identity(); +def quaternion_identity = quaternion::identity(); -def Matrix2f = Matrix2x2; -def Matrix2 = Matrix2x2; -def Matrix3f = Matrix3x3; -def Matrix3 = Matrix3x3; -def Matrix4f = Matrix4x4; -def Matrix4 = Matrix4x4; -def matrix4_ortho = matrix::ortho; -def matrix4_perspective = matrix::perspective; -def matrix4f_ortho = matrix::ortho; -def matrix4f_perspective = matrix::perspective; +def Matrix2f = Matrix2x2(); +def Matrix2 = Matrix2x2(); +def Matrix3f = Matrix3x3(); +def Matrix3 = Matrix3x3(); +def Matrix4f = Matrix4x4(); +def Matrix4 = Matrix4x4(); +def matrix4_ortho = matrix::ortho(); +def matrix4_perspective = matrix::perspective(); +def matrix4f_ortho = matrix::ortho(); +def matrix4f_perspective = matrix::perspective(); -def MATRIX2_IDENTITY = matrix::IDENTITY2; -def MATRIX2F_IDENTITY = matrix::IDENTITY2; -def MATRIX3_IDENTITY = matrix::IDENTITY3; -def MATRIX3F_IDENTITY = matrix::IDENTITY3; -def MATRIX4_IDENTITY = matrix::IDENTITY4; -def MATRIX4F_IDENTITY = matrix::IDENTITY4; +def MATRIX2_IDENTITY = matrix::IDENTITY2(); +def MATRIX2F_IDENTITY = matrix::IDENTITY2(); +def MATRIX3_IDENTITY = matrix::IDENTITY3(); +def MATRIX3F_IDENTITY = matrix::IDENTITY3(); +def MATRIX4_IDENTITY = matrix::IDENTITY4(); +def MATRIX4F_IDENTITY = matrix::IDENTITY4(); /** * @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector` diff --git a/lib/std/math/math.complex.c3 b/lib/std/math/math.complex.c3 index af6a6e8b3..3d774f114 100644 --- a/lib/std/math/math.complex.c3 +++ b/lib/std/math/math.complex.c3 @@ -1,4 +1,4 @@ -module std::math::complex; +module std::math::complex(); union Complex { diff --git a/lib/std/math/math.matrix.c3 b/lib/std/math/math.matrix.c3 index 40840d8ff..170f48aee 100644 --- a/lib/std/math/math.matrix.c3 +++ b/lib/std/math/math.matrix.c3 @@ -1,4 +1,4 @@ -module std::math::matrix; +module std::math::matrix(); struct Matrix2x2 { diff --git a/lib/std/math/math_quaternion.c3 b/lib/std/math/math_quaternion.c3 index 73dbc8178..67550e845 100644 --- a/lib/std/math/math_quaternion.c3 +++ b/lib/std/math/math_quaternion.c3 @@ -1,4 +1,4 @@ -module std::math::quaternion; +module std::math::quaternion(); import std::math::vector; union Quaternion { diff --git a/lib/std/sort/quicksort.c3 b/lib/std/sort/quicksort.c3 index 93bdff2a0..0b40d6a07 100644 --- a/lib/std/sort/quicksort.c3 +++ b/lib/std/sort/quicksort.c3 @@ -1,4 +1,4 @@ -module std::sort::quicksort; +module std::sort::quicksort(); import std::sort; def ElementType = $typeof(Type{}[0]); diff --git a/releasenotes.md b/releasenotes.md index f8b2d18ed..96750ae33 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -3,6 +3,8 @@ ## 0.5.0 Change List ### Changes / improvements +- New generic syntax. +- Ad hoc generics are now allowed. - Allow inferred type on method first argument. - Fix to void expression blocks - Temporary objects may now invoke methods using ref parameters. diff --git a/resources/examples/map.c3 b/resources/examples/map.c3 index c5855150c..764a54857 100644 --- a/resources/examples/map.c3 +++ b/resources/examples/map.c3 @@ -1,4 +1,4 @@ -module std::container::map ; +module std::container::map(); import std::core::builtin; import std::io; fault MapResult diff --git a/resources/examples/notworking/toml_parser_c2.c3 b/resources/examples/notworking/toml_parser_c2.c3 index f3ef93216..b47a14a13 100644 --- a/resources/examples/notworking/toml_parser_c2.c3 +++ b/resources/examples/notworking/toml_parser_c2.c3 @@ -751,7 +751,7 @@ fn u32 addKind(u32 value, NodeKind k) @(inline) { } fn NodeKind getKind(u32 value) @(inline) { - return cast(value >> NodeKindOffset); + return cast()(value >> NodeKindOffset); } fn u32 getValue(u32 value) @(inline) { diff --git a/resources/grammar/c3.l b/resources/grammar/c3.l index 69fc432d2..fea223517 100644 --- a/resources/grammar/c3.l +++ b/resources/grammar/c3.l @@ -216,6 +216,8 @@ b64\`{B64}+\` { count(); return(BYTES); } "=>" { count(); return(IMPLIES); } "[<" { count(); return(LVEC); } ">]" { count(); return(RVEC); } +"(<" { count(); return(LGENPAR); } +">)" { count(); return(RGENPAR); } "$$" { count(); return(BUILTIN); } ";" { count(); return(';'); } ("{") { count(); return('{'); } diff --git a/resources/grammar/grammar.y b/resources/grammar/grammar.y index e270e7162..91e2c184c 100644 --- a/resources/grammar/grammar.y +++ b/resources/grammar/grammar.y @@ -15,6 +15,7 @@ void yyerror(char *s); %token STRING_LITERAL INTEGER %token INC_OP DEC_OP SHL_OP SHR_OP LE_OP GE_OP EQ_OP NE_OP %token AND_OP OR_OP MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN +%token LGENPAR RGENPAR %token SUB_ASSIGN SHL_ASSIGN SHR_ASSIGN AND_ASSIGN %token XOR_ASSIGN OR_ASSIGN VAR NUL ELVIS NEXTCASE ANYFAULT %token MODULE IMPORT DEF EXTERN @@ -187,6 +188,7 @@ call_trailing | call_invocation | call_invocation compound_statement | '.' access_ident + | generic_expr | INC_OP | DEC_OP | '!' @@ -1104,9 +1106,9 @@ opt_distinct_inline ; generic_parameters - : additive_expr + : expr | type - | generic_parameters ',' bit_expr + | generic_parameters ',' expr | generic_parameters ',' type ; @@ -1154,8 +1156,12 @@ define_attribute | AT_TYPE_IDENT opt_attributes '=' '{' opt_attributes '}' ; +generic_expr + : LGENPAR generic_parameters RGENPAR + ; + opt_generic_parameters - : '<' generic_parameters '>' + : generic_expr | empty ; @@ -1194,7 +1200,7 @@ module_params module : MODULE path_ident opt_attributes ';' - | MODULE path_ident '<' module_params '>' opt_attributes ';' + | MODULE path_ident LGENPAR module_params RGENPAR opt_attributes ';' ; import_paths diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index ff04d18f3..4bb656ff2 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -377,6 +377,11 @@ struct TypeInfo_ Expr *len; } array; TypeInfo *pointer; + struct + { + TypeInfo *base; + Expr **params; + } generic; }; }; @@ -840,6 +845,13 @@ typedef struct BuiltinAccessKind kind : 8; ExprId inner; } ExprBuiltinAccess; + +typedef struct +{ + ExprId parent; + Expr **parmeters; +} ExprGenericIdent; + typedef struct { Expr *parent; @@ -1135,6 +1147,7 @@ struct Expr_ ExprCall call_expr; // 32 Expr *inner_expr; // 8 ExprBuiltinAccess builtin_access_expr; + ExprGenericIdent generic_ident_expr; ExprCatchUnwrap catch_unwrap_expr; // 24 ExprSubscript subscript_expr; // 12 ExprSubscriptAssign subscript_assign_expr; @@ -1482,6 +1495,7 @@ typedef struct Module_ Module **sub_modules; Decl **tests; Decl **lambdas_to_evaluate; + const char *generic_suffix; } Module; @@ -2548,6 +2562,11 @@ INLINE bool type_is_integer_unsigned(Type *type) INLINE bool type_info_poison(TypeInfo *type) { + if (global_context.suppress_errors) + { + type->resolve_status = RESOLVE_NOT_DONE; + return false; + } type->type = poisoned_type; type->resolve_status = RESOLVE_DONE; return false; diff --git a/src/compiler/copying.c b/src/compiler/copying.c index e272f6a41..14e502162 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -296,6 +296,10 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) { case EXPR_ANYSWITCH: UNREACHABLE + case EXPR_GENERIC_IDENT: + MACRO_COPY_EXPRID(expr->generic_ident_expr.parent); + MACRO_COPY_EXPR_LIST(expr->generic_ident_expr.parmeters); + return expr; case EXPR_MACRO_BODY_EXPANSION: MACRO_COPY_EXPR_LIST(expr->body_expansion_expr.values); MACRO_COPY_DECL_LIST(expr->body_expansion_expr.declarations); @@ -797,6 +801,10 @@ TypeInfo *copy_type_info(CopyStruct *c, TypeInfo *source) case TYPE_INFO_CT_IDENTIFIER: case TYPE_INFO_IDENTIFIER: return copy; + case TYPE_INFO_GENERIC: + copy->generic.base = copy_type_info(c, copy->generic.base); + copy->generic.params = copy_expr_list(c, copy->generic.params); + return copy; case TYPE_INFO_TYPEFROM: case TYPE_INFO_EVALTYPE: case TYPE_INFO_TYPEOF: diff --git a/src/compiler/enums.h b/src/compiler/enums.h index ccfcd36bc..686638db8 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -228,6 +228,7 @@ typedef enum EXPR_EXPR_BLOCK, EXPR_OPTIONAL, EXPR_FORCE_UNWRAP, + EXPR_GENERIC_IDENT, EXPR_GROUP, EXPR_HASH_IDENT, EXPR_IDENTIFIER, @@ -375,6 +376,7 @@ typedef enum TYPE_INFO_INFERRED_VECTOR, TYPE_INFO_SUBARRAY, TYPE_INFO_POINTER, + TYPE_INFO_GENERIC, } TypeInfoKind; typedef enum @@ -427,6 +429,7 @@ typedef enum TOKEN_IMPLIES, // => TOKEN_LESS_EQ, // <= TOKEN_LBRAPIPE, // {| + TOKEN_LGENPAR, // (< TOKEN_LVEC, // [< TOKEN_MINUS_ASSIGN, // -= TOKEN_MINUSMINUS, // -- @@ -437,6 +440,7 @@ typedef enum TOKEN_PLUS_ASSIGN, // += TOKEN_PLUSPLUS, // ++ TOKEN_RBRAPIPE, // |} + TOKEN_RGENPAR, // >) TOKEN_RVEC, // >] TOKEN_QUESTQUEST, // ?? TOKEN_SCOPE, // :: @@ -812,7 +816,8 @@ typedef enum ANALYSIS_CT_ECHO, ANALYSIS_CT_ASSERT, ANALYSIS_FUNCTIONS, - ANALYSIS_LAST = ANALYSIS_FUNCTIONS + ANALYSIS_FINALIZE, + ANALYSIS_LAST = ANALYSIS_FINALIZE } AnalysisStage; typedef enum diff --git a/src/compiler/expr.c b/src/compiler/expr.c index 54007ed26..2caff80f3 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -133,6 +133,7 @@ bool expr_may_addr(Expr *expr) case EXPR_VASPLAT: case EXPR_SWIZZLE: case EXPR_LAMBDA: + case EXPR_GENERIC_IDENT: return false; } UNREACHABLE @@ -325,6 +326,7 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) case EXPR_CT_ARG: case EXPR_ASM: case EXPR_SUBSCRIPT_ASSIGN: + case EXPR_GENERIC_IDENT: UNREACHABLE case EXPR_NOP: return true; @@ -689,6 +691,8 @@ bool expr_is_pure(Expr *expr) return expr_is_pure(expr->unary_expr.expr); } UNREACHABLE + case EXPR_GENERIC_IDENT: + return exprid_is_pure(expr->generic_ident_expr.parent); case EXPR_BITACCESS: case EXPR_ACCESS: // All access is pure if the parent is pure. diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index 150f6684a..ab39263f4 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -1312,7 +1312,7 @@ static bool lexer_scan_token_inner(Lexer *lexer) case '}': return return_token(lexer, TOKEN_RBRACE, "}"); case '(': - return return_token(lexer, TOKEN_LPAREN, "("); + return match(lexer, '<') ? return_token(lexer, TOKEN_LGENPAR, "(<") : return_token(lexer, TOKEN_LPAREN, "("); case ')': return return_token(lexer, TOKEN_RPAREN, ")"); case '[': @@ -1368,6 +1368,7 @@ static bool lexer_scan_token_inner(Lexer *lexer) if (match(lexer, '=')) return return_token(lexer, TOKEN_SHR_ASSIGN, ">>="); return return_token(lexer, TOKEN_SHR, ">>"); } + if (match(lexer, ')')) return return_token(lexer, TOKEN_RGENPAR, ">)"); if (match(lexer, ']')) return return_token(lexer, TOKEN_RVEC, ">]"); return match(lexer, '=') ? return_token(lexer, TOKEN_GREATER_EQ, ">=") : return_token(lexer, TOKEN_GREATER, diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 3a46f5101..e01197d01 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -6543,6 +6543,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_COND: case EXPR_ASM: case EXPR_VASPLAT: + case EXPR_GENERIC_IDENT: UNREACHABLE case EXPR_LAMBDA: llvm_emit_lambda(c, value, expr); diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index afc243dc9..5787a9e62 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -525,7 +525,7 @@ Expr *parse_expression_list(ParseContext *c, bool allow_decl) while (1) { Decl *decl; - ASSIGN_EXPR_OR_RET(Expr * expr, parse_decl_or_expr(c, &decl), poisoned_expr); + ASSIGN_EXPR_OR_RET(Expr *expr, parse_decl_or_expr(c, &decl), poisoned_expr); if (!expr) { if (!allow_decl) @@ -969,6 +969,28 @@ static Expr *parse_subscript_expr(ParseContext *c, Expr *left) return subs_expr; } +/** + * generic_expr ::= '(<' generic_parameters '>)' + */ +static Expr *parse_generic_expr(ParseContext *c, Expr *left) +{ + assert(left && expr_ok(left)); + advance_and_verify(c, TOKEN_LGENPAR); + + Expr *subs_expr = expr_new_expr(EXPR_GENERIC_IDENT, left); + subs_expr->generic_ident_expr.parent = exprid(left); + Expr **exprs = NULL; + do + { + ASSIGN_EXPR_OR_RET(Expr *param, parse_expr(c), poisoned_expr); + vec_add(exprs, param); + } while (try_consume(c, TOKEN_COMMA)); + CONSUME_OR_RET(TOKEN_RGENPAR, poisoned_expr); + subs_expr->generic_ident_expr.parmeters = exprs; + RANGE_EXTEND_PREV(subs_expr); + return subs_expr; +} + /** * access_expr ::= '.' primary_expr */ @@ -1809,6 +1831,7 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_LBRAPIPE] = { parse_expr_block, NULL, PREC_NONE }, [TOKEN_BANGBANG] = { NULL, parse_force_unwrap_expr, PREC_CALL }, [TOKEN_LBRACKET] = { NULL, parse_subscript_expr, PREC_CALL }, + [TOKEN_LGENPAR] = { NULL, parse_generic_expr, PREC_CALL }, [TOKEN_MINUS] = { parse_unary_expr, parse_binary, PREC_ADDITIVE }, [TOKEN_PLUS] = { parse_unary_expr, parse_binary, PREC_ADDITIVE }, [TOKEN_DIV] = { NULL, parse_binary, PREC_MULTIPLICATIVE }, diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 558a4484d..7ec543a71 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -144,9 +144,13 @@ static inline bool parse_optional_module_params(ParseContext *c, const char ***t *tokens_ref = NULL; - if (!try_consume(c, TOKEN_LESS)) return true; + SourceSpan span = c->span; + bool is_old_style = try_consume(c, TOKEN_LESS); + if (!is_old_style && !try_consume(c, TOKEN_LGENPAR)) return true; - if (try_consume(c, TOKEN_GREATER)) RETURN_SEMA_ERROR_HERE("Generic parameter list cannot be empty."); + // TODO remove after deprecation time + TokenType end_token = is_old_style ? TOKEN_GREATER : TOKEN_RGENPAR; + if (try_consume(c, end_token)) RETURN_SEMA_ERROR_HERE("Generic parameter list cannot be empty."); // No params while (1) @@ -170,13 +174,18 @@ static inline bool parse_optional_module_params(ParseContext *c, const char ***t advance(c); if (!try_consume(c, TOKEN_COMMA)) { - return consume(c, TOKEN_GREATER, "Expected '>'."); + if (!consume(c, end_token, "Expected '>)'.")) return false; + if (is_old_style) + { + span = extend_span_with_token(span, c->prev_span); + sema_warning_at(span, "Generics with <...> syntax is deprecated, use (<...>) instead."); + } + return true; } } - } /** - * module ::= MODULE module_path ('<' module_params '>')? (@public|@private|@local|@test|@export|@extern) EOS + * module ::= MODULE module_path ('(<' module_params '>)')? (@public|@private|@local|@test|@export|@extern) EOS */ bool parse_module(ParseContext *c, AstId contracts) { @@ -487,6 +496,24 @@ static inline TypeInfo *parse_base_type(ParseContext *c) return type_info; } +static inline TypeInfo *parse_generic_type(ParseContext *c, TypeInfo *type) +{ + assert(type_info_ok(type)); + + advance_and_verify(c, TOKEN_LGENPAR); + Expr **exprs = NULL; + do + { + ASSIGN_EXPR_OR_RET(Expr *param, parse_expr(c), poisoned_type_info); + vec_add(exprs, param); + } while (try_consume(c, TOKEN_COMMA)); + CONSUME_OR_RET(TOKEN_RGENPAR, poisoned_type_info); + TypeInfo *generic_type = type_info_new(TYPE_INFO_GENERIC, type->span); + generic_type->generic.params = exprs; + generic_type->generic.base = type; + return generic_type; +} + /** * array_type_index * : '[' constant_expression ']' @@ -597,6 +624,9 @@ TypeInfo *parse_type_with_base(ParseContext *c, TypeInfo *type_info) case TOKEN_LBRACKET: type_info = parse_array_type_index(c, type_info); break; + case TOKEN_LGENPAR: + type_info = parse_generic_type(c, type_info); + break; case TOKEN_STAR: advance(c); { @@ -1629,21 +1659,23 @@ static bool parse_macro_params(ParseContext *c, Decl *macro) } /** - * define_parameters ::= expr (',' expr)* '>' + * define_parameters ::= expr (<',' expr)* '>)' * * @return NULL if parsing failed, otherwise a list of Type* */ -static inline Expr **parse_generic_parameters(ParseContext *c) +static inline Expr **parse_generic_parameters(ParseContext *c, bool old_style) { Expr **params = NULL; - while (!try_consume(c, TOKEN_GREATER)) + // TODO remove deprecation + TokenType end_token = old_style ? TOKEN_GREATER : TOKEN_RGENPAR; + while (!try_consume(c, end_token)) { - ASSIGN_EXPR_OR_RET(Expr *arg, parse_generic_parameter(c), NULL); + ASSIGN_EXPR_OR_RET(Expr *arg, old_style ? parse_generic_parameter(c) : parse_expr(c), NULL); vec_add(params, arg); TokenType tok = c->tok; - if (tok != TOKEN_RPAREN && tok != TOKEN_GREATER) + if (tok != end_token) { - TRY_CONSUME_OR_RET(TOKEN_COMMA, "Expected ',' after argument.", NULL); + TRY_CONSUME_OR_RET(TOKEN_COMMA, "Expected ',' after the argument.", NULL); } } return params; @@ -1723,10 +1755,12 @@ static inline Decl *parse_def_type(ParseContext *c) // 2. Now parse the type which we know is here. ASSIGN_TYPE_OR_RET(TypeInfo *type_info, parse_type(c), poisoned_decl); - // 3. Do we have '<' if so it's a parameterized type e.g. foo::bar::Type. - if (try_consume(c, TOKEN_LESS)) + bool old_style_encountered = try_consume(c, TOKEN_LESS); + + // 3. Do we have '(<' if so it's a parameterized type e.g. foo::bar::Type(). + if (old_style_encountered || try_consume(c, TOKEN_LGENPAR)) { - Expr **params = parse_generic_parameters(c); + Expr **params = parse_generic_parameters(c, old_style_encountered); if (!params) return poisoned_decl; decl->decl_kind = DECL_DEFINE; decl_add_type(decl, TYPE_TYPEDEF); @@ -1736,6 +1770,10 @@ static inline Decl *parse_def_type(ParseContext *c) if (!parse_attributes_for_global(c, decl)) return poisoned_decl; RANGE_EXTEND_PREV(decl); + if (old_style_encountered) + { + sema_warning_at(decl->span, "Use of <...> for generics is deprecated, please use (<...>) instead."); + } CONSUME_EOS_OR_RET(poisoned_decl); return decl; } @@ -1849,16 +1887,22 @@ static inline Decl *parse_def_ident(ParseContext *c) decl->define_decl.span = c->span; advance(c); - if (try_consume(c, TOKEN_LESS)) + bool old_style_encountered = try_consume(c, TOKEN_LESS); + + if (old_style_encountered || try_consume(c, TOKEN_LGENPAR) ) { decl->define_decl.define_kind = DEFINE_IDENT_GENERIC; - Expr **params = parse_generic_parameters(c); + Expr **params = parse_generic_parameters(c, old_style_encountered); if (!params) return poisoned_decl; decl->define_decl.generic_params = params; } if (!parse_attributes_for_global(c, decl)) return poisoned_decl; RANGE_EXTEND_PREV(decl); + if (old_style_encountered) + { + sema_warning_at(decl->span, "Use of <...> for generics is deprecated, please use (<...>) instead."); + } CONSUME_EOS_OR_RET(poisoned_decl); return decl; } diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index b4ed654dc..755c410fc 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -1316,6 +1316,7 @@ Ast *parse_stmt(ParseContext *c) case TOKEN_INLINE: case TOKEN_DISTINCT: case TOKEN_CT_INCLUDE: + case TOKEN_LGENPAR: SEMA_ERROR_HERE("Unexpected '%s' found when expecting a statement.", token_type_to_string(c->tok)); advance(c); @@ -1323,6 +1324,7 @@ Ast *parse_stmt(ParseContext *c) case TOKEN_RPAREN: case TOKEN_RBRACE: case TOKEN_RBRACKET: + case TOKEN_RGENPAR: SEMA_ERROR_HERE("Mismatched '%s' found.", token_type_to_string(c->tok)); advance(c); return poisoned_ast; diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index d52e1b67e..7459fc956 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -79,11 +79,12 @@ INLINE void add_decl_to_list(Decl ***list, Decl *decl) } bool parse_module(ParseContext *c, AstId contracts); -Expr *parse_generic_parameter(ParseContext *c); + bool try_consume(ParseContext *c, TokenType type); bool consume(ParseContext *c, TokenType type, const char *message, ...); bool consume_const_name(ParseContext *c, const char* type); Expr *parse_precedence_with_left_side(ParseContext *c, Expr *left_side, Precedence precedence); +Expr *parse_generic_parameter(ParseContext *c); INLINE const char *symstr(ParseContext *c) { diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index c4bf40ec1..69cf75af4 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -139,7 +139,7 @@ static inline bool sema_analyse_struct_member(SemaContext *context, Decl *parent { RETURN_SEMA_ERROR(decl, "Circular dependency resolving member."); } - assert(!decl->unit || decl->unit->module->is_generic); + assert(!decl->unit || decl->unit->module->is_generic || decl->unit == parent->unit); decl->unit = parent->unit; AttributeDomain domain = ATTR_MEMBER; @@ -319,6 +319,7 @@ static bool sema_analyse_struct_members(SemaContext *context, Decl *decl, Decl * bool is_packed = decl->is_packed; unsigned member_count = vec_size(members); Decl **struct_members = decl->strukt.members; + for (unsigned i = 0; i < member_count; i++) { AGAIN:; @@ -331,12 +332,8 @@ static bool sema_analyse_struct_members(SemaContext *context, Decl *decl, Decl * bool erase_decl = false; if (!sema_analyse_struct_member(context, decl, member, &erase_decl)) { - if (decl_ok(decl)) - { - decl_poison(decl); - continue; - } - continue; + decl_poison(member); + return decl_poison(decl); } if (erase_decl) { @@ -2988,20 +2985,8 @@ static CompilationUnit *unit_copy(Module *module, CompilationUnit *unit) static Module *module_instantiate_generic(SemaContext *context, Module *module, Path *path, Expr **params, SourceSpan from) { - Module *new_module = compiler_find_or_create_module(path, NULL); - new_module->is_generic = false; - CompilationUnit **units = module->units; - VECEACH(units, i) - { - vec_add(new_module->units, unit_copy(new_module, units[i])); - } - CompilationUnit *first_context = new_module->units[0]; - if (module->contracts) - { - copy_begin(); - new_module->contracts = astid(copy_ast_macro(astptr(module->contracts))); - copy_end(); - } + unsigned decls = 0; + Decl* params_decls[MAX_PARAMS]; VECEACH(module->parameters, i) { const char *param_name = module->parameters[i]; @@ -3018,7 +3003,7 @@ static Module *module_instantiate_generic(SemaContext *context, Module *module, decl->var.init_expr = param; decl->type = param->type; decl->resolve_status = RESOLVE_NOT_DONE; - vec_add(first_context->global_decls, decl); + params_decls[decls++] = decl; continue; } if (is_value) @@ -3033,12 +3018,34 @@ static Module *module_instantiate_generic(SemaContext *context, Module *module, decl->typedef_decl.type_info = type_info; decl->type->name = decl->name; decl->type->canonical = type_info->type->canonical; - vec_add(first_context->global_decls, decl); + params_decls[decls++] = decl; } + + + Module *new_module = compiler_find_or_create_module(path, NULL); + new_module->is_generic = false; + CompilationUnit **units = module->units; + VECEACH(units, i) + { + vec_add(new_module->units, unit_copy(new_module, units[i])); + } + CompilationUnit *first_context = new_module->units[0]; + for (unsigned i = 0; i < decls; i++) + { + vec_add(first_context->global_decls, params_decls[i]); + } + + if (module->contracts) + { + copy_begin(); + new_module->contracts = astid(copy_ast_macro(astptr(module->contracts))); + copy_end(); + } + return new_module; } -static bool sema_append_generate_parameterized_name(SemaContext *c, Module *module, Decl *decl, bool mangled) +static bool sema_append_generate_parameterized_name(SemaContext *c, Module *module, Expr **params, bool mangled) { if (mangled) { @@ -3047,9 +3054,9 @@ static bool sema_append_generate_parameterized_name(SemaContext *c, Module *modu } else { - scratch_buffer_append_char('<'); + scratch_buffer_append("(<"); } - FOREACH_BEGIN_IDX(i, Expr *param, decl->define_decl.generic_params) + FOREACH_BEGIN_IDX(i, Expr *param, params) if (i != 0) { scratch_buffer_append(mangled ? "$" : ", "); @@ -3057,23 +3064,11 @@ static bool sema_append_generate_parameterized_name(SemaContext *c, Module *modu if (param->expr_kind == EXPR_TYPEINFO) { TypeInfo *type_info = param->type_expr; - if (!sema_resolve_type_info(c, type_info)) return decl_poison(decl); + if (!sema_resolve_type_info(c, type_info)) return false; Type *type = type_info->type->canonical; - if (type->type_kind == TYPE_OPTIONAL) - { - SEMA_ERROR(type_info, "Expected a non-optional type."); - return poisoned_decl; - } - if (type == type_void) - { - SEMA_ERROR(type_info, "A 'void' type cannot be used as a parameter type."); - return poisoned_decl; - } - if (type_is_invalid_storage_type(type)) - { - SEMA_ERROR(type_info, "Expected a runtime type."); - return poisoned_decl; - } + if (type->type_kind == TYPE_OPTIONAL) RETURN_SEMA_ERROR(type_info, "Expected a non-optional type."); + if (type == type_void) RETURN_SEMA_ERROR(type_info, "A 'void' type cannot be used as a parameter type."); + if (type_is_invalid_storage_type(type)) RETURN_SEMA_ERROR(type_info, "Expected a runtime type."); if (mangled) { type_mangle_introspect_name_to_buffer(type); @@ -3085,7 +3080,7 @@ static bool sema_append_generate_parameterized_name(SemaContext *c, Module *modu } else { - if (!sema_analyse_ct_expr(c, param)) return decl_poison(decl); + if (!sema_analyse_ct_expr(c, param)) return false; Type *type = param->type->canonical; if (!type_is_integer_or_bool_kind(type)) { @@ -3110,7 +3105,6 @@ static bool sema_append_generate_parameterized_name(SemaContext *c, Module *modu if (type->type_kind == TYPE_I128 || type->type_kind == TYPE_U128) { char *str = int_to_str(param->const_expr.ixx, 10); - scratch_buffer_append(str); } else @@ -3121,7 +3115,7 @@ static bool sema_append_generate_parameterized_name(SemaContext *c, Module *modu } else { - scratch_buffer_append_unsigned_int(param->const_expr.ixx.i.high); + scratch_buffer_append_unsigned_int(param->const_expr.ixx.i.low); } } if (mangled) @@ -3132,7 +3126,7 @@ static bool sema_append_generate_parameterized_name(SemaContext *c, Module *modu } } FOREACH_END(); - scratch_buffer_append_char(mangled ? '$' : '>'); + scratch_buffer_append(mangled ? "$" : ">)"); return true; } @@ -3176,6 +3170,8 @@ static bool sema_analyse_generic_module_contracts(SemaContext *c, Module *module } return true; } + + static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl) { Path *decl_path; @@ -3204,19 +3200,40 @@ static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl) default: UNREACHABLE } + Expr **params = decl->define_decl.generic_params; + Decl *symbol = sema_analyse_parameterized_identifier(c, decl_path, name, span, params); + if (!decl_ok(symbol)) return decl_poison(decl); + switch (decl->define_decl.define_kind) + { + case DEFINE_IDENT_GENERIC: + decl->define_decl.alias = symbol; + decl->type = symbol->type; + return true; + case DEFINE_TYPE_GENERIC: + { + Type *type = type_new(TYPE_TYPEDEF, decl->name); + decl->type = type; + type->decl = symbol; + decl->decl_kind = DECL_TYPEDEF; + type->canonical = symbol->type->canonical; + return true; + } + default: + UNREACHABLE + } +} + +Decl *sema_analyse_parameterized_identifier(SemaContext *c, Path *decl_path, const char *name, SourceSpan span, Expr **params) +{ NameResolve name_resolve = { .path = decl_path, .span = span, .symbol = name }; Decl *alias = unit_resolve_parameterized_symbol(c->unit, &name_resolve); - if (!decl_ok(alias)) - { - return decl_poison(decl); - } + if (!decl_ok(alias)) return poisoned_decl; Module *module = alias->unit->module; - Expr **params = decl->define_decl.generic_params; unsigned parameter_count = vec_size(module->parameters); assert(parameter_count > 0); if (parameter_count != vec_size(params)) @@ -3225,11 +3242,11 @@ static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl) sema_error_at(extend_span_with_token(params[0]->span, vectail(params)->span), "The generic module expected %d arguments, but you supplied %d, did you make a mistake?", parameter_count, - vec_size(decl->define_decl.generic_params)); - return decl_poison(decl); + vec_size(params)); + return poisoned_decl; } scratch_buffer_clear(); - if (!sema_append_generate_parameterized_name(c, module, decl, true)) return decl_poison(decl); + if (!sema_append_generate_parameterized_name(c, module, params, true)) return poisoned_decl; TokenType ident_type = TOKEN_IDENT; const char *path_string = scratch_buffer_interned(); Module *instantiated_module = global_context_find_module(path_string); @@ -3242,50 +3259,31 @@ static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl) path->module = path_string; path->span = module->name->span; path->len = scratch_buffer.len; - instantiated_module = module_instantiate_generic(c, module, path, - decl->define_decl.generic_params, decl->span); - if (!instantiated_module) return decl_poison(decl); + instantiated_module = module_instantiate_generic(c, module, path, params, span); + scratch_buffer_clear(); + if (!sema_append_generate_parameterized_name(c, module, params, false)) return poisoned_decl; + if (!instantiated_module) return poisoned_decl; + instantiated_module->generic_suffix = scratch_buffer_copy(); sema_analyze_stage(instantiated_module, c->unit->module->stage - 1); } - if (global_context.errors_found) return decl_poison(decl); + if (global_context.errors_found) return poisoned_decl; Decl *symbol = module_find_symbol(instantiated_module, name); if (!symbol) { - SEMA_ERROR(decl, "The generic module '%s' does not have '%s' for this parameterization.", module->name->module, name); - return decl_poison(decl); + sema_error_at(span, "The generic module '%s' does not have '%s' for this parameterization.", module->name->module, name); + return poisoned_decl; } if (was_initiated && instantiated_module->contracts) { SourceSpan error_span = extend_span_with_token(params[0]->span, params[parameter_count - 1]->span); if (!sema_analyse_generic_module_contracts(c, instantiated_module, error_span)) { - return decl_poison(decl); + return poisoned_decl; } } - if (!sema_analyse_decl(c, symbol)) return false; + if (!sema_analyse_decl(c, symbol)) return poisoned_decl; unit_register_external_symbol(c->compilation_unit, symbol); - switch (decl->define_decl.define_kind) - { - case DEFINE_IDENT_GENERIC: - decl->define_decl.alias = symbol; - decl->type = symbol->type; - return true; - case DEFINE_TYPE_GENERIC: - { - scratch_buffer_clear(); - scratch_buffer_append(symbol->name); - sema_append_generate_parameterized_name(c, module, decl, false); - symbol->type->name = scratch_buffer_interned(); - Type *type = type_new(TYPE_TYPEDEF, decl->name); - decl->type = type; - type->decl = symbol; - decl->decl_kind = DECL_TYPEDEF; - type->canonical = symbol->type->canonical; - return true; - } - default: - UNREACHABLE - } + return symbol; } static inline bool sema_analyse_attribute_decl(SemaContext *c, Decl *decl) diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 9d34b4acc..54e37e204 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -507,6 +507,7 @@ static bool sema_binary_is_expr_lvalue(Expr *top_expr, Expr *expr) case EXPR_ANYSWITCH: case EXPR_VASPLAT: case EXPR_TEST_HOOK: + case EXPR_GENERIC_IDENT: goto ERR; } UNREACHABLE @@ -616,6 +617,7 @@ static bool expr_may_ref(Expr *expr) case EXPR_ANYSWITCH: case EXPR_VASPLAT: case EXPR_TEST_HOOK: + case EXPR_GENERIC_IDENT: return false; } UNREACHABLE @@ -6764,6 +6766,8 @@ RETRY: { case TYPE_INFO_POISON: return poisoned_type; + case TYPE_INFO_GENERIC: + TODO case TYPE_INFO_VECTOR: { ArraySize size; @@ -6968,6 +6972,23 @@ static inline Decl *sema_find_cached_lambda(SemaContext *context, Type *func_typ return NULL; } +static inline bool sema_expr_analyse_generic_ident(SemaContext *context, Expr *expr) +{ + Expr *parent = exprptr(expr->generic_ident_expr.parent); + if (parent->expr_kind != EXPR_IDENTIFIER) + { + SEMA_ERROR(parent, "Expected an identifier to parameterize."); + return false; + } + Decl *symbol = sema_analyse_parameterized_identifier(context, parent->identifier_expr.path, parent->identifier_expr.ident, parent->span, expr->generic_ident_expr.parmeters); + if (!decl_ok(symbol)) return false; + expr->expr_kind = EXPR_IDENTIFIER; + expr->identifier_expr.decl = symbol; + expr->resolve_status = RESOLVE_DONE; + expr->type = symbol->type; + return true; +} + static inline bool sema_expr_analyse_lambda(SemaContext *context, Type *func_type, Expr *expr) { Decl *decl = expr->lambda_expr; @@ -7447,6 +7468,8 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) case EXPR_VASPLAT: SEMA_ERROR(expr, "'$vasplat' can only be used inside of macros."); return false; + case EXPR_GENERIC_IDENT: + return sema_expr_analyse_generic_ident(context, expr); case EXPR_LAMBDA: return sema_expr_analyse_lambda(context, NULL, expr); case EXPR_CT_CHECKS: diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index da7079ce8..903566c13 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -87,6 +87,7 @@ Type *cast_numeric_arithmetic_promotion(Type *type); void cast_to_int_to_max_bit_size(SemaContext *context, Expr *lhs, Expr *rhs, Type *left_type, Type *right_type); bool sema_decl_if_cond(SemaContext *context, Decl *decl); bool sema_flattened_expr_is_const(SemaContext *context, Expr *expr); +Decl *sema_analyse_parameterized_identifier(SemaContext *c, Path *decl_path, const char *name, SourceSpan span, Expr **params); bool sema_analyse_checked(SemaContext *context, Ast *directive, SourceSpan span); diff --git a/src/compiler/sema_liveness.c b/src/compiler/sema_liveness.c index 4b0f70c3a..6b6d625cd 100644 --- a/src/compiler/sema_liveness.c +++ b/src/compiler/sema_liveness.c @@ -249,6 +249,7 @@ RETRY: case EXPR_CT_EVAL: case EXPR_CT_IDENT: case EXPR_ANYSWITCH: + case EXPR_GENERIC_IDENT: UNREACHABLE case EXPR_DESIGNATOR: sema_trace_expr_liveness(expr->designator_expr.value); diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 9922d2ebb..46f3c1cf4 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -311,6 +311,23 @@ INLINE bool sema_resolve_vatype(SemaContext *context, TypeInfo *type_info) return true; } +// Foo(<...>) +INLINE bool sema_resolve_generic_type(SemaContext *context, TypeInfo *type_info) +{ + TypeInfo *inner = type_info->generic.base; + if (inner->kind != TYPE_INFO_IDENTIFIER && inner->subtype != TYPE_COMPRESSED_NONE && !inner->optional) + { + SEMA_ERROR(inner, "Parameterization required a concrete type name here."); + return false; + } + assert(inner->resolve_status == RESOLVE_NOT_DONE); + + Decl *type = sema_analyse_parameterized_identifier(context, inner->unresolved.path, inner->unresolved.name, inner->span, type_info->generic.params); + if (!decl_ok(type)) return false; + type_info->type = type->type; + return true; +} + static inline bool sema_resolve_type(SemaContext *context, TypeInfo *type_info, bool allow_inferred_type, bool is_pointee) { // Ok, already resolved. @@ -338,6 +355,9 @@ static inline bool sema_resolve_type(SemaContext *context, TypeInfo *type_info, { case TYPE_INFO_POISON: UNREACHABLE + case TYPE_INFO_GENERIC: + if (!sema_resolve_generic_type(context, type_info)) return type_info_poison(type_info); + goto APPEND_QUALIFIERS; case TYPE_INFO_VATYPE: if (!sema_resolve_vatype(context, type_info)) return type_info_poison(type_info); goto APPEND_QUALIFIERS; diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index d11ac5488..4c5aa3e3c 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -159,6 +159,8 @@ void sema_analyze_stage(Module *module, AnalysisStage stage) case ANALYSIS_FUNCTIONS: sema_analysis_pass_functions(module); break; + case ANALYSIS_FINALIZE: + break; } if (global_context.errors_found) return; } diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 360283ffb..c8176e791 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -98,6 +98,8 @@ const char *token_type_to_string(TokenType type) return "<="; case TOKEN_LBRAPIPE: return "{|"; + case TOKEN_LGENPAR: + return "(<"; case TOKEN_LVEC: return "[<"; case TOKEN_MINUS_ASSIGN: @@ -120,6 +122,8 @@ const char *token_type_to_string(TokenType type) return "??"; case TOKEN_RBRAPIPE: return "|}"; + case TOKEN_RGENPAR: + return ">)"; case TOKEN_RVEC: return ">]"; case TOKEN_SCOPE: diff --git a/src/compiler/types.c b/src/compiler/types.c index ce944e1fb..f2ba5094e 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -201,27 +201,36 @@ const char *type_to_error_string(Type *type) { case TYPE_POISONED: return "poisoned"; - case TYPE_ENUM: - case TYPE_FAULTTYPE: - case TYPE_TYPEDEF: - case TYPE_STRUCT: case TYPE_VOID: case TYPE_BOOL: case ALL_INTS: case ALL_FLOATS: - case TYPE_UNION: - case TYPE_DISTINCT: - case TYPE_BITSTRUCT: case TYPE_ANYFAULT: case TYPE_UNTYPED_LIST: case TYPE_ANY: case TYPE_MEMBER: case TYPE_WILDCARD: return type->name; + case TYPE_ENUM: + case TYPE_FAULTTYPE: + case TYPE_TYPEDEF: + case TYPE_STRUCT: + case TYPE_UNION: + case TYPE_DISTINCT: + case TYPE_BITSTRUCT: + { + Decl *decl = type->decl; + if (!decl || !decl->unit || !decl->unit->module->generic_suffix) return type->name; + scratch_buffer_clear(); + scratch_buffer_append(decl->name); + scratch_buffer_append(decl->unit->module->generic_suffix); + return scratch_buffer_copy(); + } case TYPE_FUNC: scratch_buffer_clear(); + scratch_buffer_append("fn "); type_append_func_to_scratch(type->function.prototype); - return str_printf("fn %s", scratch_buffer_to_string()); + return scratch_buffer_copy(); case TYPE_INFERRED_VECTOR: return str_printf("%s[<*>]", type_to_error_string(type->array.base)); case TYPE_VECTOR: diff --git a/src/version.h b/src/version.h index f0497df4c..6f279ca5a 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.554" \ No newline at end of file +#define COMPILER_VERSION "0.4.555" \ No newline at end of file diff --git a/test/test_suite/define/common.c3 b/test/test_suite/define/common.c3 index 903fce882..79441626f 100644 --- a/test/test_suite/define/common.c3 +++ b/test/test_suite/define/common.c3 @@ -2,11 +2,11 @@ module foo; // def = def standard_foo = __stdin; -def someFunctionIntBool = someFunction; -def FooInt = Foo; -def A_CONST_INT = A_CONST; +def someFunctionIntBool = someFunction(); +def FooInt = Foo(); +def A_CONST_INT = A_CONST(); -def standard_foo = ofke; // #error: Expected '=' +def standard_foo() = ofke; // #error: Expected '=' def fn foo = fef; // #error: A type, variable, constant or attribute name was expected here def feokfe = fn void(int); // #error: Expected a function or variable name here def AOFKE = ofek; // #error: Expected a constant name here diff --git a/test/test_suite/define/test_at.c3 b/test/test_suite/define/test_at.c3 index ee6fa3941..54c28cfc0 100644 --- a/test/test_suite/define/test_at.c3 +++ b/test/test_suite/define/test_at.c3 @@ -1,4 +1,4 @@ -module foo; +module foo(); import std::io; macro @hello(Type thing) { @@ -9,8 +9,8 @@ module bar; import foo @public; -def intHello = foo::@hello; // #error: cannot be aliased -def @intHello = foo::hello; // #error: cannot use +def intHello = foo::@hello(); // #error: cannot be aliased +def @intHello = foo::hello(); // #error: cannot use fn void main(String[] args) { @intHello(42); diff --git a/test/test_suite/define/test_at_alias.c3 b/test/test_suite/define/test_at_alias.c3 index aca1f202e..d3b2f7586 100644 --- a/test/test_suite/define/test_at_alias.c3 +++ b/test/test_suite/define/test_at_alias.c3 @@ -1,4 +1,4 @@ -module foo; +module foo(); import std::io; macro @hello(Type thing) { @@ -9,7 +9,7 @@ module bar; import foo @public; -def @intHello = foo::@hello; +def @intHello = foo::@hello(); fn void main(String[] args) { @intHello(42); diff --git a/test/test_suite/functions/test_regression.c3t b/test/test_suite/functions/test_regression.c3t index 88e893ace..ff815d154 100644 --- a/test/test_suite/functions/test_regression.c3t +++ b/test/test_suite/functions/test_regression.c3t @@ -65,8 +65,8 @@ fn int Foo2.mutate(Foo2 *foo) return ++foo.x; } -def oopsInt = test2::argh; -def oopsDouble = test2::argh; +def oopsInt = test2::argh(); +def oopsDouble = test2::argh(); def Argh = fn int(double, Bobo); def Argh2 = fn int(double, Bobo); @@ -95,15 +95,15 @@ struct Foo int b; } -def getValueInt = test2::getValue; -def getValueDouble = test2::getValue; -def IntBlob = test2::Blob; -def DoubleBlob = Blob; -def getMultInt = test2::getMult; -def getMultDouble = test2::getMult; +def getValueInt = test2::getValue(); +def getValueDouble = test2::getValue(); +def IntBlob = test2::Blob(); +def DoubleBlob = Blob(); +def getMultInt = test2::getMult(); +def getMultDouble = test2::getMult(); -def IntArray = List; -def IntList = LinkedList; +def IntArray = List(); +def IntList = LinkedList(); enum MyEnum : int { @@ -173,7 +173,7 @@ module hello_world; import foo; extern fn int printf(char *, ...); -def doubleMult = foo::check; +def doubleMult = foo::check(); fn void hello() { @@ -181,14 +181,14 @@ fn void hello() printf("Mult %f\n", doubleMult(11.1)); } -module foo ; +module foo(); fn Type check(Type i) { return i * i; } -module test2 ; +module test2(); struct Blob { diff --git a/test/test_suite/functions/test_regression_mingw.c3t b/test/test_suite/functions/test_regression_mingw.c3t index 609eaf5b4..b00711e44 100644 --- a/test/test_suite/functions/test_regression_mingw.c3t +++ b/test/test_suite/functions/test_regression_mingw.c3t @@ -67,8 +67,8 @@ fn int Foo2.mutate(Foo2 *foo) -def oopsInt = test2::argh; -def oopsDouble = test2::argh; +def oopsInt = test2::argh(); +def oopsDouble = test2::argh(); def Argh = fn int(double, Bobo); def Argh2 = fn int(double, Bobo); @@ -97,15 +97,15 @@ struct Foo int b; } -def getValueInt = test2::getValue; -def getValueDouble = test2::getValue; -def IntBlob = test2::Blob; -def DoubleBlob = Blob; -def getMultInt = test2::getMult; -def getMultDouble = test2::getMult; +def getValueInt = test2::getValue(); +def getValueDouble = test2::getValue(); +def IntBlob = test2::Blob(); +def DoubleBlob = Blob(); +def getMultInt = test2::getMult(); +def getMultDouble = test2::getMult(); -def IntArray = List; -def IntList = LinkedList; +def IntArray = List(); +def IntList = LinkedList(); enum MyEnum : int { @@ -175,7 +175,7 @@ module hello_world; import foo; extern fn int printf(char *, ...); -def doubleMult = foo::check; +def doubleMult = foo::check(); fn void hello() { @@ -183,14 +183,14 @@ fn void hello() printf("Mult %f\n", doubleMult(11.1)); } -module foo ; +module foo(); fn Type check(Type i) { return i * i; } -module test2 ; +module test2(); struct Blob { diff --git a/test/test_suite/generic/enum_set_test.c3t b/test/test_suite/generic/enum_set_test.c3t index ae687bf93..9abca4ec1 100644 --- a/test/test_suite/generic/enum_set_test.c3t +++ b/test/test_suite/generic/enum_set_test.c3t @@ -3,7 +3,7 @@ module test; import std::io; import std::collections::enumset; -def AbcEnumSet = EnumSet; +def AbcEnumSet = EnumSet(); enum Abc { diff --git a/test/test_suite/generic/generic_copy.c3t b/test/test_suite/generic/generic_copy.c3t index a1935e92b..ade578db9 100644 --- a/test/test_suite/generic/generic_copy.c3t +++ b/test/test_suite/generic/generic_copy.c3t @@ -1,4 +1,4 @@ -module foo; +module foo(); fn void abc() { @@ -9,4 +9,4 @@ fn void abc() module tester; import foo; -def abc_my = foo::abc; \ No newline at end of file +def abc_my = foo::abc(); \ No newline at end of file diff --git a/test/test_suite/generic/generic_cyclic.c3 b/test/test_suite/generic/generic_cyclic.c3 index f4e48b167..b94285bd7 100644 --- a/test/test_suite/generic/generic_cyclic.c3 +++ b/test/test_suite/generic/generic_cyclic.c3 @@ -2,14 +2,14 @@ module test; import bar; -def BazTest = Baz; +def BazTest = Baz(); // #error: Recursive definition of 'BazTest' -struct Test // #error: Recursive definition of 'Test' +struct Test { BazTest t; } -module bar; +module bar(); struct Baz { diff --git a/test/test_suite/generic/generic_idents.c3t b/test/test_suite/generic/generic_idents.c3t index 9bd2803ad..0ea745771 100644 --- a/test/test_suite/generic/generic_idents.c3t +++ b/test/test_suite/generic/generic_idents.c3t @@ -1,4 +1,4 @@ -module gen ; +module gen(); fn Type mult(Type x) { @@ -13,8 +13,8 @@ fn Type addMult(Type x, Type a, Type b) module test; import gen; -def intMult = gen::mult; -def doubleAddMult = gen::addMult; +def intMult = gen::mult(); +def doubleAddMult = gen::addMult(); fn int getIt(int i) { diff --git a/test/test_suite/generic/generic_num.c3t b/test/test_suite/generic/generic_num.c3t index f9afef039..84186ea7e 100644 --- a/test/test_suite/generic/generic_num.c3t +++ b/test/test_suite/generic/generic_num.c3t @@ -1,5 +1,5 @@ // #target: macos-x64 -module hello; +module hello(); fn Type x(Type t) { @@ -8,7 +8,7 @@ fn Type x(Type t) module test; import hello; -def xint = hello::x; +def xint = hello::x(); import std::io; diff --git a/test/test_suite/generic/generic_recursion.c3t b/test/test_suite/generic/generic_recursion.c3t index 167026fe1..25c18a303 100644 --- a/test/test_suite/generic/generic_recursion.c3t +++ b/test/test_suite/generic/generic_recursion.c3t @@ -3,7 +3,7 @@ module test; import std::io; import std::collections::list; -def TreeNodeList = List; +def TreeNodeList = List(); struct TreeNode { diff --git a/test/test_suite/generic/incorrect_argument_type.c3 b/test/test_suite/generic/incorrect_argument_type.c3 index 9c965469b..5df6bb456 100644 --- a/test/test_suite/generic/incorrect_argument_type.c3 +++ b/test/test_suite/generic/incorrect_argument_type.c3 @@ -1,4 +1,4 @@ -module foo; +module foo(); struct Foo { @@ -7,5 +7,5 @@ struct Foo module bar; import foo; -def Bar = Foo; // #error: Expected a value, not a type -def Baz = Foo<5, 4>; // #error: Expected a type, not a value +def Bar = Foo(); // #error: Expected a value, not a type +def Baz = Foo(<5, 4>); // #error: Expected a type, not a value diff --git a/test/test_suite/methods/extension_method_generic.c3 b/test/test_suite/methods/extension_method_generic.c3 index 2d6fb3e2e..203bd926d 100644 --- a/test/test_suite/methods/extension_method_generic.c3 +++ b/test/test_suite/methods/extension_method_generic.c3 @@ -1,7 +1,7 @@ module compiler_c3; import std::collections::list; -def IntArray = List; +def IntArray = List(); extern fn void printf(char*, ...); diff --git a/test/test_suite/overloading/set_overload.c3t b/test/test_suite/overloading/set_overload.c3t index da55c75c8..ca237f5f0 100644 --- a/test/test_suite/overloading/set_overload.c3t +++ b/test/test_suite/overloading/set_overload.c3t @@ -3,7 +3,7 @@ module test; import std::collections::map; -def IntMap = HashMap; +def IntMap = HashMap(); fn void main() { diff --git a/test/test_suite/stdlib/map.c3t b/test/test_suite/stdlib/map.c3t index 617b8d040..909678a1b 100644 --- a/test/test_suite/stdlib/map.c3t +++ b/test/test_suite/stdlib/map.c3t @@ -6,8 +6,8 @@ import std::collections::map; struct Foo { int x; void* bar; } -def IntFooMap = HashMap; -def IntDoubleMap = HashMap; +def IntFooMap = HashMap(); +def IntDoubleMap = HashMap(); fn String Foo.to_string(Foo* foo, Allocator* allocator = mem::heap()) @dynamic { diff --git a/test/test_suite/stdlib/priorityqueue.c3t b/test/test_suite/stdlib/priorityqueue.c3t index 2e9980ac5..cbadfad4c 100644 --- a/test/test_suite/stdlib/priorityqueue.c3t +++ b/test/test_suite/stdlib/priorityqueue.c3t @@ -4,7 +4,7 @@ import std::io; import std::math; import std::collections::priorityqueue; -def FooPriorityQueue = PriorityQueue; +def FooPriorityQueue = PriorityQueue(); fn void main() { diff --git a/test/unit/stdlib/collections/enummap.c3 b/test/unit/stdlib/collections/enummap.c3 index badd3797e..166f2b9d2 100644 --- a/test/unit/stdlib/collections/enummap.c3 +++ b/test/unit/stdlib/collections/enummap.c3 @@ -8,7 +8,7 @@ enum FooEnum THREE, } -def FooEnumMap = EnumMap; +def FooEnumMap = EnumMap(); fn void! enums() { diff --git a/test/unit/stdlib/collections/linkedlist.c3 b/test/unit/stdlib/collections/linkedlist.c3 index aadeb09fe..630f08633 100644 --- a/test/unit/stdlib/collections/linkedlist.c3 +++ b/test/unit/stdlib/collections/linkedlist.c3 @@ -1,7 +1,7 @@ module linkedlist_test @test; import std::collections::linkedlist; -def IntList = LinkedList; +def IntList = LinkedList(); fn void! test_push() { diff --git a/test/unit/stdlib/collections/list.c3 b/test/unit/stdlib/collections/list.c3 index 952a72292..c863718c2 100644 --- a/test/unit/stdlib/collections/list.c3 +++ b/test/unit/stdlib/collections/list.c3 @@ -1,8 +1,8 @@ module listtests @test; import std::collections::list; -def IntList = List; -def PtrList = List; +def IntList = List(); +def PtrList = List(); fn void! test_delete_contains_index() { diff --git a/test/unit/stdlib/collections/range.c3 b/test/unit/stdlib/collections/range.c3 index d05961186..a88d89ebb 100644 --- a/test/unit/stdlib/collections/range.c3 +++ b/test/unit/stdlib/collections/range.c3 @@ -1,8 +1,8 @@ module range_test @test; import std::collections::range; -def IntRange = Range; -def IntExRange = ExclusiveRange; +def IntRange = Range(); +def IntExRange = ExclusiveRange(); fn void! test_exrange() { diff --git a/test/unit/stdlib/sort/quicksort.c3 b/test/unit/stdlib/sort/quicksort.c3 index f399c46ce..7c47c1059 100644 --- a/test/unit/stdlib/sort/quicksort.c3 +++ b/test/unit/stdlib/sort/quicksort.c3 @@ -2,7 +2,7 @@ module sort_test @test; import std::sort; import std::sort::quicksort; -def qs_int = quicksort::sort; +def qs_int = quicksort::sort(); fn void quicksort() { @@ -22,7 +22,8 @@ fn void quicksort() } def Cmp = fn int(int*, int*); -def qs_int_ref = quicksort::sort_ref_fn; + +def qs_int_ref = quicksort::sort_ref_fn(); fn void quicksort_with() { @@ -41,7 +42,7 @@ fn void quicksort_with() } } -def qs_int_fn = quicksort::sort_fn; +def qs_int_fn = quicksort::sort_fn(); fn void quicksort_with2() {