"[]=" now works as overload. Improved eval resolution. Added $$FUNCPTR

This commit is contained in:
Christoffer Lerno
2022-10-05 22:44:31 +02:00
committed by Christoffer Lerno
parent 05d4ec55f6
commit db06f99445
27 changed files with 1960 additions and 1190 deletions

View File

@@ -3,6 +3,7 @@
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::builtin;
import libc;
import std::hash;
fault IteratorResult
{
@@ -157,4 +158,5 @@ macro uint ichar.hash(ichar c) = c;
macro uint long.hash(long i) = (uint)((i >> 32) ^ i);
macro uint ulong.hash(ulong i) = (uint)((i >> 32) ^ i);
macro uint bool.hash(bool b) = (uint)b;
macro uint typeid.hash(typeid t) = (uint)(((uptr)t >> 32) ^ (uptr)t);
macro uint typeid.hash(typeid t) = (uint)(((uptr)t >> 32) ^ (uptr)t);
macro uint char[].hash(char[] c) = (uint)fnv32a::encode(c);

41
lib/std/hash/fnv32a.c3 Normal file
View File

@@ -0,0 +1,41 @@
// Copyright (c) 2021 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::hash::fnv32a;
define Fnv32a = distinct uint;
private const FNV32A_START = 0x811c9dc5;
private const FNV32A_MUL = 0x01000193;
private macro void @update(uint &h, char x) => h = (h * FNV32A_MUL) ^ x;
fn void Fnv32a.init(Fnv32a* this)
{
*this = FNV32A_START;
}
fn void Fnv32a.update(Fnv32a* this, char[] data)
{
uint h = (uint)*this;
foreach (char x : data)
{
@update(h, x);
}
*this = (Fnv32a)h;
}
macro void Fnv32a.update_char(Fnv32a* this, char c)
{
@update(*this, x);
}
fn uint encode(char[] data)
{
uint h = FNV32A_START;
foreach (char x : data)
{
@update(h, x);
}
return h;
}

View File

@@ -22,13 +22,13 @@ struct HashMap
}
/**
* @require capacity > 0
* @require load_factor > 0.0 && load_factor < 1.0
* @require map.table.len == 0
* @require capacity < MAXIMUM_CAPACITY
* @require allocator != null
* @require capacity > 0 "The capacity must be 1 or higher"
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require !map.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
* @require allocator != null "The allocator must be non-null"
**/
fn void HashMap.init(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* allocator = mem::current_allocator())
fn void HashMap.init(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* allocator = mem::temp_allocator())
{
capacity = math::next_power_of_2(capacity);
map.allocator = allocator;
@@ -37,7 +37,7 @@ fn void HashMap.init(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, flo
map.table = array::make(Entry*, capacity, allocator);
}
fn void HashMap.init_from_map(HashMap* map, HashMap* other_map, Allocator* allocator = mem::current_allocator())
fn void HashMap.init_from_map(HashMap* map, HashMap* other_map, Allocator* allocator = mem::temp_allocator())
{
map.init(other_map.table.len, other_map.load_factor, allocator);
map.put_all_for_create(other_map);
@@ -59,7 +59,7 @@ fn Value*! HashMap.get_ref(HashMap* map, Key key)
return SearchResult.MISSING!;
}
fn Value! HashMap.get(HashMap* map, Key key)
fn Value! HashMap.get(HashMap* map, Key key) @operator([])
{
return *map.get_ref(key) @inline;
}
@@ -70,8 +70,13 @@ fn bool HashMap.has_key(HashMap* map, Key key)
return try(map.get_ref(key));
}
fn bool HashMap.set(HashMap* map, Key key, Value value)
fn bool HashMap.set(HashMap* map, Key key, Value value) @operator([]=)
{
// If the map isn't initialized, use the defaults to initialize it.
if (!map.allocator)
{
map.init();
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (Entry *e = map.table[index]; e != null; e = e.next)
@@ -106,20 +111,17 @@ fn void HashMap.clear(HashMap* map)
fn void HashMap.destroy(HashMap* map)
{
if (!map.allocator) return;
map.clear();
map.free(map.table.ptr);
map.table = Entry*[] {};
}
fn Key[] HashMap.key_tlist(HashMap* map)
{
return map.key_list(mem::temp_allocator());
}
fn Key[] HashMap.key_list(HashMap* map, Allocator* allocator = null)
fn Key[] HashMap.key_list(HashMap* map, Allocator* allocator = mem::temp_allocator())
{
if (!map.count) return Key[] {};
Key[] list = array::make(Key, map.count, allocator ?: map.allocator);
Key[] list = array::make(Key, map.count, allocator);
usize index = 0;
foreach (Entry* entry : map.table)
{
@@ -132,15 +134,10 @@ fn Key[] HashMap.key_list(HashMap* map, Allocator* allocator = null)
return list;
}
fn Value[] HashMap.value_tlist(HashMap* map)
{
return map.value_list(mem::temp_allocator());
}
fn Value[] HashMap.value_list(HashMap* map, Allocator* allocator = null)
fn Value[] HashMap.value_list(HashMap* map, Allocator* allocator = mem::temp_allocator())
{
if (!map.count) return Value[] {};
Value[] list = array::make(Value, map.count, allocator ?: map.allocator);
Value[] list = array::make(Value, map.count, allocator);
usize index = 0;
foreach (Entry* entry : map.table)
{

View File

@@ -1,4 +1,4 @@
module hash;
module std::hash;
import libc;
// Code adapted from Odin's hash.odin

View File

@@ -345,7 +345,6 @@ struct TypeInfo_
TypeInfoCompressedKind subtype : 4;
Type *type;
SourceSpan span;
union
{
struct
@@ -773,6 +772,13 @@ typedef struct
Range range;
} ExprSubscript;
typedef struct
{
ExprId expr;
DeclId method;
ExprId index;
} ExprSubscriptAssign;
typedef struct
{
ExprId left;
@@ -1105,6 +1111,7 @@ struct Expr_
ExprBuiltinAccess builtin_access_expr;
ExprCatchUnwrap catch_unwrap_expr; // 24
ExprSubscript subscript_expr; // 12
ExprSubscriptAssign subscript_assign_expr;
ExprAccess access_expr; // 16
ExprDesignator designator_expr; // 16
ExprIdentifier identifier_expr; // 24
@@ -1837,6 +1844,7 @@ extern const char *kw_return;
extern const char *kw_type;
extern const char *kw_FILE;
extern const char *kw_FUNC;
extern const char *kw_FUNCPTR;
extern const char *kw_LINE;
extern const char *kw_LINEREAL;
extern const char *kw_incr;

View File

@@ -356,6 +356,8 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr)
MACRO_COPY_EXPRID(expr->slice_assign_expr.left);
MACRO_COPY_EXPRID(expr->slice_assign_expr.right);
return expr;
case EXPR_SUBSCRIPT_ASSIGN:
UNREACHABLE
case EXPR_SLICE:
case EXPR_SUBSCRIPT:
case EXPR_SUBSCRIPT_ADDR:

View File

@@ -240,6 +240,7 @@ typedef enum
EXPR_SLICE_COPY,
EXPR_SUBSCRIPT,
EXPR_SUBSCRIPT_ADDR,
EXPR_SUBSCRIPT_ASSIGN,
EXPR_POINTER_OFFSET,
EXPR_STRINGIFY,
EXPR_ARGV_TO_SUBARRAY,

View File

@@ -132,6 +132,7 @@ bool expr_may_addr(Expr *expr)
case EXPR_VASPLAT:
case EXPR_OPERATOR_CHARS:
case EXPR_CT_CHECKS:
case EXPR_SUBSCRIPT_ASSIGN:
return false;
}
UNREACHABLE
@@ -319,6 +320,7 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind)
case EXPR_ARGV_TO_SUBARRAY:
case EXPR_CT_ARG:
case EXPR_ASM:
case EXPR_SUBSCRIPT_ASSIGN:
UNREACHABLE
case EXPR_NOP:
return true;
@@ -703,6 +705,7 @@ bool expr_is_pure(Expr *expr)
case EXPR_TRY_UNWRAP:
case EXPR_TRY_UNWRAP_CHAIN:
case EXPR_FORCE_UNWRAP:
case EXPR_SUBSCRIPT_ASSIGN:
return false;
case EXPR_CAST:
return exprid_is_pure(expr->cast_expr.expr);

View File

@@ -5688,6 +5688,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr)
return;
case EXPR_TYPEID:
case EXPR_GROUP:
case EXPR_SUBSCRIPT_ASSIGN:
// These are folded in the semantic analysis step.
UNREACHABLE
case EXPR_IDENTIFIER:

View File

@@ -849,6 +849,7 @@ Expr *recursive_may_narrow_float(Expr *expr, Type *type)
case EXPR_VASPLAT:
case EXPR_OPERATOR_CHARS:
case EXPR_CT_CHECKS:
case EXPR_SUBSCRIPT_ASSIGN:
UNREACHABLE
case EXPR_BUILTIN_ACCESS:
@@ -953,6 +954,7 @@ Expr *recursive_may_narrow_int(Expr *expr, Type *type)
case EXPR_SLICE:
case EXPR_SUBSCRIPT:
case EXPR_RETVAL:
case EXPR_SUBSCRIPT_ASSIGN:
case EXPR_TYPEID_INFO:
if (type_size(expr->type) > type_size(type)) return expr;
return NULL;

View File

@@ -1247,13 +1247,7 @@ static inline bool sema_analyse_operator_element_set(Decl *method)
{
TypeInfo *rtype;
Decl **params;
if (!sema_analyse_operator_common(method, &rtype, &params, 3)) return false;
if (rtype->type->canonical != type_void)
{
SEMA_ERROR(rtype, "The return type should be 'void'.");
return false;
}
return true;
return sema_analyse_operator_common(method, &rtype, &params, 3);
}
static inline bool sema_analyse_operator_len(Decl *method)

View File

@@ -7,6 +7,13 @@
const char *ct_eval_error = "EVAL_ERROR";
typedef enum
{
SUBSCRIPT_EVAL_VALUE,
SUBSCRIPT_EVAL_REF,
SUBSCRIPT_EVAL_ASSIGN
} SubscriptEval;
typedef struct
{
bool macro;
@@ -17,7 +24,7 @@ typedef struct
Signature *signature;
} CalledDecl;
static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, bool is_addr);
static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, SubscriptEval eval_type);
static inline bool sema_expr_analyse_pointer_offset(SemaContext *context, Expr *expr);
static inline bool sema_expr_analyse_slice(SemaContext *context, Expr *expr);
static inline bool sema_expr_analyse_group(SemaContext *context, Expr *expr);
@@ -208,31 +215,68 @@ Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr)
return context->macro_varargs[(size_t)index_val.i.low];
}
const char *sema_ct_eval_expr(SemaContext *c, const char *expr_type, Expr *inner, TokenType *type, Path **path_ref, bool report_missing)
Expr *sema_ct_eval_expr(SemaContext *c, bool is_type_eval, Expr *inner, bool report_missing)
{
Path *path = NULL;
if (!sema_analyse_expr(c, inner)) return false;
if (!expr_is_const_string(inner))
{
SEMA_ERROR(inner, "'%s' expects a constant string as the argument.", expr_type);
return ct_eval_error;
}
const char *interned_version = NULL;
if (!splitpathref(inner->const_expr.string.chars, inner->const_expr.string.len, path_ref, &interned_version, type))
{
SEMA_ERROR(inner, "A valid name was expected here.");
return ct_eval_error;
}
if (*path_ref) (*path_ref)->span = inner->span;
if (*type == TOKEN_INVALID_TOKEN)
{
if (report_missing)
{
SEMA_ERROR(inner, "'%s' could not be found, did you spell it right?", interned_version);
return ct_eval_error;
}
SEMA_ERROR(inner, "'%s' expects a constant string as the argument.", is_type_eval ? "$evaltype" : "$eval");
return NULL;
}
return interned_version;
const char *interned_version = NULL;
TokenType token = sema_splitpathref(inner->const_expr.string.chars, inner->const_expr.string.len, &path, &interned_version);
switch (token)
{
case TOKEN_CONST_IDENT:
inner->identifier_expr.is_const = true;
break;
case TOKEN_IDENT:
if (!interned_version)
{
if (report_missing)
{
SEMA_ERROR(inner, "'%.*s' could not be found, did you spell it right?", (int)inner->const_expr.string.len, inner->const_expr.string.chars);
}
return NULL;
}
inner->identifier_expr.is_const = false;
break;
case TYPE_TOKENS:
{
TypeInfo *info = type_info_new_base(type_from_token(token), inner->span);
inner->expr_kind = EXPR_TYPEINFO;
inner->resolve_status = RESOLVE_NOT_DONE;
inner->type_expr = info;
return inner;
}
case TOKEN_TYPE_IDENT:
{
TypeInfo *info = type_info_new(TYPE_INFO_IDENTIFIER, inner->span);
info->unresolved.name = interned_version;
info->unresolved.path = path;
info->resolve_status = RESOLVE_NOT_DONE;
inner->expr_kind = EXPR_TYPEINFO;
inner->resolve_status = RESOLVE_NOT_DONE;
inner->type_expr = info;
return inner;
}
default:
if (is_type_eval)
{
SEMA_ERROR(inner, "Only valid types may be resolved with $evaltype.");
}
else
{
SEMA_ERROR(inner, "Only plain function, variable and constant names may be resolved with $eval.");
}
return NULL;
}
inner->expr_kind = EXPR_IDENTIFIER;
inner->resolve_status = RESOLVE_NOT_DONE;
inner->identifier_expr.ident = interned_version;
inner->identifier_expr.path = path;
return inner;
}
static Expr *expr_access_inline_member(Expr *parent, Decl *parent_decl)
@@ -308,6 +352,7 @@ static bool sema_binary_is_expr_lvalue(Expr *top_expr, Expr *expr)
{
switch (expr->expr_kind)
{
case EXPR_SUBSCRIPT_ASSIGN:
case EXPR_CT_IDENT:
return true;
case EXPR_IDENTIFIER:
@@ -433,6 +478,7 @@ ERR:
bool sema_expr_check_assign(SemaContext *c, Expr *expr)
{
if (!sema_binary_is_expr_lvalue(expr, expr)) return false;
if (expr->expr_kind == EXPR_SUBSCRIPT_ASSIGN) return true;
if (expr->expr_kind == EXPR_BITACCESS || expr->expr_kind == EXPR_ACCESS) expr = expr->access_expr.parent;
if (expr->expr_kind == EXPR_IDENTIFIER)
{
@@ -2177,7 +2223,56 @@ static bool sema_subscript_rewrite_index_const_list(Expr *const_list, Expr *inde
return expr_rewrite_to_const_initializer_index(const_list->type, const_list->const_expr.initializer, result, idx);
}
static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, bool is_addr)
/**
* Find index type or overload for subscript.
*/
static Expr *sema_expr_find_index_type_or_overload_for_subscript(SemaContext *context, Expr *current_expr, SubscriptEval eval_type, Type **index_type_ptr, Decl **overload_ptr)
{
Decl *overload = NULL;
switch (eval_type)
{
case SUBSCRIPT_EVAL_REF:
overload = sema_find_operator(context, current_expr, OVERLOAD_ELEMENT_REF);
break;
case SUBSCRIPT_EVAL_VALUE:
overload = sema_find_operator(context, current_expr, OVERLOAD_ELEMENT_AT);
break;
case SUBSCRIPT_EVAL_ASSIGN:
overload = sema_find_operator(context, current_expr, OVERLOAD_ELEMENT_SET);
if (overload)
{
*overload_ptr = overload;
assert(vec_size(overload->func_decl.signature.params) == 3);
*index_type_ptr = overload->func_decl.signature.params[2]->type;
return current_expr;
}
break;
}
// Overload found for [] and &[]
if (overload)
{
*overload_ptr = overload;
assert(overload->func_decl.signature.rtype);
*index_type_ptr = type_infoptr(overload->func_decl.signature.rtype)->type;
return current_expr;
}
// Otherwise, see if we have an indexed type.
Type *inner_type = type_get_indexed_type(current_expr->type);
if (inner_type)
{
*index_type_ptr = inner_type;
*overload_ptr = NULL;
return current_expr;
}
if (type_is_substruct(current_expr->type))
{
Expr *embedded_struct = expr_access_inline_member(current_expr, current_expr->type->decl);
return sema_expr_find_index_type_or_overload_for_subscript(context, embedded_struct, eval_type, index_type_ptr, overload_ptr);
}
return NULL;
}
static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, SubscriptEval eval_type)
{
assert(expr->expr_kind == EXPR_SUBSCRIPT || expr->expr_kind == EXPR_SUBSCRIPT_ADDR);
@@ -2196,7 +2291,6 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr,
Type *underlying_type = type_flatten(subscripted->type);
Type *current_type = underlying_type;
Expr *current_expr = subscripted;
int64_t index_value = -1;
bool start_from_end = expr->subscript_expr.range.start_from_end;
@@ -2208,7 +2302,7 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr,
SEMA_ERROR(index, "The index may not be negative.");
return false;
}
int64_t size = current_type == type_untypedlist ? vec_size(current_expr->const_expr.untyped_list) : current_type->array.len;
int64_t size = current_type == type_untypedlist ? vec_size(subscripted->const_expr.untyped_list) : current_type->array.len;
assert(size >= 0 && "Unexpected overflow");
if (!int_fits(index->const_expr.ixx, TYPE_I64) || size == 0)
{
@@ -2246,13 +2340,14 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr,
// 4. If we are indexing into a complist
if (underlying_type == type_untypedlist)
{
if (is_addr)
if (eval_type == SUBSCRIPT_EVAL_REF)
{
SEMA_ERROR(subscripted, "You need to use && to take the address of a temporary.");
return false;
}
// 4a. This may either be an initializer list or a CT value
while (current_expr->expr_kind == EXPR_CT_IDENT) current_expr = current_expr->ct_ident_expr.decl->var.init_expr;
Expr *current_expr = subscripted;
while (subscripted->expr_kind == EXPR_CT_IDENT) current_expr = current_expr->ct_ident_expr.decl->var.init_expr;
// 4b. Now we need to check that we actually have a valid type.
if (index_value < 0)
@@ -2260,39 +2355,50 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr,
SEMA_ERROR(index, "To subscript an untyped list a compile time integer index is needed.");
return false;
}
expr_replace(expr, subscripted->const_expr.untyped_list[index_value]);
if (eval_type == SUBSCRIPT_EVAL_ASSIGN) TODO;
expr_replace(expr, current_expr->const_expr.untyped_list[index_value]);
return true;
}
if (!sema_cast_rvalue(context, current_expr)) return false;
if (!sema_cast_rvalue(context, subscripted)) return false;
Type *inner_type = sema_subscript_find_indexable_type_recursively(&current_type, &current_expr);
if (!inner_type)
Decl *overload = NULL;
Type *index_type = NULL;
Expr *current_expr = sema_expr_find_index_type_or_overload_for_subscript(context, subscripted, eval_type, &index_type, &overload);
if (!index_type)
{
Decl *decl = NULL;
if (is_addr) decl = sema_find_operator(context, current_expr, OVERLOAD_ELEMENT_REF);
if (!decl)
if (!overload && eval_type == SUBSCRIPT_EVAL_REF)
{
decl = sema_find_operator(context, current_expr, OVERLOAD_ELEMENT_AT);
if (decl && is_addr)
// Maybe there is a [] overload?
if (sema_expr_find_index_type_or_overload_for_subscript(context,
subscripted,
SUBSCRIPT_EVAL_VALUE,
&index_type,
&overload))
{
SEMA_ERROR(expr, "A function or macro with '@operator([])' is not defined for %s, so you need && to take the address of the temporary.",
SEMA_ERROR(expr,
"A function or macro with '@operator(&[])' is not defined for %s, so you need && to take the address of the temporary.",
type_quoted_error_string(current_expr->type));
return false;
}
}
if (decl)
{
Expr **args = NULL;
vec_add(args, index);
return sema_insert_method_call(context, expr, decl, current_expr, args);
}
}
if (!inner_type)
{
SEMA_ERROR(subscripted, "Cannot index '%s'.", type_to_error_string(subscripted->type));
return false;
}
if (overload)
{
if (eval_type == SUBSCRIPT_EVAL_ASSIGN)
{
expr->expr_kind = EXPR_SUBSCRIPT_ASSIGN;
expr->subscript_assign_expr.expr = exprid(current_expr);
expr->subscript_assign_expr.index = exprid(index);
expr->subscript_assign_expr.method = declid(overload);
return true;
}
Expr **args = NULL;
vec_add(args, index);
return sema_insert_method_call(context, expr, overload, current_expr, args);
}
// Cast to an appropriate type for index.
if (!cast_to_index(index)) return false;
@@ -2306,9 +2412,9 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr,
(void)start_from_end;
}
if (is_addr)
if (eval_type == SUBSCRIPT_EVAL_REF)
{
inner_type = type_get_ptr(inner_type);
index_type = type_get_ptr(index_type);
}
else
{
@@ -2318,7 +2424,7 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr,
}
}
expr->subscript_expr.expr = exprid(current_expr);
expr->type = type_add_optional(inner_type, failable);
expr->type = type_add_optional(index_type, failable);
return true;
}
@@ -2509,30 +2615,17 @@ RETRY:
}
case EXPR_CT_EVAL:
{
Expr *inner = child->inner_expr;
TokenType type;
// Only report missing if missing var is NULL
Path *path = NULL;
const char *ident = sema_ct_eval_expr(context, "$eval", inner, &type, &path, missing == NULL);
if (!ident && missing)
Expr *result = sema_ct_eval_expr(context, false, child->inner_expr, missing == NULL);
if (!result)
{
*missing = true;
if (missing) *missing = true;
return NULL;
}
if (ident == ct_eval_error) return NULL;
switch (type)
{
case TOKEN_IDENT:
case TOKEN_CONST_IDENT:
child->expr_kind = EXPR_IDENTIFIER;
child->identifier_expr.ident = ident;
child->identifier_expr.path = path;
child->identifier_expr.is_const = type == TOKEN_CONST_IDENT;
goto RETRY;
default:
SEMA_ERROR(inner, "Only function, variable and constant names may be resolved with $eval.");
return NULL;
}
expr_replace(child, result);
goto RETRY;
}
default:
break;
@@ -3693,9 +3786,16 @@ static bool sema_expr_analyse_assign(SemaContext *context, Expr *expr, Expr *lef
{
return sema_expr_analyse_ct_type_identifier_assign(context, expr, left, right);
}
if (left->expr_kind == EXPR_SUBSCRIPT)
{
if (!sema_expr_analyse_subscript(context, left, SUBSCRIPT_EVAL_ASSIGN)) return false;
}
else
{
if (!sema_analyse_expr_lvalue(context, left)) return false;
}
if (!sema_analyse_expr_lvalue(context, left)) return false;
bool is_subscript_assign = left->expr_kind == EXPR_SUBSCRIPT_ASSIGN;
// 2. Check assignability
if (!sema_expr_check_assign(context, left)) return false;
@@ -3710,6 +3810,13 @@ static bool sema_expr_analyse_assign(SemaContext *context, Expr *expr, Expr *lef
sema_rewrap_var(context, left->identifier_expr.decl);
return true;
}
if (left->expr_kind == EXPR_SUBSCRIPT_ASSIGN)
{
Expr **args = NULL;
vec_add(args, exprptr(left->subscript_assign_expr.index));
vec_add(args, right);
return sema_insert_method_call(context, expr, declptr(left->subscript_assign_expr.method), exprptr(left->subscript_assign_expr.expr), args);
}
if (left->expr_kind == EXPR_BITACCESS)
{
if (!sema_bit_assignment_check(right, left->access_expr.ref)) return false;
@@ -5523,6 +5630,28 @@ static inline bool sema_expr_analyse_compiler_const(SemaContext *context, Expr *
expr_rewrite_to_string(expr, context->compilation_unit->file->name);
return true;
}
if (string == kw_FUNCPTR)
{
switch (context->call_env.kind)
{
case CALL_ENV_GLOBAL_INIT:
case CALL_ENV_CHECKS:
case CALL_ENV_INITIALIZER:
case CALL_ENV_FINALIZER:
case CALL_ENV_ATTR:
expr_rewrite_to_const_zero(expr, type_voidptr);
return true;
case CALL_ENV_FUNCTION:
expr->expr_kind = EXPR_IDENTIFIER;
expr->resolve_status = RESOLVE_DONE;
expr->identifier_expr.decl = context->call_env.current_function;
expr->type = expr->identifier_expr.decl->type;
expr_insert_addr(expr);
return true;
}
UNREACHABLE
}
if (string == kw_FUNC)
{
switch (context->call_env.kind)
@@ -5932,25 +6061,15 @@ RETRY:
case TYPE_INFO_EVALTYPE:
{
Expr *expr = type_info->unresolved_type_expr;
TokenType type;
Path *path = NULL;
const char *ident = sema_ct_eval_expr(context, "$eval", expr, &type, &path, false);
if (!ident) return NULL;
if (ident == ct_eval_error) return poisoned_type;
switch (type)
expr = sema_ct_eval_expr(context, "$evaltype", expr, false);
if (!expr) return NULL;
if (expr->expr_kind != EXPR_TYPEINFO)
{
case TOKEN_TYPE_IDENT:
type_info->unresolved.name = ident;
type_info->span = expr->span;
type_info->unresolved.path = path;
type_info->kind = TYPE_INFO_IDENTIFIER;
goto RETRY;
case TYPE_TOKENS:
return type_info->type = type_from_token(type);
default:
SEMA_ERROR(expr, "Only type names may be resolved with $evaltype.");
return poisoned_type;
SEMA_ERROR(expr, "Only type names may be resolved with $evaltype.");
return poisoned_type;
}
type_info = expr->type_expr;
goto RETRY;
}
case TYPE_INFO_SUBARRAY:
{
@@ -6055,28 +6174,9 @@ RETRY:
if (!sema_expr_analyse_builtin(context, main_var, false)) goto NOT_DEFINED;
break;
case EXPR_CT_EVAL:
{
Expr *inner = main_var->inner_expr;
TokenType token_type;
Path *path = NULL;
const char *ident = sema_ct_eval_expr(context, "$eval", inner, &token_type, &path, false);
if (ident == ct_eval_error) return false;
if (!ident) goto NOT_DEFINED;
switch (token_type)
{
case TOKEN_IDENT:
case TOKEN_CONST_IDENT:
main_var->expr_kind = EXPR_IDENTIFIER;
main_var->resolve_status = RESOLVE_NOT_DONE;
main_var->identifier_expr.ident = ident;
main_var->identifier_expr.path = path;
main_var->identifier_expr.is_const = token_type == TOKEN_CONST_IDENT;
goto RETRY;
default:
SEMA_ERROR(inner, "Only function, variable and constant names may be resolved with $eval.");
return false;
}
}
main_var = sema_ct_eval_expr(context, "$eval", main_var->inner_expr, false);
if (!main_var) goto NOT_DEFINED;
goto RETRY;
default:
SEMA_ERROR(main_var, "Expected an identifier here.");
return false;
@@ -6245,24 +6345,17 @@ static inline bool sema_expr_analyse_ct_stringify(SemaContext *context, Expr *ex
static inline bool sema_expr_analyse_ct_eval(SemaContext *context, Expr *expr)
{
Expr *inner = expr->inner_expr;
TokenType type;
Path *path = NULL;
const char *ident = sema_ct_eval_expr(context, "$eval", inner, &type, &path, true);
if (ident == ct_eval_error) return false;
switch (type)
Expr *result = sema_ct_eval_expr(context, "$eval", expr->inner_expr, true);
if (!result) return false;
if (result->expr_kind == EXPR_TYPEINFO)
{
case TOKEN_IDENT:
case TOKEN_CONST_IDENT:
expr->expr_kind = EXPR_IDENTIFIER;
expr->identifier_expr.ident = ident;
expr->identifier_expr.path = path;
expr->identifier_expr.is_const = type == TOKEN_CONST_IDENT;
return sema_analyse_expr_dispatch(context, expr);
default:
SEMA_ERROR(inner, "Only function, variable and constant names may be resolved with $eval.");
return false;
SEMA_ERROR(result, "Evaluation to a type requires the use of '$evaltype' rather than '$eval'.");
return false;
}
expr_replace(expr, result);
return sema_analyse_expr_dispatch(context, expr);
}
@@ -6481,12 +6574,13 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr)
case EXPR_CALL:
return sema_expr_analyse_call(context, expr);
case EXPR_SUBSCRIPT:
return sema_expr_analyse_subscript(context, expr, false);
return sema_expr_analyse_subscript(context, expr, SUBSCRIPT_EVAL_VALUE);
case EXPR_SUBSCRIPT_ADDR:
return sema_expr_analyse_subscript(context, expr, true);
return sema_expr_analyse_subscript(context, expr, SUBSCRIPT_EVAL_REF);
case EXPR_GROUP:
return sema_expr_analyse_group(context, expr);
case EXPR_BITACCESS:
case EXPR_SUBSCRIPT_ASSIGN:
UNREACHABLE
case EXPR_ACCESS:
return sema_expr_analyse_access(context, expr);
@@ -6769,10 +6863,11 @@ bool sema_analyse_inferred_expr(SemaContext *context, Type *infer_type, Expr *ex
return true;
}
bool splitpathref(const char *string, ArraySize len, Path **path_ref, const char **ident_ref, TokenType *type_ref)
TokenType sema_splitpathref(const char *string, ArraySize len, Path **path_ref, const char **ident_ref)
{
ArraySize path_end = 0;
*path_ref = NULL;
*ident_ref = NULL;
for (ArraySize i = 0; i < len; i++)
{
char ch = string[i];
@@ -6785,7 +6880,7 @@ bool splitpathref(const char *string, ArraySize len, Path **path_ref, const char
}
else
{
return false;
return TOKEN_INVALID_TOKEN;
}
}
}
@@ -6802,23 +6897,30 @@ bool splitpathref(const char *string, ArraySize len, Path **path_ref, const char
len--;
string++;
}
if (len == 0) return false;
if (len == 0) return TOKEN_INVALID_TOKEN;
uint32_t hash = FNV1_SEED;
for (size_t i = 0; i < len; i++)
{
char c = string[i];
if (!char_is_alphanum_(c)) return false;
if (!char_is_alphanum_(c)) return TOKEN_INVALID_TOKEN;
hash = FNV1a(c, hash);
}
*ident_ref = symtab_find(string, len, hash, type_ref);
if (!*ident_ref)
TokenType type;
*ident_ref = symtab_find(string, len, hash, &type);
if (!*ident_ref) return TOKEN_IDENT;
switch (type)
{
scratch_buffer_clear();
scratch_buffer_append_len(string, len);
*ident_ref = scratch_buffer_to_string();
*type_ref = TOKEN_INVALID_TOKEN;
case TOKEN_TYPE_IDENT:
case TOKEN_IDENT:
case TOKEN_CONST_IDENT:
return type;
case TYPE_TOKENS:
if (!*path_ref) return type;
FALLTHROUGH;
default:
*ident_ref = NULL;
return TOKEN_INVALID_TOKEN;
}
return true;
}
bool sema_insert_method_call(SemaContext *context, Expr *method_call, Decl *method_decl, Expr *parent, Expr **arguments)

View File

@@ -27,6 +27,17 @@
#define POP_BREAKCONT() POP_CONTINUE(); POP_BREAK()
#define IS_CONST(_x) ((_x)->expr_kind == EXPR_CONST)
typedef enum
{
SPLIT_PATH_IDENT,
SPLIT_PATH_CONST_IDENT,
SPLIT_PATH_TYPE_IDENT,
SPLIT_PATH_BUILTIN_TYPE_IDENT,
SPLIT_PATH_UNKNOWN_IDENTIFIER,
SPLIT_PATH_NOT_SINGLE_IDENTIFIER,
SPLIT_PATH_NOT_AN_IDENTIFIER,
} SplitPathResult;
extern const char *ct_eval_error;
Decl **global_context_acquire_locals_list(void);
@@ -38,7 +49,8 @@ void context_pop_defers_and_replace_ast(SemaContext *context, Ast *ast);
void context_change_scope_for_label(SemaContext *context, Decl *label);
void context_change_scope_with_flags(SemaContext *context, ScopeFlags flags);
SemaContext *context_transform_for_eval(SemaContext *context, SemaContext *temp_context, CompilationUnit *eval_unit);
bool splitpathref(const char *string, ArraySize len, Path **path_ref, const char **ident_ref, TokenType *type_ref);
TokenType sema_splitpathref(const char *string, ArraySize len, Path **path_ref, const char **ident_ref);
void sema_context_init(SemaContext *context, CompilationUnit *unit);
void sema_context_destroy(SemaContext *context);
@@ -65,7 +77,7 @@ bool sema_insert_method_call(SemaContext *context, Expr *method_call, Decl *meth
bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr);
bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool failable);
Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr);
const char *sema_ct_eval_expr(SemaContext *c, const char *expr_type, Expr *inner, TokenType *type, Path **path_ref, bool report_missing);
Expr *sema_ct_eval_expr(SemaContext *c, bool is_type, Expr *inner, bool report_missing);
bool sema_analyse_asm(SemaContext *context, AsmInlineBlock *block, Ast *asm_stmt);
bool sema_bit_assignment_check(Expr *right, Decl *member);
int sema_check_comp_time_bool(SemaContext *context, Expr *expr);

View File

@@ -273,7 +273,6 @@ bool sema_resolve_type_shallow(SemaContext *context, TypeInfo *type_info, bool a
allow_inferred_type = false;
in_shallow = true;
}
RETRY:
switch (type_info->kind)
{
case TYPE_INFO_POISON:
@@ -301,29 +300,18 @@ RETRY:
{
Expr *expr = type_info->unresolved_type_expr;
TokenType type;
Path *path = NULL;
const char *ident = sema_ct_eval_expr(context, "$eval", expr, &type, &path, true);
if (ident == ct_eval_error) return type_info_poison(type_info);
switch (type)
Expr *inner = sema_ct_eval_expr(context, "$evaltype", expr, true);
if (!inner) return false;
if (inner->expr_kind != EXPR_TYPEINFO)
{
case TOKEN_TYPE_IDENT:
type_info->unresolved.name = ident;
type_info->span = expr->span;
type_info->unresolved.path = path;
type_info->kind = TYPE_INFO_IDENTIFIER;
goto RETRY;
case TYPE_TOKENS:
if (path)
{
SEMA_ERROR(path, "Built in types cannot have a path prefix.");
return false;
}
type_info->type = type_from_token(type);
goto APPEND_QUALIFIERS;
default:
SEMA_ERROR(expr, "Only type names may be resolved with $evaltype.");
return type_info_poison(type_info);
SEMA_ERROR(expr, "Only type names may be resolved with $evaltype.");
return type_info_poison(type_info);
}
TypeInfo *inner_type = inner->type_expr;
if (!sema_resolve_type_info(context, inner_type)) return false;
type_info->type = inner_type->type;
type_info->resolve_status = RESOLVE_DONE;
goto APPEND_QUALIFIERS;
}
case TYPE_INFO_TYPEOF:
{

View File

@@ -285,7 +285,7 @@ void sema_analysis_run(void)
Path *path;
const char *ident;
TokenType type;
if (!splitpathref(panicfn, strlen(panicfn), &path, &ident, &type) || path == NULL)
if (sema_splitpathref(panicfn, strlen(panicfn), &path, &ident) != TOKEN_IDENT || path == NULL || !ident)
{
error_exit("'%s' is not a valid panic function.", panicfn);
}

View File

@@ -41,6 +41,7 @@ const char *builtin_list[NUMBER_OF_BUILTINS];
const char *kw_FILE;
const char *kw_FUNC;
const char *kw_FUNCPTR;
const char *kw_LINEREAL;
const char *kw_LINE;
const char *kw_align;
@@ -131,6 +132,7 @@ void symtab_init(uint32_t capacity)
kw_LINEREAL = KW_DEF("LINEREAL");
kw_FILE = KW_DEF("FILE");
kw_FUNC = KW_DEF("FUNC");
kw_FUNCPTR = KW_DEF("FUNCPTR");
type = TOKEN_TYPE_IDENT;
kw_typekind = KW_DEF("TypeKind");

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.3.68"
#define COMPILER_VERSION "0.3.69"

View File

@@ -0,0 +1,69 @@
// #target: macos-x64
module test;
import std::io;
fn void test(int x)
{
$typeof($$FUNCPTR) ptr = $$FUNCPTR;
io::printfln("%d", x);
if (x > 0) ptr(x - 1);
}
fn void main()
{
test(10);
}
/* #expect: test.ll
define void @test_test(i32 %0) #0 {
entry:
%ptr = alloca void (i32)*, align 8
%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 void (i32)* @test_test, void (i32)** %ptr, align 8
store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i64 2 }, %"char[]"* %taddr, align 8
%1 = bitcast %"char[]"* %taddr to { i8*, i64 }*
%2 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %1, i32 0, i32 0
%lo = load i8*, i8** %2, align 8
%3 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %1, i32 0, i32 1
%hi = load i64, i64* %3, align 8
store i32 %0, 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
%gt = icmp sgt i32 %0, 0
br i1 %gt, label %if.then, label %if.exit
if.then: ; preds = %voiderr
%15 = load void (i32)*, void (i32)** %ptr, align 8
%sub = sub i32 %0, 1
call void %15(i32 %sub)
br label %if.exit
if.exit: ; preds = %if.then, %voiderr
ret void
}

View File

@@ -16,5 +16,5 @@ fn void main3()
fn void main4()
{
$extnameof($evaltype("foo::int")); // #error: Built in types cannot have a path prefix.
$extnameof($evaltype("foo::int")); // #error: Only valid types may be resolved with $evaltype.
}

View File

@@ -24,7 +24,7 @@ fn void f()
fn void g()
{
int x = $evaltype("bar::").sizeof; // #error: A valid name was expected here.
int x = $evaltype("bar::").sizeof; // #error: Only valid types may be resolved with $evaltype
}
fn void k()

View File

@@ -0,0 +1,39 @@
// #target: macos-x64
module test;
import std::map;
define IntMap = HashMap<char[], int>;
fn void main()
{
IntMap map;
map.set("Hello", 4);
map["Bye"] = 5;
}
/* #expect: test.ll
define void @test_main() #0 {
entry:
%map = alloca %HashMap, align 8
%taddr = alloca %"char[]", align 8
%taddr1 = alloca %"char[]", align 8
%0 = bitcast %HashMap* %map to i8*
call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 40, i1 false)
store %"char[]" { i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str, i32 0, i32 0), i64 5 }, %"char[]"* %taddr, align 8
%1 = bitcast %"char[]"* %taddr to { i8*, i64 }*
%2 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %1, i32 0, i32 0
%lo = load i8*, i8** %2, align 8
%3 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %1, i32 0, i32 1
%hi = load i64, i64* %3, align 8
%4 = call i8 @"std_map$$sa$char.int_HashMap_set"(%HashMap* %map, i8* %lo, i64 %hi, i32 4)
store %"char[]" { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i32 0, i32 0), i64 3 }, %"char[]"* %taddr1, align 8
%5 = bitcast %"char[]"* %taddr1 to { i8*, i64 }*
%6 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %5, i32 0, i32 0
%lo2 = load i8*, i8** %6, align 8
%7 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %5, i32 0, i32 1
%hi3 = load i64, i64* %7, align 8
%8 = call i8 @"std_map$$sa$char.int_HashMap_set"(%HashMap* %map, i8* %lo2, i64 %hi3, i32 5)
ret void
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,65 @@
// #target: macos-x64
module test;
import std::io;
fn void test(int x)
{
$typeof($$FUNCPTR) ptr = $$FUNCPTR;
io::printfln("%d", x);
if (x > 0) ptr(x - 1);
}
fn void main()
{
test(10);
}
/* #expect: test.ll
define void @test_test(i32 %0) #0 {
entry:
%ptr = alloca ptr, align 8
%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 ptr @test_test, ptr %ptr, align 8
store %"char[]" { ptr @.str, i64 2 }, ptr %taddr, align 8
%1 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 0
%lo = load ptr, ptr %1, align 8
%2 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 1
%hi = load i64, ptr %2, align 8
store i32 %0, 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
%gt = icmp sgt i32 %0, 0
br i1 %gt, label %if.then, label %if.exit
if.then: ; preds = %voiderr
%11 = load ptr, ptr %ptr, align 8
%sub = sub i32 %0, 1
call void %11(i32 %sub)
br label %if.exit
if.exit: ; preds = %if.then, %voiderr
ret void
}

View File

@@ -16,5 +16,5 @@ fn void main3()
fn void main4()
{
$extnameof($evaltype("foo::int")); // #error: Built in types cannot have a path prefix.
$extnameof($evaltype("foo::int")); // #error: Only valid types may be resolved with $evaltype.
}

View File

@@ -1,6 +1,5 @@
module foo;
import bar;
import bar::abc;
fn void a()
{
@@ -25,7 +24,7 @@ fn void f()
fn void g()
{
int x = $evaltype("bar::").sizeof; // #error: A valid name was expected here.
int x = $evaltype("bar::").sizeof; // #error: Only valid types may be resolved with $evaltype
}
fn void k()

View File

@@ -0,0 +1,36 @@
// #target: macos-x64
module test;
import std::map;
define IntMap = HashMap<char[], int>;
fn void main()
{
IntMap map;
map.set("Hello", 4);
map["Bye"] = 5;
}
/* #expect: test.ll
define void @test_main() #0 {
entry:
%map = alloca %HashMap, align 8
%taddr = alloca %"char[]", align 8
%taddr1 = alloca %"char[]", align 8
call void @llvm.memset.p0.i64(ptr align 8 %map, i8 0, i64 40, i1 false)
store %"char[]" { ptr @.str, i64 5 }, 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 i8 @"std_map$$sa$char.int_HashMap_set"(ptr %map, ptr %lo, i64 %hi, i32 4)
store %"char[]" { ptr @.str.1, i64 3 }, ptr %taddr1, align 8
%3 = getelementptr inbounds { ptr, i64 }, ptr %taddr1, i32 0, i32 0
%lo2 = load ptr, ptr %3, align 8
%4 = getelementptr inbounds { ptr, i64 }, ptr %taddr1, i32 0, i32 1
%hi3 = load i64, ptr %4, align 8
%5 = call i8 @"std_map$$sa$char.int_HashMap_set"(ptr %map, ptr %lo2, i64 %hi3, i32 5)
ret void
}

File diff suppressed because it is too large Load Diff