diff --git a/compiler/checker/checker.odin b/compiler/checker/checker.odin index 460e7f3..5364605 100644 --- a/compiler/checker/checker.odin +++ b/compiler/checker/checker.odin @@ -2804,9 +2804,9 @@ infer_call_comptime_values :: proc( for value_bound in bound { all_bound = all_bound && value_bound } - // Concrete arguments bind first. Numeric constants and `null` are contextual and - // therefore only contribute after stronger evidence has had a chance to bind the - // parameter type. + // Concrete arguments bind first. Numeric constants, string literals, and `null` + // are contextual and therefore only contribute after stronger evidence has had a + // chance to bind the parameter type. weak_passes := [2]bool{false, true} for weak in weak_passes { for arg_id, source_index in args { @@ -2819,7 +2819,8 @@ infer_call_comptime_values :: proc( } arg_expr := checker.ast_module.exprs[arg_id] is_null := arg_expr.kind == .Null - is_weak := is_numeric_constant_expr(checker, arg_id) || is_null + is_weak := is_numeric_constant_expr(checker, arg_id) || + arg_expr.kind == .String || is_null if is_weak != weak { continue } diff --git a/compiler_tests.odin b/compiler_tests.odin index 5f10c50..bde4c46 100644 --- a/compiler_tests.odin +++ b/compiler_tests.odin @@ -4686,6 +4686,55 @@ main func() void { testing.expect(t, unrecoverable >= 3) } +@(test) +string_literals_are_contextual_comptime_type_inference_evidence :: proc(t: ^testing.T) { + text := `pair func($T type, expected, actual T) T { + _ = expected + return actual +} +identity func($T type, value T) T { + return value +} +main func() void { + actual []u8 :: "hello" + contextual :: pair("hello", actual) + exact :: identity("hello") + _ = contextual + _ = exact +} +` + source_file := source.Source{path="test.bro", text=text} + diagnostics := source.init_diagnostics(&source_file) + defer source.destroy_diagnostics(&diagnostics) + symbols := symbol.init_table() + defer symbol.destroy_table(&symbols) + stream := lexer.lex(&source_file, &diagnostics, &symbols) + defer delete(stream.items) + ast_module := parser.parse(&stream, &source_file, &diagnostics) + defer ast.destroy_module(&ast_module) + hir_module := checker.check(&ast_module, &diagnostics, &symbols) + defer hir.destroy_module(&hir_module) + + pair_found := false + identity_found := false + for function in hir_module.functions { + name := symbol.resolve(&symbols, function.name) + if name == "pair" { + pair_found = true + testing.expect(t, types.is_slice(function.result, &hir_module.types)) + } else if name == "identity" { + identity_found = true + _, array, ok := types.array_pointer(function.result, &hir_module.types) + testing.expect(t, ok && array.child == types.U8 && array.count == 5 && + array.has_sentinel && array.sentinel == 0) + } + } + + testing.expect_value(t, len(diagnostics.items), 0) + testing.expect(t, pair_found) + testing.expect(t, identity_found) +} + @(test) comptime_params_may_be_interleaved_and_are_erased_from_the_abi :: proc(t: ^testing.T) { text := `valid func($T type, $N usize, value T) [N]T {