Skip to content
← Back

src/syntax/process/pure_slot_check.ghul

1
namespace Syntax.Process is
2
use Logging;
3
4
use Source.LOCATION;
5
6
// Enforces pure function-typed slots: a value reaching one must be
7
// a store-free literal, a store-free or declared-pure named
8
// function, or a value whose own static type is already pure.
9
//
10
// The test is "cannot prove store-free", not "is known to store",
11
// so it is conservative and reports a warning. An unsatisfied
12
// argument also drops heap facts at the call; a store does not,
13
// so for a store the warning is the only signal.
14
class PURE_SLOT_CHECK is
15
_logger: Logger;
16
_flow: NARROWING_FLOW;
17
_build_flags: Compiler.GLOBAL_BUILD_FLAGS;
18
19
init(
20
logger: Logger,
21
flow: NARROWING_FLOW,
22
build_flags: Compiler.GLOBAL_BUILD_FLAGS
23
) is
24
super.init();
25
26
_logger = logger;
27
_flow = flow;
28
_build_flags = build_flags;
29
si
30
31
// Every argument of a fully-built call that lands in a pure
32
// formal. Applies to any call-shaped value: an ordinary call,
33
// a user-defined operator or indexer, a construction.
34
check_call(location: LOCATION, value: IR.Values.Value?) is
35
let function: Semantic.Symbols.Function? mut = null;
36
let arguments: Collections.List[IR.Values.Value]? mut = null;
37
38
if isa IR.Values.Call.INSTANCE(value) then
39
let call = cast IR.Values.Call.INSTANCE(value);
40
function = call.function;
41
arguments = call.arguments;
42
elif isa IR.Values.Call.GLOBAL(value) then
43
let call = cast IR.Values.Call.GLOBAL(value);
44
function = call.function;
45
arguments = call.arguments;
46
elif isa IR.Values.Call.STATIC(value) then
47
let call = cast IR.Values.Call.STATIC(value);
48
function = call.function;
49
arguments = call.arguments;
50
elif isa IR.Values.Call.STRUCT(value) then
51
let call = cast IR.Values.Call.STRUCT(value);
52
function = call.function;
53
arguments = call.arguments;
54
else
55
return;
56
fi
57
58
let formals = function.arguments;
59
60
for i in 0..formals.count do
61
if i >= arguments.count then
62
return;
63
fi
64
65
if formals[i].is_pure_function /\ !is_satisfied(arguments[i]) then
66
if !_build_flags.no_warn_impure_function_value then
67
_logger.warn(location, "impure-function-value", "argument must be a pure function");
68
fi
69
70
_flow.on_call(location);
71
fi
72
od
73
si
74
75
// A value stored into a slot whose type is a pure function
76
// type. The slot's type is a claim that it holds store-free
77
// values, and is trusted wherever the slot is read, so the
78
// store is where the value has to be shown store-free.
79
check_store(location: LOCATION, target_type: Semantic.Types.Type?, value: IR.Values.Value?) is
80
if
81
target_type? /\ target_type.is_pure_function /\
82
!is_satisfied(value) /\
83
!_build_flags.no_warn_impure_function_value
84
then
85
_logger.warn(location, "impure-function-value", "value must be a pure function");
86
fi
87
si
88
89
is_satisfied(argument: IR.Values.Value?) -> bool is
90
if !argument? then
91
return true;
92
fi
93
94
// A reference is typed with the type of the slot it is
95
// read into, so where it names a known function that
96
// function's own store-free bit decides.
97
if let function = argument.referenced_function then
98
return _is_store_free(function);
99
fi
100
101
if argument.has_symbol then
102
let symbol = argument.symbol;
103
104
if isa Semantic.Symbols.Function(symbol) then
105
return _is_store_free(cast Semantic.Symbols.Function(symbol));
106
elif isa Semantic.Symbols.Variable(symbol) /\ symbol.type? /\ symbol.type.is_pure_function then
107
return true;
108
fi
109
fi
110
111
return argument.type? /\ argument.type!.is_pure_function;
112
si
113
114
// A literal is answered by what its body did, since
115
// infer-store-free only covers named functions; a named
116
// function by its store-free bit, which a `pure` declaration
117
// sets without the body being provable.
118
_is_store_free(function: Semantic.Symbols.Function) -> bool =>
119
if isa Semantic.Symbols.Closure(function) then
120
function.is_store_free \/ !function.literal_body_impure
121
else
122
function.is_store_free
123
fi;
124
si
125
si