mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Trailing body arguments may now be &ref, #hash, $const and $Type arguments.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
- Addition of $append and $concat functions.
|
||||
- Added $$str_hash, $$str_upper, $$str_lower, $$str_find builtins.
|
||||
- Improved error notes when call expressions have errors.
|
||||
|
||||
- Trailing body arguments may now be `&ref`, `#hash`, `$const` and `$Type` arguments.
|
||||
### Fixes
|
||||
- Error with unsigned compare in `@ensure` when early returning 0 #1207.
|
||||
- Prevent Mach-O from removing `@init` and `@dynamic` in a more reliable way #1200.
|
||||
|
||||
@@ -6373,6 +6373,7 @@ static void llvm_emit_macro_body_expansion(GenContext *c, BEValue *value, Expr *
|
||||
VECEACH(declarations, i)
|
||||
{
|
||||
Decl *decl = declarations[i];
|
||||
if (!values[i]) continue;
|
||||
if (!decl->backend_ref) llvm_emit_local_var_alloca(c, decl);
|
||||
}
|
||||
|
||||
@@ -6382,6 +6383,7 @@ static void llvm_emit_macro_body_expansion(GenContext *c, BEValue *value, Expr *
|
||||
VECEACH(values, i)
|
||||
{
|
||||
Expr *expr = values[i];
|
||||
if (!expr) continue;
|
||||
llvm_emit_expr(c, value, expr);
|
||||
llvm_store_to_ptr_aligned(c, declarations[i]->backend_ref, value, declarations[i]->alignment);
|
||||
}
|
||||
|
||||
@@ -1166,9 +1166,20 @@ static bool parse_next_is_typed_parameter(ParseContext *c, ParameterParseKind pa
|
||||
case TOKEN_CT_TYPEOF:
|
||||
case TOKEN_CT_TYPEFROM:
|
||||
return true;
|
||||
case TOKEN_CT_TYPE_IDENT:
|
||||
case TOKEN_CT_VATYPE:
|
||||
return parse_kind == PARAM_PARSE_LAMBDA || parse_kind == PARAM_PARSE_CALL;
|
||||
case TOKEN_CT_TYPE_IDENT:
|
||||
if (parse_kind == PARAM_PARSE_LAMBDA) return true;
|
||||
if (parse_kind != PARAM_PARSE_CALL) return false;
|
||||
switch (peek(c))
|
||||
{
|
||||
case TOKEN_IDENT:
|
||||
case TOKEN_HASH_IDENT:
|
||||
case TOKEN_CT_IDENT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ static inline bool parse_next_may_be_type_or_ident(ParseContext *c)
|
||||
case TOKEN_CONST_IDENT:
|
||||
case TOKEN_IDENT:
|
||||
case TOKEN_HASH_CONST_IDENT:
|
||||
case TOKEN_HASH:
|
||||
case TOKEN_HASH_IDENT:
|
||||
case TOKEN_CT_IDENT:
|
||||
case TOKEN_CT_CONST_IDENT:
|
||||
case TYPELIKE_TOKENS:
|
||||
|
||||
@@ -3237,16 +3237,19 @@ INLINE bool sema_analyse_macro_body(SemaContext *context, Decl **body_parameters
|
||||
param->resolve_status = RESOLVE_RUNNING;
|
||||
assert(param->decl_kind == DECL_VAR);
|
||||
TypeInfo *type_info = type_infoptrzero(param->var.type_info);
|
||||
switch (param->var.kind)
|
||||
VarDeclKind kind = param->var.kind;
|
||||
switch (kind)
|
||||
{
|
||||
case VARDECL_PARAM:
|
||||
if (type_info && !sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT)) return false;
|
||||
break;
|
||||
case VARDECL_PARAM_EXPR:
|
||||
case VARDECL_PARAM_CT:
|
||||
case VARDECL_PARAM_REF:
|
||||
case VARDECL_PARAM_CT_TYPE:
|
||||
RETURN_SEMA_ERROR(param, "Only plain variables are allowed as body parameters.");
|
||||
if (!type_info) break;
|
||||
if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT)) return false;
|
||||
if (kind != VARDECL_PARAM_REF || type_is_pointer(type_info->type)) break;
|
||||
RETURN_SEMA_ERROR(type_info, "A pointer type was expected for a ref argument, did you mean %s?",
|
||||
type_quoted_error_string(type_get_ptr(type_info->type)));
|
||||
case VARDECL_CONST:
|
||||
case VARDECL_GLOBAL:
|
||||
case VARDECL_LOCAL:
|
||||
|
||||
@@ -1527,6 +1527,125 @@ INLINE bool sema_arg_is_pass_through_ref(Expr *expr)
|
||||
return decl->var.kind == VARDECL_PARAM_REF;
|
||||
}
|
||||
|
||||
static bool sema_analyse_parameter(SemaContext *context, Expr *arg, Decl *param, Decl *definition, bool *optional_ref, bool *no_match_ref, bool macro)
|
||||
{
|
||||
VarDeclKind kind = param->var.kind;
|
||||
Type *type = param->type;
|
||||
|
||||
// 16. Analyse a regular argument.
|
||||
switch (kind)
|
||||
{
|
||||
case VARDECL_PARAM_REF:
|
||||
// &foo
|
||||
if (!sema_analyse_expr_lvalue(context, arg)) return false;
|
||||
if (sema_arg_is_pass_through_ref(arg) && !sema_expr_check_assign(context, arg))
|
||||
{
|
||||
SEMA_NOTE(definition, "The definition is here.");
|
||||
return false;
|
||||
}
|
||||
expr_insert_addr(arg);
|
||||
*optional_ref |= IS_OPTIONAL(arg);
|
||||
if (!sema_call_check_contract_param_match(context, param, arg))
|
||||
{
|
||||
SEMA_NOTE(definition, "The definition is here.");
|
||||
return false;
|
||||
}
|
||||
if (type_storage_type(type) != STORAGE_NORMAL)
|
||||
{
|
||||
RETURN_SEMA_ERROR(arg, "A value of type %s cannot be passed by reference.", type_quoted_error_string(type));
|
||||
}
|
||||
if (type && type->canonical != arg->type->canonical)
|
||||
{
|
||||
SEMA_ERROR(arg, "'%s' cannot be implicitly cast to '%s'.", type_to_error_string(arg->type), type_to_error_string(type));
|
||||
SEMA_NOTE(definition, "The definition is here.");
|
||||
return false;
|
||||
}
|
||||
if (!param->alignment)
|
||||
{
|
||||
if (arg->expr_kind == EXPR_IDENTIFIER)
|
||||
{
|
||||
param->alignment = arg->identifier_expr.decl->alignment;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sema_set_alloca_alignment(context, arg->type, ¶m->alignment)) return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VARDECL_PARAM:
|
||||
// foo
|
||||
if (!sema_analyse_expr_rhs(context, type, arg, true, no_match_ref)) return false;
|
||||
if (IS_OPTIONAL(arg)) *optional_ref = true;
|
||||
switch (type_storage_type(arg->type))
|
||||
{
|
||||
case STORAGE_NORMAL:
|
||||
break;
|
||||
case STORAGE_VOID:
|
||||
case STORAGE_WILDCARD:
|
||||
RETURN_SEMA_ERROR(arg, "A 'void' value cannot be passed as a parameter.");
|
||||
case STORAGE_COMPILE_TIME:
|
||||
RETURN_SEMA_ERROR(arg, "It is only possible to use %s as a compile time parameter.",
|
||||
type_invalid_storage_type_name(arg->type));
|
||||
case STORAGE_UNKNOWN:
|
||||
RETURN_SEMA_ERROR(arg, "A value of type '%s' has no known size so cannot be "
|
||||
"passed as a parameter, you can pass a pointer to it though.",
|
||||
type_quoted_error_string(arg->type));
|
||||
}
|
||||
if (!sema_call_check_contract_param_match(context, param, arg))
|
||||
{
|
||||
SEMA_NOTE(definition, "The definition was here.");
|
||||
return false;
|
||||
}
|
||||
if (!param->alignment)
|
||||
{
|
||||
assert(macro && "Only in the macro case should we need to insert the alignment.");
|
||||
if (!sema_set_alloca_alignment(context, arg->type, ¶m->alignment)) return false;
|
||||
}
|
||||
break;
|
||||
case VARDECL_PARAM_EXPR:
|
||||
// #foo
|
||||
param->var.hash_var.context = context;
|
||||
param->var.hash_var.span = arg->span;
|
||||
break;
|
||||
case VARDECL_PARAM_CT:
|
||||
// $foo
|
||||
assert(macro);
|
||||
if (!sema_analyse_expr_rhs(context, type, arg, true, no_match_ref))
|
||||
{
|
||||
SEMA_NOTE(definition, "The definition is here.");
|
||||
return false;
|
||||
}
|
||||
if (!expr_is_constant_eval(arg, CONSTANT_EVAL_CONSTANT_VALUE))
|
||||
{
|
||||
RETURN_SEMA_FUNC_ERROR(definition, arg, "A compile time parameter must always be a constant, did you mistake it for a normal paramter?");
|
||||
}
|
||||
break;
|
||||
case VARDECL_PARAM_CT_TYPE:
|
||||
// $Foo
|
||||
if (!sema_analyse_expr_lvalue_fold_const(context, arg)) return false;
|
||||
if (arg->expr_kind != EXPR_TYPEINFO)
|
||||
{
|
||||
RETURN_SEMA_FUNC_ERROR(definition, arg, "A type, like 'int' or 'double' was expected for the parameter '%s'.", param->name);
|
||||
}
|
||||
break;
|
||||
case VARDECL_CONST:
|
||||
case VARDECL_GLOBAL:
|
||||
case VARDECL_LOCAL:
|
||||
case VARDECL_MEMBER:
|
||||
case VARDECL_BITMEMBER:
|
||||
case VARDECL_LOCAL_CT:
|
||||
case VARDECL_LOCAL_CT_TYPE:
|
||||
case VARDECL_UNWRAPPED:
|
||||
case VARDECL_REWRAPPED:
|
||||
case VARDECL_ERASE:
|
||||
UNREACHABLE
|
||||
}
|
||||
if (param && type_len_is_inferred(type))
|
||||
{
|
||||
param->type = type_no_optional(arg->type);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call, CalledDecl callee,
|
||||
bool *optional, bool *no_match_ref)
|
||||
{
|
||||
@@ -1670,123 +1789,8 @@ static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call
|
||||
assert(arg == NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
Decl *param = decl_params[i];
|
||||
VarDeclKind kind = param->var.kind;
|
||||
Type *type = param->type;
|
||||
|
||||
// 16. Analyse a regular argument.
|
||||
switch (kind)
|
||||
{
|
||||
case VARDECL_PARAM_REF:
|
||||
// &foo
|
||||
if (!sema_analyse_expr_lvalue(context, arg)) return false;
|
||||
if (sema_arg_is_pass_through_ref(arg) && !sema_expr_check_assign(context, arg))
|
||||
{
|
||||
SEMA_NOTE(callee.definition, "The definition is here.");
|
||||
return false;
|
||||
}
|
||||
expr_insert_addr(arg);
|
||||
*optional |= IS_OPTIONAL(arg);
|
||||
if (!sema_call_check_contract_param_match(context, param, arg))
|
||||
{
|
||||
SEMA_NOTE(callee.definition, "The definition is here.");
|
||||
return false;
|
||||
}
|
||||
if (type_storage_type(type) != STORAGE_NORMAL)
|
||||
{
|
||||
RETURN_SEMA_ERROR(arg, "A value of type %s cannot be passed by reference.", type_quoted_error_string(type));
|
||||
}
|
||||
if (type && type->canonical != arg->type->canonical)
|
||||
{
|
||||
SEMA_ERROR(arg, "'%s' cannot be implicitly cast to '%s'.", type_to_error_string(arg->type), type_to_error_string(type));
|
||||
SEMA_NOTE(callee.definition, "The definition is here.");
|
||||
return false;
|
||||
}
|
||||
if (!param->alignment)
|
||||
{
|
||||
if (arg->expr_kind == EXPR_IDENTIFIER)
|
||||
{
|
||||
param->alignment = arg->identifier_expr.decl->alignment;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sema_set_alloca_alignment(context, arg->type, ¶m->alignment)) return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VARDECL_PARAM:
|
||||
// foo
|
||||
if (!sema_analyse_expr_rhs(context, type, arg, true, no_match_ref)) return false;
|
||||
if (IS_OPTIONAL(arg)) *optional = true;
|
||||
switch (type_storage_type(arg->type))
|
||||
{
|
||||
case STORAGE_NORMAL:
|
||||
break;
|
||||
case STORAGE_VOID:
|
||||
case STORAGE_WILDCARD:
|
||||
RETURN_SEMA_ERROR(arg, "A 'void' value cannot be passed as a parameter.");
|
||||
case STORAGE_COMPILE_TIME:
|
||||
RETURN_SEMA_ERROR(arg, "It is only possible to use %s as a compile time parameter.",
|
||||
type_invalid_storage_type_name(arg->type));
|
||||
case STORAGE_UNKNOWN:
|
||||
RETURN_SEMA_ERROR(arg, "A value of type '%s' has no known size so cannot be "
|
||||
"passed as a parameter, you can pass a pointer to it though.",
|
||||
type_quoted_error_string(arg->type));
|
||||
}
|
||||
if (!sema_call_check_contract_param_match(context, param, arg))
|
||||
{
|
||||
SEMA_NOTE(callee.definition, "The definition was here.");
|
||||
return false;
|
||||
}
|
||||
if (!param->alignment)
|
||||
{
|
||||
assert(callee.macro && "Only in the macro case should we need to insert the alignment.");
|
||||
if (!sema_set_alloca_alignment(context, arg->type, ¶m->alignment)) return false;
|
||||
}
|
||||
break;
|
||||
case VARDECL_PARAM_EXPR:
|
||||
// #foo
|
||||
param->var.hash_var.context = context;
|
||||
param->var.hash_var.span = arg->span;
|
||||
break;
|
||||
case VARDECL_PARAM_CT:
|
||||
// $foo
|
||||
assert(callee.macro);
|
||||
if (!sema_analyse_expr_rhs(context, type, arg, true, no_match_ref))
|
||||
{
|
||||
SEMA_NOTE(callee.definition, "The definition is here.");
|
||||
return false;
|
||||
}
|
||||
if (!expr_is_constant_eval(arg, CONSTANT_EVAL_CONSTANT_VALUE))
|
||||
{
|
||||
RETURN_SEMA_FUNC_ERROR(callee.definition, arg, "A compile time parameter must always be a constant, did you mistake it for a normal paramter?");
|
||||
}
|
||||
break;
|
||||
case VARDECL_PARAM_CT_TYPE:
|
||||
// $Foo
|
||||
if (!sema_analyse_expr_lvalue_fold_const(context, arg)) return false;
|
||||
if (arg->expr_kind != EXPR_TYPEINFO)
|
||||
{
|
||||
RETURN_SEMA_FUNC_ERROR(callee.definition, arg, "A type, like 'int' or 'double' was expected for the parameter '%s'.", param->name);
|
||||
}
|
||||
break;
|
||||
case VARDECL_CONST:
|
||||
case VARDECL_GLOBAL:
|
||||
case VARDECL_LOCAL:
|
||||
case VARDECL_MEMBER:
|
||||
case VARDECL_BITMEMBER:
|
||||
case VARDECL_LOCAL_CT:
|
||||
case VARDECL_LOCAL_CT_TYPE:
|
||||
case VARDECL_UNWRAPPED:
|
||||
case VARDECL_REWRAPPED:
|
||||
case VARDECL_ERASE:
|
||||
UNREACHABLE
|
||||
}
|
||||
if (param && type_len_is_inferred(type))
|
||||
{
|
||||
param->type = type_no_optional(arg->type);
|
||||
}
|
||||
if (!sema_analyse_parameter(context, arg, decl_params[i], callee.definition,
|
||||
optional, no_match_ref, callee.macro)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -2050,25 +2054,63 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
|
||||
Decl *body_param = macro_body_params[i];
|
||||
assert(body_param->resolve_status == RESOLVE_DONE);
|
||||
Decl *body_arg = body_params[i];
|
||||
if (!body_arg->var.type_info)
|
||||
VarDeclKind kind_of_expected = body_param->var.kind;
|
||||
if (kind_of_expected != body_arg->var.kind)
|
||||
{
|
||||
RETURN_SEMA_ERROR(body_arg, "Expected a type parameter before this variable name.");
|
||||
}
|
||||
TypeInfo *type_info = vartype(body_arg);
|
||||
if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT)) return false;
|
||||
body_arg->type = type_info->type;
|
||||
if (type_info)
|
||||
{
|
||||
Type *declare_type = type_info->type->canonical;
|
||||
if (declare_type != body_arg->type->canonical)
|
||||
switch (kind_of_expected)
|
||||
{
|
||||
if (no_match_ref) goto NO_MATCH_REF;
|
||||
RETURN_SEMA_ERROR(type_info, "This parameter should be %s but was %s",
|
||||
type_quoted_error_string(declare_type),
|
||||
type_quoted_error_string(body_arg->type));
|
||||
case VARDECL_PARAM_CT_TYPE:
|
||||
RETURN_SEMA_FUNC_ERROR(decl, body_arg, "Expected a type argument.");
|
||||
case VARDECL_PARAM_EXPR:
|
||||
RETURN_SEMA_FUNC_ERROR(decl, body_arg, "Expected an expression argument.");
|
||||
case VARDECL_PARAM_REF:
|
||||
RETURN_SEMA_FUNC_ERROR(decl, body_arg, "Expected a reference ('&') argument.");
|
||||
case VARDECL_PARAM_CT:
|
||||
RETURN_SEMA_FUNC_ERROR(decl, body_arg, "Expected a compile time ('$') argument.");
|
||||
case VARDECL_PARAM:
|
||||
RETURN_SEMA_FUNC_ERROR(decl, body_arg, "Expected a regular argument.");
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
if (!body_arg->alignment)
|
||||
// No type checking
|
||||
switch (kind_of_expected)
|
||||
{
|
||||
case VARDECL_PARAM_CT_TYPE:
|
||||
continue;
|
||||
case VARDECL_PARAM_CT:
|
||||
case VARDECL_PARAM_EXPR:
|
||||
case VARDECL_PARAM_REF:
|
||||
// Optional typing
|
||||
break;
|
||||
case VARDECL_PARAM:
|
||||
// Mandatory typing
|
||||
if (!body_arg->var.type_info)
|
||||
{
|
||||
RETURN_SEMA_ERROR(body_arg, "Expected a type parameter before this variable name.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
TypeInfo *expected_type_info = vartype(body_param);
|
||||
TypeInfo *type_info = vartype(body_arg);
|
||||
if (type_info && !sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT)) return false;
|
||||
Type *type = type_info ? type_info->type : NULL;
|
||||
if (type && expected_type_info && type->canonical != expected_type_info->type->canonical)
|
||||
{
|
||||
if (no_match_ref) goto NO_MATCH_REF;
|
||||
RETURN_SEMA_ERROR(type_info, "This parameter should be %s but was %s",
|
||||
type_quoted_error_string(expected_type_info->type),
|
||||
type_quoted_error_string(type));
|
||||
}
|
||||
if (type && kind_of_expected == VARDECL_PARAM_REF && !type_is_pointer(type_info->type))
|
||||
{
|
||||
RETURN_SEMA_ERROR(type_info, "A pointer type was expected for a ref argument, did you mean %s?",
|
||||
type_quoted_error_string(type_get_ptr(type_info->type)));
|
||||
}
|
||||
body_arg->type = type;
|
||||
if (type_info && type_storage_type(type_info->type))
|
||||
{
|
||||
if (!sema_set_alloca_alignment(context, body_arg->type, &body_arg->alignment)) return false;
|
||||
}
|
||||
@@ -2290,10 +2332,8 @@ NO_MATCH_REF:
|
||||
static bool sema_call_analyse_body_expansion(SemaContext *macro_context, Expr *call)
|
||||
{
|
||||
Decl *macro = macro_context->current_macro;
|
||||
assert(macro);
|
||||
DeclId body_param = macro->func_decl.body_param;
|
||||
assert(body_param);
|
||||
|
||||
assert(macro && macro->func_decl.body_param);
|
||||
Decl *body_decl = declptr(macro->func_decl.body_param);
|
||||
ExprCall *call_expr = &call->call_expr;
|
||||
Expr *macro_body = exprptrzero(call_expr->macro_body);
|
||||
if (macro_body && vec_size(macro_body->macro_body_expr.body_arguments))
|
||||
@@ -2307,19 +2347,42 @@ static bool sema_call_analyse_body_expansion(SemaContext *macro_context, Expr *c
|
||||
}
|
||||
// Theoretically we could support named arguments, but that's unnecessary.
|
||||
unsigned expressions = vec_size(call_expr->arguments);
|
||||
Decl **body_parameters = declptr(body_param)->body_params;
|
||||
Decl **body_parameters = body_decl->body_params;
|
||||
if (expressions != vec_size(body_parameters))
|
||||
{
|
||||
PRINT_ERROR_AT(call, "Expected %d parameter(s).", vec_size(body_parameters));
|
||||
PRINT_ERROR_AT(call, "Expected %d parameter(s) to %s.", vec_size(body_parameters), body_decl->name);
|
||||
}
|
||||
Expr **args = call_expr->arguments;
|
||||
|
||||
Decl **params = macro_context->yield_params;
|
||||
// Evaluate the expressions. TODO hash expressions
|
||||
|
||||
bool has_optional_arg = false;
|
||||
|
||||
// Evaluate the expressions.
|
||||
for (unsigned i = 0; i < expressions; i++)
|
||||
{
|
||||
Decl *param = params[i];
|
||||
Expr *expr = args[i];
|
||||
if (!sema_analyse_expr_rhs(macro_context, params[i]->type, expr, false, NULL)) return false;
|
||||
if (!sema_analyse_parameter(macro_context, expr, param, body_decl, &has_optional_arg, NULL, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (has_optional_arg)
|
||||
{
|
||||
sema_error_at(macro_context, expr->span, "Optional arguments are not permitted in a body invocation.");
|
||||
return false;
|
||||
}
|
||||
switch (param->var.kind)
|
||||
{
|
||||
case VARDECL_PARAM_EXPR:
|
||||
case VARDECL_PARAM_CT:
|
||||
case VARDECL_PARAM_CT_TYPE:
|
||||
param->var.init_expr = args[i];
|
||||
args[i] = NULL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AstId macro_defer = macro_context->active_scope.defer_last;
|
||||
@@ -2405,10 +2468,6 @@ bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr, bool *no_match_ref)
|
||||
{
|
||||
if (no_match_ref) *no_match_ref = true;
|
||||
@@ -4354,11 +4413,10 @@ CHECK_DEEPER:
|
||||
return true;
|
||||
}
|
||||
|
||||
// 10. Dump all members and methods into the scope.
|
||||
// 10. Dump all members and methods into a decl stack.
|
||||
Decl *decl = type->decl;
|
||||
|
||||
Decl *member = sema_decl_stack_find_decl_member(decl, kw);
|
||||
|
||||
if (member && decl_is_enum_kind(decl) && member->decl_kind == DECL_VAR && expr_is_const(parent))
|
||||
{
|
||||
if (!sema_analyse_decl(context, decl)) return false;
|
||||
@@ -9023,8 +9081,7 @@ static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr)
|
||||
}
|
||||
break;
|
||||
case EXPR_TYPEINFO:
|
||||
SEMA_ERROR(expr, "A type must be followed by either (...) or '.'.");
|
||||
return false;
|
||||
RETURN_SEMA_ERROR(expr, "A type must be followed by either (...) or '.'.");
|
||||
case EXPR_CT_IDENT:
|
||||
if (!sema_cast_ct_ident_rvalue(context, expr)) return false;
|
||||
break;
|
||||
|
||||
195
test/test_suite/macros/macro_body_ref_hash_constant_type.c3t
Normal file
195
test/test_suite/macros/macro_body_ref_hash_constant_type.c3t
Normal file
@@ -0,0 +1,195 @@
|
||||
// #target: macos-x64
|
||||
module test;
|
||||
import std::io;
|
||||
|
||||
fn void main()
|
||||
{
|
||||
@boba(;#hash_val, &foo, int $value, $Type)
|
||||
{
|
||||
io::printn("Now invoking hash");
|
||||
#hash_val;
|
||||
#hash_val;
|
||||
*foo += $value;
|
||||
io::printfn("The type was: %s", $Type.nameof);
|
||||
};
|
||||
}
|
||||
|
||||
macro void @boba(;@body(#hash, &foo, $val, $Type))
|
||||
{
|
||||
io::printn("Boba");
|
||||
int a = 0;
|
||||
int b = 0;
|
||||
@body({| io::printfn("Send %d", a); a++; |}, b, 3, int);
|
||||
io::printfn("%d", b);
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
define void @test.main() #0 {
|
||||
entry:
|
||||
%len = alloca i64, align 8
|
||||
%error_var = alloca i64, align 8
|
||||
%retparam = alloca i64, align 8
|
||||
%error_var2 = alloca i64, align 8
|
||||
%error_var8 = alloca i64, align 8
|
||||
%a = alloca i32, align 4
|
||||
%b = alloca i32, align 4
|
||||
%foo = alloca ptr, align 8
|
||||
%len14 = alloca i64, align 8
|
||||
%error_var15 = alloca i64, align 8
|
||||
%retparam17 = alloca i64, align 8
|
||||
%error_var23 = alloca i64, align 8
|
||||
%error_var29 = alloca i64, align 8
|
||||
%varargslots = alloca [1 x %any], align 16
|
||||
%retparam37 = alloca i64, align 8
|
||||
%varargslots41 = alloca [1 x %any], align 16
|
||||
%retparam42 = alloca i64, align 8
|
||||
%varargslots47 = alloca [1 x %any], align 16
|
||||
%taddr = alloca %"char[]", align 8
|
||||
%retparam48 = alloca i64, align 8
|
||||
%varargslots51 = alloca [1 x %any], align 16
|
||||
%retparam52 = alloca i64, align 8
|
||||
%0 = call ptr @std.io.stdout()
|
||||
%1 = call i64 @std.io.File.write(ptr %retparam, ptr %0, ptr @.str, i64 4)
|
||||
%not_err = icmp eq i64 %1, 0
|
||||
%2 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
|
||||
br i1 %2, label %after_check, label %assign_optional
|
||||
|
||||
assign_optional: ; preds = %entry
|
||||
store i64 %1, ptr %error_var, align 8
|
||||
br label %guard_block
|
||||
|
||||
after_check: ; preds = %entry
|
||||
br label %noerr_block
|
||||
|
||||
guard_block: ; preds = %assign_optional
|
||||
br label %voiderr
|
||||
|
||||
noerr_block: ; preds = %after_check
|
||||
%3 = load i64, ptr %retparam, align 8
|
||||
store i64 %3, ptr %len, align 8
|
||||
%4 = call i64 @std.io.File.write_byte(ptr %0, i8 zeroext 10)
|
||||
%not_err3 = icmp eq i64 %4, 0
|
||||
%5 = call i1 @llvm.expect.i1(i1 %not_err3, i1 true)
|
||||
br i1 %5, label %after_check5, label %assign_optional4
|
||||
|
||||
assign_optional4: ; preds = %noerr_block
|
||||
store i64 %4, ptr %error_var2, align 8
|
||||
br label %guard_block6
|
||||
|
||||
after_check5: ; preds = %noerr_block
|
||||
br label %noerr_block7
|
||||
|
||||
guard_block6: ; preds = %assign_optional4
|
||||
br label %voiderr
|
||||
|
||||
noerr_block7: ; preds = %after_check5
|
||||
%6 = call i64 @std.io.File.flush(ptr %0)
|
||||
%not_err9 = icmp eq i64 %6, 0
|
||||
%7 = call i1 @llvm.expect.i1(i1 %not_err9, i1 true)
|
||||
br i1 %7, label %after_check11, label %assign_optional10
|
||||
|
||||
assign_optional10: ; preds = %noerr_block7
|
||||
store i64 %6, ptr %error_var8, align 8
|
||||
br label %guard_block12
|
||||
|
||||
after_check11: ; preds = %noerr_block7
|
||||
br label %noerr_block13
|
||||
|
||||
guard_block12: ; preds = %assign_optional10
|
||||
br label %voiderr
|
||||
|
||||
noerr_block13: ; preds = %after_check11
|
||||
%8 = load i64, ptr %len, align 8
|
||||
%add = add i64 %8, 1
|
||||
br label %voiderr
|
||||
|
||||
voiderr: ; preds = %noerr_block13, %guard_block12, %guard_block6, %guard_block
|
||||
store i32 0, ptr %a, align 4
|
||||
store i32 0, ptr %b, align 4
|
||||
store ptr %b, ptr %foo, align 8
|
||||
%9 = call ptr @std.io.stdout()
|
||||
%10 = call i64 @std.io.File.write(ptr %retparam17, ptr %9, ptr @.str.1, i64 17)
|
||||
%not_err18 = icmp eq i64 %10, 0
|
||||
%11 = call i1 @llvm.expect.i1(i1 %not_err18, i1 true)
|
||||
br i1 %11, label %after_check20, label %assign_optional19
|
||||
|
||||
assign_optional19: ; preds = %voiderr
|
||||
store i64 %10, ptr %error_var15, align 8
|
||||
br label %guard_block21
|
||||
|
||||
after_check20: ; preds = %voiderr
|
||||
br label %noerr_block22
|
||||
|
||||
guard_block21: ; preds = %assign_optional19
|
||||
br label %voiderr36
|
||||
|
||||
noerr_block22: ; preds = %after_check20
|
||||
%12 = load i64, ptr %retparam17, align 8
|
||||
store i64 %12, ptr %len14, align 8
|
||||
%13 = call i64 @std.io.File.write_byte(ptr %9, i8 zeroext 10)
|
||||
%not_err24 = icmp eq i64 %13, 0
|
||||
%14 = call i1 @llvm.expect.i1(i1 %not_err24, i1 true)
|
||||
br i1 %14, label %after_check26, label %assign_optional25
|
||||
|
||||
assign_optional25: ; preds = %noerr_block22
|
||||
store i64 %13, ptr %error_var23, align 8
|
||||
br label %guard_block27
|
||||
|
||||
after_check26: ; preds = %noerr_block22
|
||||
br label %noerr_block28
|
||||
|
||||
guard_block27: ; preds = %assign_optional25
|
||||
br label %voiderr36
|
||||
|
||||
noerr_block28: ; preds = %after_check26
|
||||
%15 = call i64 @std.io.File.flush(ptr %9)
|
||||
%not_err30 = icmp eq i64 %15, 0
|
||||
%16 = call i1 @llvm.expect.i1(i1 %not_err30, i1 true)
|
||||
br i1 %16, label %after_check32, label %assign_optional31
|
||||
|
||||
assign_optional31: ; preds = %noerr_block28
|
||||
store i64 %15, ptr %error_var29, align 8
|
||||
br label %guard_block33
|
||||
|
||||
after_check32: ; preds = %noerr_block28
|
||||
br label %noerr_block34
|
||||
|
||||
guard_block33: ; preds = %assign_optional31
|
||||
br label %voiderr36
|
||||
|
||||
noerr_block34: ; preds = %after_check32
|
||||
%17 = load i64, ptr %len14, align 8
|
||||
%add35 = add i64 %17, 1
|
||||
br label %voiderr36
|
||||
|
||||
voiderr36: ; preds = %noerr_block34, %guard_block33, %guard_block27, %guard_block21
|
||||
%18 = insertvalue %any undef, ptr %a, 0
|
||||
%19 = insertvalue %any %18, i64 ptrtoint (ptr @"$ct.int" to i64), 1
|
||||
store %any %19, ptr %varargslots, align 16
|
||||
%20 = call i64 @std.io.printfn(ptr %retparam37, ptr @.str.2, i64 7, ptr %varargslots, i64 1)
|
||||
%21 = load i32, ptr %a, align 4
|
||||
%add40 = add i32 %21, 1
|
||||
store i32 %add40, ptr %a, align 4
|
||||
%22 = insertvalue %any undef, ptr %a, 0
|
||||
%23 = insertvalue %any %22, i64 ptrtoint (ptr @"$ct.int" to i64), 1
|
||||
store %any %23, ptr %varargslots41, align 16
|
||||
%24 = call i64 @std.io.printfn(ptr %retparam42, ptr @.str.3, i64 7, ptr %varargslots41, i64 1)
|
||||
%25 = load i32, ptr %a, align 4
|
||||
%add45 = add i32 %25, 1
|
||||
store i32 %add45, ptr %a, align 4
|
||||
%26 = load ptr, ptr %foo, align 8
|
||||
%27 = load i32, ptr %26, align 4
|
||||
%add46 = add i32 %27, 3
|
||||
store i32 %add46, ptr %26, align 4
|
||||
store %"char[]" { ptr @.str.5, i64 3 }, ptr %taddr, align 8
|
||||
%28 = insertvalue %any undef, ptr %taddr, 0
|
||||
%29 = insertvalue %any %28, i64 ptrtoint (ptr @"$ct.String" to i64), 1
|
||||
store %any %29, ptr %varargslots47, align 16
|
||||
%30 = call i64 @std.io.printfn(ptr %retparam48, ptr @.str.4, i64 16, ptr %varargslots47, i64 1)
|
||||
%31 = insertvalue %any undef, ptr %b, 0
|
||||
%32 = insertvalue %any %31, i64 ptrtoint (ptr @"$ct.int" to i64), 1
|
||||
store %any %32, ptr %varargslots51, align 16
|
||||
%33 = call i64 @std.io.printfn(ptr %retparam52, ptr @.str.6, i64 2, ptr %varargslots51, i64 1)
|
||||
ret void
|
||||
}
|
||||
4
test/test_suite/macros/macro_ref_body_err1.c3
Normal file
4
test/test_suite/macros/macro_ref_body_err1.c3
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
macro void @bar(;@body(int &foo)) // #error: did you mean 'int*'
|
||||
{}
|
||||
|
||||
11
test/test_suite/macros/macro_ref_body_err2.c3
Normal file
11
test/test_suite/macros/macro_ref_body_err2.c3
Normal file
@@ -0,0 +1,11 @@
|
||||
fn void main()
|
||||
{
|
||||
@foo(;int &foo) // #error: did you mean 'int*'
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
macro void @foo(;@body(&foo))
|
||||
{
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user