Skip to content

Commit c711003

Browse files
committed
Reject allocating calls in noalloc functions
1 parent a63c964 commit c711003

4 files changed

Lines changed: 71 additions & 8 deletions

File tree

src/analyzer.rs

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -712,14 +712,22 @@ impl Analyzer<'_> {
712712
Expr::Call {
713713
callee, args, span, ..
714714
} => {
715-
if let Callee::Name(name) = callee
716-
&& self.hir.type_kind(name).is_some()
717-
{
718-
self.noalloc_allocation_diagnostic(
719-
function_name,
720-
span,
721-
format!("constructor `{name}` creates a new value."),
722-
);
715+
match self.hir.resolve_call(callee) {
716+
CallResolution::Resolved { signature, kind } => {
717+
if matches!(kind, ResolvedCalleeKind::Constructor { .. }) {
718+
self.noalloc_allocation_diagnostic(
719+
function_name,
720+
span,
721+
format!(
722+
"constructor `{}` creates a new value.",
723+
callee_display(callee)
724+
),
725+
);
726+
} else if !signature.effects.iter().any(|effect| effect == "noalloc") {
727+
self.allocating_call_diagnostic(function_name, callee, span);
728+
}
729+
}
730+
CallResolution::EnumVariant | CallResolution::Unknown => {}
723731
}
724732
for arg in args {
725733
self.check_noalloc_expr(function_name, &arg.value);
@@ -1397,6 +1405,33 @@ impl Analyzer<'_> {
13971405
);
13981406
}
13991407

1408+
fn allocating_call_diagnostic(
1409+
&mut self,
1410+
function_name: &str,
1411+
callee: &Callee,
1412+
span: &crate::diagnostic::Span,
1413+
) {
1414+
self.diagnostics.push(
1415+
Diagnostic::error(
1416+
code::INVALID_NOALLOC_CALL,
1417+
format!(
1418+
"`{function_name}` is declared noalloc but calls possibly allocating function `{}`.",
1419+
callee_display(callee)
1420+
),
1421+
span.clone(),
1422+
"possibly allocating call in noalloc function",
1423+
)
1424+
.with_cause(
1425+
"A `noalloc` function may only call enum variants or functions also declared `effects(noalloc)`.",
1426+
)
1427+
.with_fix(
1428+
"remove_noalloc_or_call_noalloc",
1429+
"Remove `noalloc`, or call only APIs whose signatures are declared `effects(noalloc)`.",
1430+
"manual",
1431+
),
1432+
);
1433+
}
1434+
14001435
fn non_pure_call_diagnostic(
14011436
&mut self,
14021437
function_name: &str,

src/diagnostic.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub mod code {
2020
pub const DUPLICATE_FILE_FEATURE: &str = "RS0017";
2121
pub const INVALID_NO_BLOCK_CALL: &str = "RS0018";
2222
pub const INVALID_NO_PANIC_CALL: &str = "RS0019";
23+
pub const INVALID_NOALLOC_CALL: &str = "RS0020";
2324
pub const FEATURE_VIOLATION: &str = "RS0101";
2425
pub const UNNAMED_ARGUMENT: &str = "RS0201";
2526
pub const MISSING_DATA_EFFECT: &str = "RS0202";
@@ -317,6 +318,11 @@ static DIAGNOSTIC_EXPLANATIONS: &[DiagnosticExplanation] = &[
317318
title: "invalid no_panic call",
318319
explanation: "`effects(no_panic)` is a guarantee that the function does not intentionally panic. Calls inside it must target constructors, enum variants, or functions also declared `effects(no_panic)`.",
319320
},
321+
DiagnosticExplanation {
322+
code: code::INVALID_NOALLOC_CALL,
323+
title: "invalid noalloc call",
324+
explanation: "`effects(noalloc)` is a guarantee that the function performs no heap allocation. Calls inside it must target enum variants or functions also declared `effects(noalloc)`, while direct constructors and `manage` are allocation diagnostics.",
325+
},
320326
DiagnosticExplanation {
321327
code: code::FEATURE_VIOLATION,
322328
title: "feature violation",
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// expect: RS0020
2+
3+
fn may_allocate(value: Int) -> Int {
4+
return value
5+
}
6+
7+
fn promised_noalloc(value: Int) -> Int
8+
effects(noalloc)
9+
{
10+
return may_allocate(value: value)
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
fn cheap_id(value: Int) -> Int
2+
effects(noalloc)
3+
{
4+
return value
5+
}
6+
7+
fn cheap_wrapper(value: Int) -> Int
8+
effects(noalloc)
9+
{
10+
return cheap_id(value: value)
11+
}

0 commit comments

Comments
 (0)