Skip to content

Commit fc68046

Browse files
committed
Add Type#intrinsic_type for cv-ref-pointer stripping
Mirrors Rice's `intrinsic_type` metafunction: strip the reference, follow pointer indirection until reaching a non-pointer type, then drop cv-qualifiers. Useful for skip-list and bindability checks that want to ask "what does this type ultimately denote?" without dispatching on every reference/pointer/cv combination separately. Examples: * `T &` → `T` * `T *` → `T` * `T **` → `T` * `T *&` → `T` * `T **&` → `T` * `const T &` → `T` Composes the existing non_reference_type / pointee / unqualified_type walkers, all of which were recently hardened against type_invalid input, so this method is safe on invalid types too.
1 parent b94ddd6 commit fc68046

3 files changed

Lines changed: 107 additions & 0 deletions

File tree

lib/ffi/clang/types/type.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,27 @@ def non_reference_type
151151
Type.create Lib.get_non_reference_type(@type), @translation_unit
152152
end
153153

154+
# Get the intrinsic type — strip the reference, follow pointer
155+
# indirection until reaching a non-pointer type, then drop
156+
# cv-qualifiers. Named after Rice's `intrinsic_type` metafunction
157+
# of the same shape. Useful when asking "what does this type
158+
# ultimately denote?" for skip-list and bindability checks.
159+
#
160+
# Examples:
161+
# * `T &` becomes `T`
162+
# * `T *` becomes `T`
163+
# * `T **&` becomes `T`
164+
# * `const T &` becomes `T`
165+
#
166+
# @returns [Type] The intrinsic (innermost, unqualified) type.
167+
def intrinsic_type
168+
type = self.non_reference_type
169+
while type.kind == :type_pointer
170+
type = type.pointee
171+
end
172+
type.unqualified_type
173+
end
174+
154175
# Get the type of a template argument at the given index.
155176
# For template specializations (e.g., `std::vector<int>`), this returns the type of
156177
# the template argument at the specified position.

spec/ffi/clang/fixtures/test.cxx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ void f_variadic(int a, ...);
4040
void f_non_variadic(int a, char b, long c);
4141

4242
typedef int const* const_int_ptr;
43+
typedef int** int_pp;
44+
void takesPtrRefs(int*& pRef, int**& ppRef);
4345
int int_array[8];
4446

4547
struct RefQualifier {

spec/ffi/clang/type_spec.rb

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,90 @@
198198
end
199199
end
200200

201+
describe "#intrinsic_type" do
202+
it "strips an lvalue reference" do
203+
ref_type = find_matching(cursor_cxx) do |child, parent|
204+
child.kind == :cursor_cxx_method and child.spelling == "takesARef"
205+
end.type.arg_types.to_a[0]
206+
expect(ref_type.kind).to eq(:type_lvalue_ref)
207+
208+
intrinsic = ref_type.intrinsic_type
209+
expect(intrinsic.kind).to eq(:type_int)
210+
end
211+
212+
it "strips an rvalue reference" do
213+
ref_type = find_matching(cursor_cxx) do |child, parent|
214+
child.kind == :cursor_cxx_method and child.spelling == "takesARef"
215+
end.type.arg_types.to_a[1]
216+
expect(ref_type.kind).to eq(:type_rvalue_ref)
217+
218+
intrinsic = ref_type.intrinsic_type
219+
expect(intrinsic.kind).to eq(:type_float)
220+
end
221+
222+
it "follows a single pointer and drops cv-qualifiers" do
223+
# const_int_ptr is `int const *`; intrinsic_type should yield `int`.
224+
ptr_type = find_matching(cursor_cxx) do |child, parent|
225+
child.kind == :cursor_typedef_decl and child.spelling == "const_int_ptr"
226+
end.type.canonical
227+
expect(ptr_type.kind).to eq(:type_pointer)
228+
229+
intrinsic = ptr_type.intrinsic_type
230+
expect(intrinsic.kind).to eq(:type_int)
231+
expect(intrinsic.const_qualified?).to be false
232+
end
233+
234+
it "follows a pointer-to-pointer chain" do
235+
# int_pp is `int **`; intrinsic_type should yield `int`.
236+
pp_type = find_matching(cursor_cxx) do |child, parent|
237+
child.kind == :cursor_typedef_decl and child.spelling == "int_pp"
238+
end.type.canonical
239+
expect(pp_type.kind).to eq(:type_pointer)
240+
241+
intrinsic = pp_type.intrinsic_type
242+
expect(intrinsic.kind).to eq(:type_int)
243+
end
244+
245+
it "strips a reference to a pointer" do
246+
# takesPtrRefs(int*& pRef, ...) — first arg is `int *&`.
247+
arg_type = find_matching(cursor_cxx) do |child, parent|
248+
child.kind == :cursor_function and child.spelling == "takesPtrRefs"
249+
end.type.arg_types.to_a[0]
250+
expect(arg_type.kind).to eq(:type_lvalue_ref)
251+
252+
intrinsic = arg_type.intrinsic_type
253+
expect(intrinsic.kind).to eq(:type_int)
254+
end
255+
256+
it "strips a reference to a pointer-to-pointer" do
257+
# takesPtrRefs(..., int**& ppRef) — second arg is `int **&`.
258+
arg_type = find_matching(cursor_cxx) do |child, parent|
259+
child.kind == :cursor_function and child.spelling == "takesPtrRefs"
260+
end.type.arg_types.to_a[1]
261+
expect(arg_type.kind).to eq(:type_lvalue_ref)
262+
263+
intrinsic = arg_type.intrinsic_type
264+
expect(intrinsic.kind).to eq(:type_int)
265+
end
266+
267+
it "returns the unchanged kind for a fundamental type" do
268+
int_type = find_matching(cursor_cxx) do |child, parent|
269+
child.kind == :cursor_field_decl and child.spelling == "int_member_a"
270+
end.type
271+
expect(int_type.kind).to eq(:type_int)
272+
expect(int_type.intrinsic_type.kind).to eq(:type_int)
273+
end
274+
275+
it "returns an invalid type without crashing on a type_invalid input" do
276+
invalid_cxtype = FFI::Clang::Lib::CXType.new
277+
invalid_type = FFI::Clang::Types::Type.new(invalid_cxtype, nil)
278+
expect(invalid_type.kind).to eq(:type_invalid)
279+
280+
result = invalid_type.intrinsic_type
281+
expect(result.kind).to eq(:type_invalid)
282+
end
283+
end
284+
201285
describe "#const_qualified?" do
202286
let(:pointer_type) do
203287
find_matching(cursor_cxx) do |child, parent|

0 commit comments

Comments
 (0)