Generic modules also accepts integers and booleans.

This commit is contained in:
Christoffer Lerno
2022-10-23 19:16:25 +02:00
parent 155c8862c9
commit 02374c6aab
11 changed files with 308 additions and 49 deletions

42
releasenotes.md Normal file
View File

@@ -0,0 +1,42 @@
Release Notes
0.4.0 Change List
- Compatibility with LLVM 16.
- Dropped LLVM 12 support.
- Updated memory allocators. Added @scoped and @pool macros.
- Various bug fixes.
- Constant pointers may be compile time evaluated.
- Added many new builtins.
- Emit asm using --emit-asm.
- Added --nostdlib and --nolibc.
- Fixes to adding libraries at link time.
- Various improved error messages.
- Windows debug info fixes.
- Add of `foreach_r`.
- Script downloading the MSVC SDK to cross compile to windows.
- Many standard library additions.
- Extension methods may be added for built-in types.
- Macros may take vector and array arguments generic over length.
- Macro varargs with $vaarg, $vacount etc.
- Many vector builtins added as dot methods.
- in / out / inout doc parameters checked.
- Initial inline asm support for aarch64 and x64.
- Single line short function declaration.
- Added `$checks` builtin.
- Optional single module compilation.
- Static initialization / finalization to have code running at start/end.
- C3 custom printf function in the stdlib.
- `[]=` overload now works correctly.
- More compile time reflection added and general cleanup done.
- usize/isize/iptrdiff/uptrdiff replaced by usz/isz.
- Add `var` to allow type inference on regular variables.
- LLVM codegen optimizations.
- `??` now allows chaining another optional.
- int128 support on all platforms.
- `import` is now allowed anywhere at the top level.
- `project.c3p` renamed `project.json`
- Update to project properties, e.g. "libs" -> "dependencies" etc.
- $$TIME, $$DATE and $$FUNCTION builtin defines added.
- Improvements to untyped lists.
- Various builtins added: $$prefetch, $$reverse, $$shufflevector etc.

View File

@@ -609,7 +609,7 @@ typedef struct
SourceSpan span;
};
};
TypeInfo **generic_params;
Expr **generic_params;
};
Decl *alias;
};
@@ -1451,7 +1451,7 @@ typedef struct Module_
AnalysisStage stage : 6;
Ast **files; // Asts
AstId docs;
Decl** method_extensions;
HTable symbols;
struct CompilationUnit_ **units;

View File

@@ -901,7 +901,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
{
case DEFINE_TYPE_GENERIC:
case DEFINE_IDENT_GENERIC:
MACRO_COPY_TYPE_LIST(decl->define_decl.generic_params);
MACRO_COPY_EXPR_LIST(decl->define_decl.generic_params);
break;
case DEFINE_IDENT_ALIAS:
break;

View File

@@ -89,6 +89,11 @@ static Expr *parse_precedence(ParseContext *c, Precedence precedence)
return parse_precedence_with_left_side(c, left_side, precedence);
}
Expr *parse_generic_parameter(ParseContext *c)
{
return parse_precedence(c, PREC_ADDITIVE);
}
Expr *parse_expr_or_initializer_list(ParseContext *c)
{
return parse_expr(c);

View File

@@ -90,7 +90,7 @@ static inline bool parse_top_level_block(ParseContext *c, Decl ***decls, TokenTy
CONSUME_OR_RET(TOKEN_COLON, false);
while (!tok_is(c, end1) && !tok_is(c, end2) && !tok_is(c, end3) && !tok_is(c, TOKEN_EOF))
{
Decl *decl = parse_top_level_statement(c, false);
Decl *decl = parse_top_level_statement(c, NULL);
if (!decl) continue;
assert(decl);
if (decl_ok(decl))
@@ -173,7 +173,7 @@ static inline Decl *parse_ct_case(ParseContext *c)
{
TokenType type = c->tok;
if (type == TOKEN_CT_DEFAULT || type == TOKEN_CT_CASE || type == TOKEN_CT_ENDSWITCH) break;
ASSIGN_DECL_OR_RET(Decl * stmt, parse_top_level_statement(c, false), poisoned_decl);
ASSIGN_DECL_OR_RET(Decl *stmt, parse_top_level_statement(c, NULL), poisoned_decl);
vec_add(decl->ct_case_decl.body, stmt);
}
return decl;
@@ -282,12 +282,13 @@ static inline bool parse_optional_module_params(ParseContext *c, const char ***t
switch (c->tok)
{
case TOKEN_TYPE_IDENT:
case TOKEN_CONST_IDENT:
break;
case TOKEN_COMMA:
SEMA_ERROR_HERE("Unexpected ','");
return false;
case TOKEN_IDENT:
SEMA_ERROR_HERE("The module parameter must be a type.");
SEMA_ERROR_HERE("The module parameter must be a type or a constant.");
return false;
case TOKEN_CT_IDENT:
case TOKEN_CT_TYPE_IDENT:
@@ -309,13 +310,8 @@ static inline bool parse_optional_module_params(ParseContext *c, const char ***t
/**
* module ::= MODULE module_path ('<' module_params '>')? EOS
*/
bool parse_module(ParseContext *c)
bool parse_module(ParseContext *c, AstId docs)
{
if (!try_consume(c, TOKEN_MODULE))
{
return context_set_module_from_filename(c);
}
bool is_private = try_consume(c, TOKEN_PRIVATE);
if (tok_is(c, TOKEN_STRING))
@@ -359,11 +355,30 @@ bool parse_module(ParseContext *c)
const char **generic_parameters = NULL;
if (!parse_optional_module_params(c, &generic_parameters))
{
context_set_module(c, path, NULL, is_private);
if (!context_set_module(c, path, NULL, is_private)) return false;
recover_top_level(c);
if (docs)
{
SEMA_ERROR(astptr(docs), "Contracts cannot be use with non-generic modules.");
return false;
}
return true;
}
context_set_module(c, path, generic_parameters, is_private);
if (!context_set_module(c, path, generic_parameters, is_private)) return false;
if (docs)
{
AstId old_docs = c->unit->module->docs;
if (old_docs)
{
Ast *last = ast_last(astptr(old_docs));
last->next = docs;
}
else
{
c->unit->module->docs = docs;
}
}
CONSUME_EOS_OR_RET(false);
return true;
}
@@ -1629,24 +1644,24 @@ static bool parse_macro_arguments(ParseContext *c, Visibility visibility, Decl *
}
/**
* define_parameters ::= type (',' type)* '>'
* define_parameters ::= expr (',' expr)* '>'
*
* @return NULL if parsing failed, otherwise a list of Type*
*/
static inline TypeInfo **parse_generic_parameters(ParseContext *c)
static inline Expr **parse_generic_parameters(ParseContext *c)
{
TypeInfo **types = NULL;
Expr **params = NULL;
while (!try_consume(c, TOKEN_GREATER))
{
ASSIGN_TYPE_OR_RET(TypeInfo *type_info, parse_type(c), NULL);
vec_add(types, type_info);
ASSIGN_EXPR_OR_RET(Expr *arg, parse_generic_parameter(c), NULL);
vec_add(params, arg);
TokenType tok = c->tok;
if (tok != TOKEN_RPAREN && tok != TOKEN_GREATER)
{
TRY_CONSUME_OR_RET(TOKEN_COMMA, "Expected ',' after argument.", NULL);
}
}
return types;
return params;
}
/**
@@ -1693,7 +1708,7 @@ static inline Decl *parse_define_type(ParseContext *c, Visibility visibility)
// 3. Do we have '<' if so it's a parameterized type e.g. foo::bar::Type<int, double>.
if (try_consume(c, TOKEN_LESS))
{
TypeInfo **params = parse_generic_parameters(c);
Expr **params = parse_generic_parameters(c);
if (!params) return poisoned_decl;
Decl *decl = decl_new(DECL_DEFINE, alias_name, name_loc, visibility);
decl->define_decl.define_kind = DEFINE_TYPE_GENERIC;
@@ -1796,7 +1811,7 @@ static inline Decl *parse_define_ident(ParseContext *c, Visibility visibility)
if (try_consume(c, TOKEN_LESS))
{
decl->define_decl.define_kind = DEFINE_IDENT_GENERIC;
TypeInfo **params = parse_generic_parameters(c);
Expr **params = parse_generic_parameters(c);
if (!params) return poisoned_decl;
decl->define_decl.generic_params = params;
}
@@ -2519,7 +2534,7 @@ static bool parse_docs(ParseContext *c, AstId *docs_ref)
* @param visibility
* @return Decl* or a poison value if parsing failed
*/
Decl *parse_top_level_statement(ParseContext *c, bool allow_import)
Decl *parse_top_level_statement(ParseContext *c, ParseContext **c_ref)
{
AstId docs = 0;
if (!parse_docs(c, &docs)) return poisoned_decl;
@@ -2539,8 +2554,35 @@ Decl *parse_top_level_statement(ParseContext *c, bool allow_import)
Decl *decl;
TokenType tok = c->tok;
if (tok != TOKEN_MODULE && !c->unit->module)
{
if (!context_set_module_from_filename(c)) return poisoned_decl;
// Pass the docs to the next thing.
}
switch (tok)
{
case TOKEN_MODULE:
if (visibility != VISIBLE_PUBLIC)
{
SEMA_ERROR_HERE("Did not expect visibility before 'module'.");
return poisoned_decl;
}
if (!c_ref)
{
SEMA_ERROR_HERE("'module' cannot appear inside of conditional compilation.");
return poisoned_decl;
}
advance(c);
if (c->unit->module)
{
ParseContext *new_context = CALLOCS(ParseContext);
*new_context = *c;
new_context->unit = unit_create(c->unit->file);
*c_ref = c = new_context;
}
if (!parse_module(c, docs)) return poisoned_decl;
return NULL;
case TOKEN_DOCS_START:
if (visibility != VISIBLE_PUBLIC)
{
@@ -2596,7 +2638,7 @@ Decl *parse_top_level_statement(ParseContext *c, bool allow_import)
}
case TOKEN_IMPORT:
if (!check_no_visibility_before(c, visibility)) return poisoned_decl;
if (!allow_import)
if (!c_ref)
{
SEMA_ERROR_HERE("'import' may not appear inside a compile time statement.");
return poisoned_decl;

View File

@@ -64,19 +64,9 @@ static inline void parse_translation_unit(ParseContext *c)
// Prime everything
advance(c);
advance(c);
NEXT_CONTEXT:
if (!parse_module(c)) return;
while (!tok_is(c, TOKEN_EOF))
{
if (tok_is(c, TOKEN_MODULE))
{
ParseContext *new_context = CALLOCS(ParseContext);
*new_context = *c;
new_context->unit = unit_create(c->unit->file);
c = new_context;
goto NEXT_CONTEXT;
}
Decl *decl = parse_top_level_statement(c, true);
Decl *decl = parse_top_level_statement(c, &c);
if (!decl) continue;
if (decl_ok(decl))
{

View File

@@ -16,7 +16,7 @@
#define TRY_CONSUME_AFTER(_tok, _message, _type) do { if (!try_consume(c, _tok)) { sema_error_at_after(c->prev_span, _message); return _type; } } while(0)
#define CHECK_EXPR_OR_RET(_expr) do { if (!expr_ok(_expr)) return _expr; } while(0)
Decl *parse_top_level_statement(ParseContext *c, bool allow_import);
Decl *parse_top_level_statement(ParseContext *c, ParseContext **new_context);
Ast *parse_ct_assert_stmt(ParseContext *c);
Ast *parse_stmt(ParseContext *c);
Path *parse_path_prefix(ParseContext *c, bool *had_error);
@@ -52,8 +52,8 @@ parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref, Dec
bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool *splat, bool vasplat);
Expr *parse_type_compound_literal_expr_after_type(ParseContext *c, TypeInfo *type_info);
bool parse_module(ParseContext *c);
bool parse_module(ParseContext *c, AstId docs);
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);

View File

@@ -51,7 +51,7 @@ static inline bool sema_analyse_distinct(SemaContext *context, Decl *decl);
static CompilationUnit *unit_copy(Module *module, CompilationUnit *unit);
static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl);
static Module *module_instantiate_generic(Module *module, Path *path, TypeInfo **parms);
static Module *module_instantiate_generic(Module *module, Path *path, Expr **params);
static inline bool sema_analyse_enum_param(SemaContext *context, Decl *param, bool *has_default);
static inline bool sema_analyse_enum(SemaContext *context, Decl *decl);
@@ -2500,7 +2500,6 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local)
}
static CompilationUnit *unit_copy(Module *module, CompilationUnit *unit)
{
CompilationUnit *copy = unit_create(unit->file);
@@ -2511,7 +2510,7 @@ static CompilationUnit *unit_copy(Module *module, CompilationUnit *unit)
return copy;
}
static Module *module_instantiate_generic(Module *module, Path *path, TypeInfo **parms)
static Module *module_instantiate_generic(Module *module, Path *path, Expr **params)
{
Module *new_module = compiler_find_or_create_module(path, NULL, module->is_private);
new_module->is_generic = true;
@@ -2523,10 +2522,20 @@ static Module *module_instantiate_generic(Module *module, Path *path, TypeInfo *
CompilationUnit *first_context = new_module->units[0];
VECEACH(module->parameters, i)
{
const char *param = module->parameters[i];
Decl *decl = decl_new_with_type(param, parms[i]->span, DECL_TYPEDEF, VISIBLE_PUBLIC);
const char *param_name = module->parameters[i];
Expr *param = params[i];
if (param->expr_kind != EXPR_TYPEINFO)
{
Decl *decl = decl_new_var(param_name, param->span, NULL, VARDECL_CONST, VISIBLE_PUBLIC);
decl->var.init_expr = param;
decl->type = param->type;
decl->resolve_status = RESOLVE_NOT_DONE;
vec_add(first_context->global_decls, decl);
continue;
}
Decl *decl = decl_new_with_type(param_name, params[i]->span, DECL_TYPEDEF, VISIBLE_PUBLIC);
decl->resolve_status = RESOLVE_DONE;
TypeInfo *type_info = parms[i];
TypeInfo *type_info = param->type_expr;
assert(type_info->resolve_status == RESOLVE_DONE);
decl->typedef_decl.type_info = type_info;
decl->type->name = decl->name;
@@ -2576,7 +2585,7 @@ static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl)
}
Module *module = alias->unit->module;
TypeInfo **params = decl->define_decl.generic_params;
Expr **params = decl->define_decl.generic_params;
unsigned parameter_count = vec_size(module->parameters);
assert(parameter_count > 0);
if (parameter_count != vec_size(params))
@@ -2591,10 +2600,62 @@ static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl)
scratch_buffer_clear();
scratch_buffer_append_len(module->name->module, module->name->len);
scratch_buffer_append("$$");
FOREACH_BEGIN_IDX(i, TypeInfo *type_info, decl->define_decl.generic_params)
if (!sema_resolve_type_info(c, type_info)) return decl_poison(decl);
FOREACH_BEGIN_IDX(i, Expr *param, decl->define_decl.generic_params)
if (i != 0) scratch_buffer_append_char('.');
type_mangle_introspect_name_to_buffer(type_info->type->canonical);
if (param->expr_kind == EXPR_TYPEINFO)
{
TypeInfo *type = param->type_expr;
if (!sema_resolve_type_info(c, type)) return decl_poison(decl);
if (type->kind == TYPE_OPTIONAL)
{
SEMA_ERROR(type, "Expected a non-optional type.");
return poisoned_decl;
}
if (type_is_invalid_storage_type(type->type))
{
SEMA_ERROR(type, "Expected a runtime type.");
return poisoned_decl;
}
type_mangle_introspect_name_to_buffer(type->type->canonical);
}
else
{
if (!sema_analyse_ct_expr(c, param)) return decl_poison(decl);
Type *type = param->type->canonical;
if (!type_is_integer_or_bool_kind(type))
{
SEMA_ERROR(param, "Only integer and boolean types may be generic arguments.");
return poisoned_decl;
}
assert(expr_is_const(param));
if (type == type_bool)
{
scratch_buffer_append_char(param->const_expr.b ? 't' : 'f');
}
else
{
char *maybe_neg = &scratch_buffer.str[scratch_buffer.len];
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
{
if (type_is_signed(type))
{
scratch_buffer_append_signed_int(param->const_expr.ixx.i.low);
}
else
{
scratch_buffer_append_unsigned_int(param->const_expr.ixx.i.high);
}
}
// Replace - with _
if (maybe_neg[0] == '-') maybe_neg[0] = '_';
}
}
FOREACH_END();
TokenType ident_type = TOKEN_IDENT;
const char *path_string = scratch_buffer_interned();

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.3.95"
#define COMPILER_VERSION "0.3.96"

View File

@@ -0,0 +1,61 @@
// #target: macos-x64
module hello<Type, FOO>;
fn Type x(Type t)
{
return t * t + FOO;
}
module test;
import hello;
define xint = hello::x<int, -123>;
import std::io;
fn void main()
{
io::printfln("%d", xint(4));
}
/* #expect: test.ll
define void @test_main() #0 {
entry:
%retparam = alloca i64, align 8
%taddr = alloca %"char[]", align 8
%vararg = alloca %"variant[]", align 8
%varargslots = alloca [1 x %variant], align 16
%taddr1 = alloca i32, align 4
store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i64 2 }, %"char[]"* %taddr, align 8
%0 = bitcast %"char[]"* %taddr to { i8*, i64 }*
%1 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %0, i32 0, i32 0
%lo = load i8*, i8** %1, align 8
%2 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %0, i32 0, i32 1
%hi = load i64, i64* %2, align 8
%3 = call i32 @"hello$$int._123_x"(i32 4)
store i32 %3, i32* %taddr1, align 4
%4 = bitcast i32* %taddr1 to i8*
%5 = insertvalue %variant undef, i8* %4, 0
%6 = insertvalue %variant %5, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
%7 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots, i64 0, i64 0
store %variant %6, %variant* %7, align 16
%8 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 1
store i64 1, i64* %8, align 8
%9 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 0
%10 = bitcast [1 x %variant]* %varargslots to %variant*
store %variant* %10, %variant** %9, align 8
%11 = bitcast %"variant[]"* %vararg to { i8*, i64 }*
%12 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %11, i32 0, i32 0
%lo2 = load i8*, i8** %12, align 8
%13 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %11, i32 0, i32 1
%hi3 = load i64, i64* %13, align 8
%14 = call i64 @std_io_printfln(i64* %retparam, i8* %lo, i64 %hi, i8* %lo2, i64 %hi3)
%not_err = icmp eq i64 %14, 0
br i1 %not_err, label %after_check, label %voiderr
after_check: ; preds = %entry
br label %voiderr
voiderr: ; preds = %after_check, %entry
ret void
}

View File

@@ -0,0 +1,58 @@
// #target: macos-x64
module hello<Type, FOO>;
fn Type x(Type t)
{
return t * t + FOO;
}
module test;
import hello;
define xint = hello::x<int, -123>;
import std::io;
fn void main()
{
io::printfln("%d", xint(4));
}
/* #expect: test.ll
define void @test_main() #0 {
entry:
%retparam = alloca i64, align 8
%taddr = alloca %"char[]", align 8
%vararg = alloca %"variant[]", align 8
%varargslots = alloca [1 x %variant], align 16
%taddr1 = alloca i32, align 4
store %"char[]" { ptr @.str, i64 2 }, ptr %taddr, align 8
%0 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 0
%lo = load ptr, ptr %0, align 8
%1 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 1
%hi = load i64, ptr %1, align 8
%2 = call i32 @"hello$$int._123_x"(i32 4)
store i32 %2, ptr %taddr1, align 4
%3 = insertvalue %variant undef, ptr %taddr1, 0
%4 = insertvalue %variant %3, i64 ptrtoint (ptr @"ct$int" to i64), 1
%5 = getelementptr inbounds [1 x %variant], ptr %varargslots, i64 0, i64 0
store %variant %4, ptr %5, align 16
%6 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 1
store i64 1, ptr %6, align 8
%7 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 0
store ptr %varargslots, ptr %7, align 8
%8 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 0
%lo2 = load ptr, ptr %8, align 8
%9 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 1
%hi3 = load i64, ptr %9, align 8
%10 = call i64 @std_io_printfln(ptr %retparam, ptr %lo, i64 %hi, ptr %lo2, i64 %hi3)
%not_err = icmp eq i64 %10, 0
br i1 %not_err, label %after_check, label %voiderr
after_check: ; preds = %entry
br label %voiderr
voiderr: ; preds = %after_check, %entry
ret void
}