Skip to content
← Back

src/semantic/symbols/import_arguments.ghul

1
namespace Semantic.Symbols is
2
use Type = Semantic.Types.Type
3
4
// Whether an imported function's parameters hand it any way to
5
// reach either code declared in the assembly being built or
6
// storage that existed before the call.
7
//
8
// Imported code can invoke only members it can name, and the names
9
// available to it for an argument are those declared on the
10
// parameter's own type. That type is never a type declared here,
11
// so the only in-assembly code an argument can expose is an
12
// override of a member the parameter's type already declares.
13
//
14
// The parameter types that expose nothing at all are named here
15
// rather than derived, because the two plausible ways to derive
16
// them are both wrong. Being a value type says nothing on its own:
17
// a struct holds whatever its fields and type arguments hold, so
18
// an `(object, object)` tuple hands over two references where an
19
// `(int, int)` hands over none, and `Span[T]` or `ArraySegment[T]`
20
// hands over a reference to storage the caller already owned.
21
// Descending through the fields to find out fares no better,
22
// because a struct whose members have not been materialized
23
// presents as one with no fields, and absence of evidence would
24
// read as evidence of safety in precisely the cases that matter.
25
//
26
// So the test is a positive one. A scalar and a `string` hold no
27
// reference by construction, need no reflection to establish it,
28
// and between them cover what imported constructors actually take.
29
// Everything else declines, which costs a needless crossing and
30
// nothing more.
31
class IMPORT_ARGUMENTS is
32
all_arguments_scalar(function: Function?) -> bool static is
33
if !function? then
34
return false
35
fi
36
37
for argument in function.arguments do
38
if !_is_scalar(argument) then
39
return false
40
fi
41
od
42
43
return true
44
si
45
46
// A `ref` or pointer parameter is not a value type here, which
47
// is what keeps the interpolated-string-handler overloads out.
48
// A type variable is excluded explicitly: `GENERIC` reports
49
// itself a value type unconditionally, and what a parameter
50
// spelled as a bare `T` carries is settled by the
51
// instantiation.
52
_is_scalar(type: Type?) -> bool static is
53
if !type? \/ type.is_type_variable then
54
return false
55
fi
56
57
// An enum carries no reference and cannot be inherited, so
58
// the members an imported callee can name on it are the
59
// fixed BCL comparison and formatting code, and none of it
60
// can reach code declared here.
61
if isa ENUM_STRUCT(type.symbol) then
62
return true
63
fi
64
65
let lookup = IoC.CONTAINER.instance.innate_symbol_lookup
66
67
// string is sealed, so nothing declared here can override
68
// anything the callee could name on it
69
if !type.is_value_type then
70
return type.matches(lookup.get_string_type())
71
fi
72
73
return
74
type.matches(lookup.get_bool_type()) \/
75
type.matches(lookup.get_char_type()) \/
76
type.matches(lookup.get_byte_type()) \/
77
type.matches(lookup.get_ubyte_type()) \/
78
type.matches(lookup.get_short_type()) \/
79
type.matches(lookup.get_ushort_type()) \/
80
type.matches(lookup.get_int_type()) \/
81
type.matches(lookup.get_uint_type()) \/
82
type.matches(lookup.get_long_type()) \/
83
type.matches(lookup.get_ulong_type()) \/
84
type.matches(lookup.get_word_type()) \/
85
type.matches(lookup.get_uword_type()) \/
86
type.matches(lookup.get_single_type()) \/
87
type.matches(lookup.get_double_type()) \/
88
type.matches(lookup.get_decimal_type())
89
si
90
si
91
si