diff --git a/ANNOTATION_INFO_REWORK.md b/ANNOTATION_INFO_REWORK.md index 9f9868c29..abfef42dd 100644 --- a/ANNOTATION_INFO_REWORK.md +++ b/ANNOTATION_INFO_REWORK.md @@ -100,11 +100,14 @@ Deprecated (kept working, `[deprecated]`): ## das-side string fields and AOT `AnnotationInfo.name` / `AnnotationArgumentInfo.name` (and `sValue`, `module_name`) are -`const char *` bound as das `string` — same as `VarInfo.name`. Comparisons and string -interpolation work in all tiers, but passing the field *directly as a function argument* -compiles interpreted and fails AOT C++ compilation (`const char*` → `char*`). This is a -pre-existing emitter wart shared by every `const char *` bound field; the in-tree -convention is to interpolate (`"{ann.name}"`) when passing onward. +`const char *` bound as das `string` — same as `VarInfo.name`. Historically, passing such +a field *directly as a function argument* compiled interpreted but failed AOT C++ +compilation (`const char*` → `char*`): the emitter cast const-owner string field reads to +`(const char * const)`. Fixed in the follow-up to PR #3101 — const-owner reads now emit a +two-step cast `((char *)(const char *)(...))`: the inner cast picks the const-qualified +conversion operator on class-typed members (the `SimpleString` case from f38081856), the +outer strips pointee-const, which is safe because das strings are immutable and always +lower to `char *`. Direct field passes now work in all tiers; no interpolation needed. ## Known pre-existing gap (unchanged) diff --git a/daslib/aot_cpp.das b/daslib/aot_cpp.das index 66ea6a4d7..eb50c3a91 100644 --- a/daslib/aot_cpp.das +++ b/daslib/aot_cpp.das @@ -2274,9 +2274,11 @@ class public CppAot : AstVisitor { write(*ss, "{get_variant_field(field_type, field.fieldIndex)}::get(") } elif (field_type.isHandle) { if (field._type.isString) { - // c-cast const char * etc string casts to char * or char * const + // two-step cast: inner (const char *) picks the const-qualified conversion on + // const owners (e.g. operator const char*() const), outer strips pointee-const + // since das string always lowers to char * and is immutable anyway if (field.value._type.flags.constant) { - write(*ss, "((const char * const)("); + write(*ss, "((char *)(const char *)("); } else { write(*ss, "(({describeCppType(field._type, DescribeConfig(cross_platform=cross_platform))})("); } @@ -2289,9 +2291,9 @@ class public CppAot : AstVisitor { } elif (field_type.baseType == Type.tPointer) { if (field_type.firstType.isHandle) { if (field._type.isString) { - // c-cast const char * etc string casts to char * or char * const + // two-step cast, same as the by-value handle case above if (field.value._type.flags.constant) { - write(*ss, "((const char * const)("); + write(*ss, "((char *)(const char *)("); } else { write(*ss, "(({describeCppType(field._type, DescribeConfig(cross_platform=cross_platform))})("); } diff --git a/modules/dasLLVM/daslib/llvm_jit.das b/modules/dasLLVM/daslib/llvm_jit.das index fe18b5fca..3a72ca4df 100644 --- a/modules/dasLLVM/daslib/llvm_jit.das +++ b/modules/dasLLVM/daslib/llvm_jit.das @@ -4665,8 +4665,8 @@ class public LlvmJitVisitor : AstVisitor { let ann_ty = get_llvm_type_for_annotation_argument_info() var fields = fixed_array( types.ConstI32(uint64(int(arg.basicType))), - get_string_constant_ptr(g_builder, "{arg.name}"), - arg.basicType == Type.tString ? get_string_constant_ptr(g_builder, "{arg.sValue}") : LLVMConstPointerNull(types.LLVMVoidPtrType()), + get_string_constant_ptr(g_builder, arg.name), + arg.basicType == Type.tString ? get_string_constant_ptr(g_builder, arg.sValue) : LLVMConstPointerNull(types.LLVMVoidPtrType()), types.ConstI32(uint64(uint(arg.iValue)))) return LLVMConstNamedStruct(ann_ty, array_data_ptr(fields), 4u) } @@ -4708,8 +4708,8 @@ class public LlvmJitVisitor : AstVisitor { args_ptr = g_builder |> LLVMBuildPointerCast(args_global, types.LLVMVoidPtrType(), "") } var fields = fixed_array( - get_string_constant_ptr(g_builder, "{ann.name}"), - get_string_constant_ptr(g_builder, "{ann.module_name}"), + get_string_constant_ptr(g_builder, ann.name), + get_string_constant_ptr(g_builder, ann.module_name), args_ptr, types.ConstI32(ann.count |> uint64()), LLVMConstPointerNull(types.LLVMVoidPtrType())) diff --git a/tests/language/annotation_info.das b/tests/language/annotation_info.das index 00b634c94..fa1cd37be 100644 --- a/tests/language/annotation_info.das +++ b/tests/language/annotation_info.das @@ -38,7 +38,8 @@ def test_struct_annotations(t : T?) { t |> equal(int(sinfo.annotation_count), 1) var seen_args = 0 for (ann in each_annotation(*sinfo)) { - t |> equal("{ann.name}", "comment") + // direct const char*-backed field pass — regression for the AOT (char*)(const char*) cast + t |> equal(ann.name, "comment") t |> equal(int(ann.count), 5) for (arg in each_annotation_argument(ann)) { let value = get_annotation_argument_value(arg) @@ -78,7 +79,7 @@ def test_field_annotations(t : T?) { if (fld.name == "a") { t |> equal(int(fld.annotation_argument_count), 1) for (arg in each_annotation_argument(fld)) { - t |> equal("{arg.name}", "v_int") + t |> equal(arg.name, "v_int") t |> equal(get_annotation_argument_value(arg) as tInt, 13) } checked ++