Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions ANNOTATION_INFO_REWORK.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
10 changes: 6 additions & 4 deletions daslib/aot_cpp.das
Original file line number Diff line number Diff line change
Expand Up @@ -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))})(");
}
Expand All @@ -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))})(");
}
Expand Down
8 changes: 4 additions & 4 deletions modules/dasLLVM/daslib/llvm_jit.das
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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()))
Expand Down
5 changes: 3 additions & 2 deletions tests/language/annotation_info.das
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 ++
Expand Down
Loading