Skip to content

Commit 6f6c9e9

Browse files
committed
cpp_import/clang: Better handle Unexposed types
We were previously looking at the name, which was not very reliable (or correct at all).
1 parent 1d44f81 commit 6f6c9e9

2 files changed

Lines changed: 138 additions & 41 deletions

File tree

selfhost/cpp_import/clang.jakt

Lines changed: 137 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ import cpp_import::clang_c {
2727
clang_EvalResult_getAsStr, clang_EvalResult_getAsUnsigned, clang_EvalResult_getKind
2828
clang_EvalResult_isUnsignedInt, clang_Type_getNamedType, clang_Type_getNumTemplateArguments
2929
clang_Type_getSizeOf, clang_Type_getTemplateArgumentAsType, clang_createIndex, clang_disposeIndex
30-
clang_disposeString, clang_disposeTokens, clang_disposeTranslationUnit, clang_equalCursors, clang_getArgType
31-
clang_getArrayElementType, clang_getArraySize, clang_getCString, clang_getCanonicalCursor
30+
clang_disposeString, clang_disposeTokens, clang_disposeTranslationUnit, clang_equalCursors, clang_equalTypes
31+
clang_getArgType, clang_getArrayElementType, clang_getArraySize, clang_getCString, clang_getCanonicalCursor
3232
clang_getCanonicalType, clang_getCursorDefinition, clang_getCursorExtent, clang_getCursorKind
3333
clang_getCursorKindSpelling, clang_getCursorLocation, clang_getCursorPrettyPrinted
3434
clang_getCursorPrintingPolicy, clang_getCursorResultType, clang_getCursorSemanticParent, clang_getCursorSpelling
@@ -563,6 +563,104 @@ class CppImportProcessor {
563563
return new_scope_id
564564
}
565565

566+
fn resolve_unexposed_type(
567+
mut this
568+
program: &mut CheckedProgram
569+
scope_id: ScopeId
570+
root_scope_id: ScopeId
571+
module_id: ModuleId
572+
anon t: CXType
573+
) throws -> TypeId {
574+
// Try to find a decl for the type, to avoid:
575+
// - Unexposed types that are actually elaborated types (e.g. "class Foo" instead of "Foo")
576+
// - Random types that happen to have cv-qualifiers or other decorations that cause clang to give us an unexposed type instead of the actual type.
577+
mut definition_cursor = clang_getTypeDeclaration(t)
578+
if clang_getCursorKind(definition_cursor) is CXCursor_NoDeclFound {
579+
let canonical = clang_getCanonicalType(t)
580+
if not clang_equalTypes(canonical, t) {
581+
definition_cursor = clang_getTypeDeclaration(canonical)
582+
}
583+
}
584+
585+
// If we have a valid declaration, handle like CXType_Elaborated.
586+
if not (clang_getCursorKind(definition_cursor) is CXCursor_NoDeclFound) {
587+
let template_argument_count = clang_Type_getNumTemplateArguments(t)
588+
589+
let base_type: TypeId? = match template_argument_count > 0 {
590+
true => program.find_type_in_scope(
591+
scope_id
592+
name: name_from(definition_cursor)
593+
ignore_mixin_scopes: true
594+
root_scope: root_scope_id
595+
)
596+
false => program.find_type_in_scope(
597+
scope_id
598+
name: name_from(definition_cursor)
599+
ignore_mixin_scopes: true
600+
root_scope: root_scope_id
601+
)
602+
}
603+
604+
return match base_type.has_value() {
605+
false => unknown_type_id()
606+
true => {
607+
mut type_id = base_type!
608+
let base_usr = string_from(clang_getCursorUSR(cursor: definition_cursor))
609+
610+
if base_usr == "c:@N@AK@S@ByteString" {
611+
return program.find_type_in_scope(
612+
scope_id: program.prelude_scope_id()
613+
name: "String"
614+
) ?? builtin(BuiltinType::JaktString)
615+
}
616+
617+
let is_nnrp_class_type = base_usr.starts_with("c:@N@AK@S@NonnullRefPtr>") or base_usr == "c:@N@AK@ST>1#T@NonnullRefPtr"
618+
let is_ak_function = base_usr.starts_with("c:@N@AK@S@Function>")
619+
620+
if template_argument_count > 0 {
621+
let count = template_argument_count as! u32
622+
mut args: [TypeId] = []
623+
for i in 0..count {
624+
let type = clang_Type_getTemplateArgumentAsType(t, i)
625+
args.push(.type_from(program, scope_id, root_scope_id, module_id, type))
626+
}
627+
628+
if count == 1 and (is_nnrp_class_type or is_ak_function) {
629+
type_id = args[0]
630+
} else {
631+
type_id = program.find_or_add_type_id(
632+
type: match program.get_type(base_type!) {
633+
Struct(id) | GenericInstance(id) => Type::GenericInstance(id, args)
634+
Enum(id) | GenericEnumInstance(id) => Type::GenericEnumInstance(id, args)
635+
else => {
636+
if .debug_print { eprintln("ICE: Unexposed type is not a struct or enum: {}", program.type_name(base_type!, debug_mode: true)) }
637+
yield Type::Unknown
638+
}
639+
}
640+
module_id
641+
only_in_current_module: true
642+
)
643+
}
644+
}
645+
646+
yield type_id
647+
}
648+
}
649+
}
650+
651+
// No declaration found (e.g. type parameters). Fall back to spelling-based lookup.
652+
let name = string_from(clang_getTypeSpelling(t))
653+
if name.is_empty() {
654+
return builtin(BuiltinType::Unknown)
655+
}
656+
return program.find_type_in_scope(
657+
scope_id
658+
name
659+
ignore_mixin_scopes: true
660+
root_scope: root_scope_id
661+
) ?? builtin(BuiltinType::Unknown)
662+
}
663+
566664
fn type_from(
567665
mut this
568666
program: &mut CheckedProgram
@@ -668,37 +766,44 @@ class CppImportProcessor {
668766
mut type_id = base_type_id
669767
let base_usr = string_from(clang_getCursorUSR(cursor: definition_cursor))
670768

671-
// NonnullRefPtr<T> where T is a class -> T
672-
let is_nnrp_class_type = base_usr.starts_with("c:@N@AK@S@NonnullRefPtr>") or base_usr == "c:@N@AK@ST>1#T@NonnullRefPtr"
673-
// Function<T> -> T
674-
let is_ak_function = base_usr.starts_with("c:@N@AK@S@Function>")
675-
676-
// Check template params
677-
if template_argument_count > 0 {
678-
let count = template_argument_count as! u32
679-
mut args: [TypeId] = []
680-
for i in 0..count {
681-
let type = clang_Type_getTemplateArgumentAsType(t, i)
682-
args.push(.type_from(program, scope_id, root_scope_id, module_id, type))
683-
}
769+
if base_usr == "c:@N@AK@S@ByteString" {
770+
type_id = program.find_type_in_scope(
771+
scope_id: program.prelude_scope_id()
772+
name: "String"
773+
) ?? builtin(BuiltinType::JaktString)
774+
} else {
775+
// NonnullRefPtr<T> where T is a class -> T
776+
let is_nnrp_class_type = base_usr.starts_with("c:@N@AK@S@NonnullRefPtr>") or base_usr == "c:@N@AK@ST>1#T@NonnullRefPtr"
777+
// Function<T> -> T
778+
let is_ak_function = base_usr.starts_with("c:@N@AK@S@Function>")
779+
780+
// Check template params
781+
if template_argument_count > 0 {
782+
let count = template_argument_count as! u32
783+
mut args: [TypeId] = []
784+
for i in 0..count {
785+
let type = clang_Type_getTemplateArgumentAsType(t, i)
786+
args.push(.type_from(program, scope_id, root_scope_id, module_id, type))
787+
}
684788

685-
if count == 1 and (is_nnrp_class_type or is_ak_function) {
686-
// Special case: Treat NNRP<T> as T when T is a class type
687-
// Special case: Treat Function<Fn> as Fn.
688-
type_id = args[0]
689-
} else {
690-
type_id = program.find_or_add_type_id(
691-
type: match program.get_type(base_type!) {
692-
Struct(id) | GenericInstance(id) => Type::GenericInstance(id, args)
693-
Enum(id) | GenericEnumInstance(id) => Type::GenericEnumInstance(id, args)
694-
else => {
695-
if .debug_print { eprintln("ICE: Elaborated type is not a struct or enum: {}", program.type_name(base_type!, debug_mode: true)) }
696-
yield Type::Unknown
789+
if count == 1 and (is_nnrp_class_type or is_ak_function) {
790+
// Special case: Treat NNRP<T> as T when T is a class type
791+
// Special case: Treat Function<Fn> as Fn.
792+
type_id = args[0]
793+
} else {
794+
type_id = program.find_or_add_type_id(
795+
type: match program.get_type(base_type!) {
796+
Struct(id) | GenericInstance(id) => Type::GenericInstance(id, args)
797+
Enum(id) | GenericEnumInstance(id) => Type::GenericEnumInstance(id, args)
798+
else => {
799+
if .debug_print { eprintln("ICE: Elaborated type is not a struct or enum: {}", program.type_name(base_type!, debug_mode: true)) }
800+
yield Type::Unknown
801+
}
697802
}
698-
}
699-
module_id
700-
only_in_current_module: true
701-
)
803+
module_id
804+
only_in_current_module: true
805+
)
806+
}
702807
}
703808
}
704809

@@ -707,16 +812,7 @@ class CppImportProcessor {
707812
}
708813
}
709814
CXType_Unexposed => {
710-
let name = string_from(clang_getTypeSpelling(t))
711-
yield match name.is_empty() {
712-
true => builtin(BuiltinType::Unknown)
713-
false => program.find_type_in_scope(
714-
scope_id
715-
name
716-
ignore_mixin_scopes: true
717-
root_scope: root_scope_id
718-
) ?? builtin(BuiltinType::Unknown)
719-
}
815+
yield .resolve_unexposed_type(program, scope_id, root_scope_id, module_id, t)
720816
}
721817
CXType_LValueReference => program.find_or_add_type_id(
722818
type: Type::Reference(

selfhost/cpp_import/clang_c.jakt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ import extern c "clang-c/Index.h" {
514514
extern fn clang_getCString(anon string: CXString) -> raw c_char
515515
extern fn clang_disposeString(anon string: CXString)
516516
extern fn clang_getCanonicalType(anon type: CXType) -> CXType
517+
extern fn clang_equalTypes(anon a: CXType, anon b: CXType) -> bool
517518
extern fn clang_getPointeeType(anon type: CXType) -> CXType
518519
extern fn clang_CXXMethod_isStatic(anon cursor: CXCursor) -> bool
519520
extern fn clang_CXXMethod_isVirtual(anon cursor: CXCursor) -> bool

0 commit comments

Comments
 (0)