diff --git a/releasenotes.md b/releasenotes.md index 62c8fdec4..e4dca4d8c 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -42,6 +42,7 @@ - Fix issue where a compile time parameter is followed by "...". - Fix issue with some conversions to untyped list. - Issue where a `if (catch e = ...)` in a defer would be incorrectly copied. Causing codegen error. +- Variable in if-try / if-catch cannot be a reused variable name. ### Stdlib changes diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 16d9915fb..9b347baea 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -368,9 +368,7 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) case EXPR_CATCH_UNWRAP: if (expr->resolve_status == RESOLVE_DONE) { - Decl *fix = expr->catch_unwrap_expr.decl; - fixup_decl(c, &fix); - if (fix == expr->catch_unwrap_expr.decl) MACRO_COPY_DECL(expr->catch_unwrap_expr.decl); + MACRO_COPY_DECL(expr->catch_unwrap_expr.decl); MACRO_COPY_EXPR(expr->catch_unwrap_expr.lhs); } else diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 2a683fde2..c4f5f1718 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -731,118 +731,56 @@ static inline bool sema_analyse_try_unwrap(SemaContext *context, Expr *expr) } // Case B. We are unwrapping to a variable that may or may not exist. - bool implicit_declaration = false; TypeInfo *var_type = expr->try_unwrap_expr.type; - // 1. Check if we are doing an implicit declaration. - if (!var_type && ident->expr_kind == EXPR_IDENTIFIER) - { - BoolErr res = sema_symbol_is_defined_in_scope(context, ident->identifier_expr.ident); - if (res == BOOL_ERR) return false; - implicit_declaration = res == BOOL_FALSE; - } - // 2. If we have a type for the variable, resolve it. if (var_type) { if (!sema_resolve_type_info(context, var_type, RESOLVE_TYPE_DEFAULT)) return false; if (IS_OPTIONAL(var_type)) { - SEMA_ERROR(var_type, "Only non-optional types may be used as types for 'try', please remove the '!'."); - return false; + RETURN_SEMA_ERROR(var_type, "Only non-optional types may be used as types for 'try', please remove the '!'."); } } - // 3. We interpret this as an assignment to an existing variable. - if (!var_type && !implicit_declaration) + // 3. We are creating a new variable + + // 3a. If we had a variable type, then our expression must be an identifier. + if (ident->expr_kind != EXPR_IDENTIFIER) RETURN_SEMA_ERROR(ident, "A variable name was expected here."); + + assert(ident->resolve_status != RESOLVE_DONE); + if (ident->identifier_expr.path) RETURN_SEMA_ERROR(ident->identifier_expr.path, "The variable may not have a path."); + + if (ident->identifier_expr.is_const) RETURN_SEMA_ERROR(ident, "Expected a variable starting with a lower case letter."); + + // 3b. Evaluate the expression + if (!sema_analyse_expr(context, optional)) return false; + + if (!IS_OPTIONAL(optional)) { - // 3a. Resolve the identifier. - if (!sema_analyse_expr_lvalue(context, ident)) return false; - - // 3b. Make sure it's assignable - if (!sema_expr_check_assign(context, ident)) return false; - - // 3c. It can't be optional either. - if (IS_OPTIONAL(ident)) - { - if (ident->expr_kind == EXPR_IDENTIFIER) - { - SEMA_ERROR(ident, "This is an optional variable, you should only have non-optional variables on the left side unless you use 'try' without '='."); - } - else - { - SEMA_ERROR(ident, "This is an optional expression, it can't go on the left hand side of a 'try'."); - } - return false; - } - - // 3d. We can now analyse the expression using the variable type. - if (!sema_analyse_expr(context, optional)) return false; - - if (!IS_OPTIONAL(optional)) - { - SEMA_ERROR(optional, "Expected an optional expression to 'try' here. If it isn't an optional, remove 'try'."); - return false; - } - - if (!cast_implicit(context, optional, ident->type, false)) return false; - - expr->try_unwrap_expr.assign_existing = true; - expr->try_unwrap_expr.lhs = ident; + RETURN_SEMA_ERROR(optional, "Expected an optional expression to 'try' here. If it isn't an optional, remove 'try'."); + return false; } - else + + if (var_type) { - // 4. We are creating a new variable - - // 4a. If we had a variable type, then our expression must be an identifier. - if (ident->expr_kind != EXPR_IDENTIFIER) - { - SEMA_ERROR(ident, "A variable name was expected here."); - return false; - } - - assert(ident->resolve_status != RESOLVE_DONE); - if (ident->identifier_expr.path) - { - SEMA_ERROR(ident->identifier_expr.path, "The variable may not have a path."); - return false; - } - - if (ident->identifier_expr.is_const) - { - SEMA_ERROR(ident, "Expected a variable starting with a lower case letter."); - return false; - } - - // 4b. Evaluate the expression - if (!sema_analyse_expr(context, optional)) return false; - - if (!IS_OPTIONAL(optional)) - { - SEMA_ERROR(optional, "Expected an optional expression to 'try' here. If it isn't an optional, remove 'try'."); - return false; - } - - if (var_type) - { - if (!cast_implicit(context, optional, var_type->type, false)) return false; - } - - // 4c. Create a type_info if needed. - if (!var_type) - { - var_type = type_info_new_base(optional->type->optional, optional->span); - } - - // 4d. A new declaration is created. - Decl *decl = decl_new_var(ident->identifier_expr.ident, ident->span, var_type, VARDECL_LOCAL); - - // 4e. Analyse it - if (!sema_analyse_var_decl(context, decl, true)) return false; - - expr->try_unwrap_expr.decl = decl; + if (!cast_implicit(context, optional, var_type->type, false)) return false; } + // 4c. Create a type_info if needed. + if (!var_type) + { + var_type = type_info_new_base(optional->type->optional, optional->span); + } + + // 4d. A new declaration is created. + Decl *decl = decl_new_var(ident->identifier_expr.ident, ident->span, var_type, VARDECL_LOCAL); + + // 4e. Analyse it + if (!sema_analyse_var_decl(context, decl, true)) return false; + + expr->try_unwrap_expr.decl = decl; + expr->try_unwrap_expr.optional = optional; expr->type = type_bool; expr->resolve_status = RESOLVE_DONE; @@ -875,7 +813,6 @@ static inline bool sema_analyse_catch_unwrap(SemaContext *context, Expr *expr) { Expr *ident = expr->catch_unwrap_expr.variable; - bool implicit_declaration = false; TypeInfo *type = expr->catch_unwrap_expr.type; if (!type && !ident) @@ -884,70 +821,34 @@ static inline bool sema_analyse_catch_unwrap(SemaContext *context, Expr *expr) expr->catch_unwrap_expr.decl = NULL; goto RESOLVE_EXPRS; } - if (!type && ident->expr_kind == EXPR_IDENTIFIER) + type = type ? type : type_info_new_base(type_anyfault, expr->span); + + if (!sema_resolve_type_info(context, type, RESOLVE_TYPE_DEFAULT)) return false; + + if (type->type->canonical != type_anyfault) { - BoolErr res = sema_symbol_is_defined_in_scope(context, ident->identifier_expr.ident); - if (res == BOOL_ERR) return false; - implicit_declaration = res == BOOL_FALSE; + RETURN_SEMA_ERROR(type, "Expected the type to be %s, not %s.", type_quoted_error_string(type_anyfault), + type_quoted_error_string(type->type)); + } + if (ident->expr_kind != EXPR_IDENTIFIER) + { + RETURN_SEMA_ERROR(ident, "A variable name was expected here."); } - if (!type && !implicit_declaration) - { - if (!sema_analyse_expr_lvalue(context, ident)) return false; + assert(ident->resolve_status != RESOLVE_DONE); + if (ident->identifier_expr.path) RETURN_SEMA_ERROR(ident->identifier_expr.path, "The variable may not have a path."); + if (ident->identifier_expr.is_const) RETURN_SEMA_ERROR(ident, "Expected a variable starting with a lower case letter."); - if (!sema_expr_check_assign(context, ident)) return false; + // 4d. A new declaration is created. + Decl *decl = decl_new_var(ident->identifier_expr.ident, ident->span, type, VARDECL_LOCAL); + decl->var.no_init = true; - if (ident->type->canonical != type_anyfault) - { - SEMA_ERROR(ident, "Expected the variable to have the type %s, not %s.", type_quoted_error_string(type_anyfault), - type_quoted_error_string(ident->type)); - return false; - } + // 4e. Analyse it + if (!sema_analyse_var_decl(context, decl, true)) return false; - expr->catch_unwrap_expr.lhs = ident; - expr->catch_unwrap_expr.decl = NULL; - } - else - { - type = type ? type : type_info_new_base(type_anyfault, expr->span); + expr->catch_unwrap_expr.decl = decl; + expr->catch_unwrap_expr.lhs = NULL; - if (!sema_resolve_type_info(context, type, RESOLVE_TYPE_DEFAULT)) return false; - - if (type->type->canonical != type_anyfault) - { - SEMA_ERROR(type, "Expected the type to be %s, not %s.", type_quoted_error_string(type_anyfault), - type_quoted_error_string(type->type)); - return false; - } - if (ident->expr_kind != EXPR_IDENTIFIER) - { - SEMA_ERROR(ident, "A variable name was expected here."); - return false; - } - - assert(ident->resolve_status != RESOLVE_DONE); - if (ident->identifier_expr.path) - { - SEMA_ERROR(ident->identifier_expr.path, "The variable may not have a path."); - return false; - } - - if (ident->identifier_expr.is_const) - { - SEMA_ERROR(ident, "Expected a variable starting with a lower case letter."); - return false; - } - - // 4d. A new declaration is created. - Decl *decl = decl_new_var(ident->identifier_expr.ident, ident->span, type, VARDECL_LOCAL); - decl->var.no_init = true; - - // 4e. Analyse it - if (!sema_analyse_var_decl(context, decl, true)) return false; - - expr->catch_unwrap_expr.decl = decl; - expr->catch_unwrap_expr.lhs = NULL; - } RESOLVE_EXPRS:; FOREACH(Expr *, fail, expr->catch_unwrap_expr.exprs) { diff --git a/test/test_suite/defer/defer_with_catch.c3t b/test/test_suite/defer/defer_with_catch.c3t index 5892014b8..e48b18aec 100644 --- a/test/test_suite/defer/defer_with_catch.c3t +++ b/test/test_suite/defer/defer_with_catch.c3t @@ -6,7 +6,6 @@ fn void main() { int a; defer { if (catch e = test()) {} } - anyfault b; defer { if (catch b = test()) {} } if (a > 0) return; } @@ -16,74 +15,92 @@ entry: %a = alloca i32, align 4 %b = alloca i64, align 8 %e = alloca i64, align 8 - %e13 = alloca i64, align 8 + %b7 = alloca i64, align 8 + %e14 = alloca i64, align 8 store i32 0, ptr %a, align 4 - store i64 0, ptr %b, align 8 %0 = load i32, ptr %a, align 4 %gt = icmp sgt i32 %0, 0 br i1 %gt, label %if.then, label %if.exit + if.then: ; preds = %entry br label %testblock + testblock: ; preds = %if.then %1 = call i64 @test.test() %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 = %testblock store i64 %1, ptr %b, align 8 br label %end_block + after_check: ; preds = %testblock store i64 0, ptr %b, align 8 br label %end_block + end_block: ; preds = %after_check, %assign_optional %3 = load i64, ptr %b, align 8 %neq = icmp ne i64 %3, 0 br label %testblock1 + testblock1: ; preds = %end_block %4 = call i64 @test.test() %not_err2 = icmp eq i64 %4, 0 %5 = call i1 @llvm.expect.i1(i1 %not_err2, i1 true) br i1 %5, label %after_check4, label %assign_optional3 + assign_optional3: ; preds = %testblock1 store i64 %4, ptr %e, align 8 br label %end_block5 + after_check4: ; preds = %testblock1 store i64 0, ptr %e, align 8 br label %end_block5 + end_block5: ; preds = %after_check4, %assign_optional3 %6 = load i64, ptr %e, align 8 %neq6 = icmp ne i64 %6, 0 ret void + if.exit: ; preds = %entry - br label %testblock7 -testblock7: ; preds = %if.exit + br label %testblock8 + +testblock8: ; preds = %if.exit %7 = call i64 @test.test() - %not_err8 = icmp eq i64 %7, 0 - %8 = call i1 @llvm.expect.i1(i1 %not_err8, i1 true) - br i1 %8, label %after_check10, label %assign_optional9 -assign_optional9: ; preds = %testblock7 - store i64 %7, ptr %b, align 8 - br label %end_block11 -after_check10: ; preds = %testblock7 - store i64 0, ptr %b, align 8 - br label %end_block11 -end_block11: ; preds = %after_check10, %assign_optional9 - %9 = load i64, ptr %b, align 8 - %neq12 = icmp ne i64 %9, 0 - br label %testblock14 -testblock14: ; preds = %end_block11 + %not_err9 = icmp eq i64 %7, 0 + %8 = call i1 @llvm.expect.i1(i1 %not_err9, i1 true) + br i1 %8, label %after_check11, label %assign_optional10 + +assign_optional10: ; preds = %testblock8 + store i64 %7, ptr %b7, align 8 + br label %end_block12 + +after_check11: ; preds = %testblock8 + store i64 0, ptr %b7, align 8 + br label %end_block12 + +end_block12: ; preds = %after_check11, %assign_optional10 + %9 = load i64, ptr %b7, align 8 + %neq13 = icmp ne i64 %9, 0 + br label %testblock15 + +testblock15: ; preds = %end_block12 %10 = call i64 @test.test() - %not_err15 = icmp eq i64 %10, 0 - %11 = call i1 @llvm.expect.i1(i1 %not_err15, i1 true) - br i1 %11, label %after_check17, label %assign_optional16 -assign_optional16: ; preds = %testblock14 - store i64 %10, ptr %e13, align 8 - br label %end_block18 -after_check17: ; preds = %testblock14 - store i64 0, ptr %e13, align 8 - br label %end_block18 -end_block18: ; preds = %after_check17, %assign_optional16 - %12 = load i64, ptr %e13, align 8 - %neq19 = icmp ne i64 %12, 0 + %not_err16 = icmp eq i64 %10, 0 + %11 = call i1 @llvm.expect.i1(i1 %not_err16, i1 true) + br i1 %11, label %after_check18, label %assign_optional17 + +assign_optional17: ; preds = %testblock15 + store i64 %10, ptr %e14, align 8 + br label %end_block19 + +after_check18: ; preds = %testblock15 + store i64 0, ptr %e14, align 8 + br label %end_block19 + +end_block19: ; preds = %after_check18, %assign_optional17 + %12 = load i64, ptr %e14, align 8 + %neq20 = icmp ne i64 %12, 0 ret void -} \ No newline at end of file +} diff --git a/test/test_suite/errors/try_assign.c3t b/test/test_suite/errors/try_assign.c3t index 9842653f8..d8770d959 100644 --- a/test/test_suite/errors/try_assign.c3t +++ b/test/test_suite/errors/try_assign.c3t @@ -18,7 +18,6 @@ fn void main() { printf("Test\n"); } - anyfault e; if (catch e = z) { printf("Oh noes!\n"); @@ -27,7 +26,6 @@ fn void main() /* #expect: try_assign.ll - define void @try_assign.main() #0 { entry: %x = alloca i32, align 4 @@ -125,7 +123,6 @@ if.then17: ; preds = %phi_try_catch15 br label %if.exit18 if.exit18: ; preds = %if.then17, %phi_try_catch15 - store i64 0, ptr %e, align 8 br label %testblock testblock: ; preds = %if.exit18 diff --git a/test/test_suite/errors/try_catch_unwrapping_while_if.c3 b/test/test_suite/errors/try_catch_unwrapping_while_if.c3 index fac45d9f4..2153a110f 100644 --- a/test/test_suite/errors/try_catch_unwrapping_while_if.c3 +++ b/test/test_suite/errors/try_catch_unwrapping_while_if.c3 @@ -87,7 +87,6 @@ fn void test9() { int! z = 234; int! w = 123; - anyfault e; if (catch e = z) { }