From 7be4020eed69a0bbf3a8d68ae4063919c7c15a0f Mon Sep 17 00:00:00 2001 From: Evan Typanski Date: Thu, 6 Mar 2025 14:36:14 -0500 Subject: [PATCH] Reject function prototypes without `&cxxname`. --- hilti/toolchain/src/compiler/validator.cc | 6 ++- tests/Baseline/hilti.ast.imported-id/output | 52 +++++++++---------- .../output | 3 ++ tests/Baseline/spicy.tools.doc-strings/output | 3 +- tests/hilti/ast/basic-module.hlt | 2 +- tests/hilti/ast/imported-id.hlt | 4 +- .../hilti/hiltic/jit/broken-hilti-linking.hlt | 2 +- .../hilti/validation/function-proto-fail.hlt | 10 ++++ tests/spicy/tools/doc-strings.spicy | 2 +- 9 files changed, 51 insertions(+), 33 deletions(-) create mode 100644 tests/Baseline/hilti.validation.function-proto-fail/output create mode 100644 tests/hilti/validation/function-proto-fail.hlt diff --git a/hilti/toolchain/src/compiler/validator.cc b/hilti/toolchain/src/compiler/validator.cc index 89d0108bd..36d2e1892 100644 --- a/hilti/toolchain/src/compiler/validator.cc +++ b/hilti/toolchain/src/compiler/validator.cc @@ -233,14 +233,18 @@ struct VisitorPost : visitor::PreOrder, public validator::VisitorMixIn { void operator()(Function* n) final { checkNodeAttributes(n->nodeTag(), n->attributes(), "function"); + auto is_hook = n->ftype()->flavor() == type::function::Flavor::Hook; if ( auto attrs = n->attributes() ) { if ( auto prio = attrs->find(hilti::attribute::Kind::Priority) ) { - if ( n->ftype()->flavor() != type::function::Flavor::Hook ) + if ( ! is_hook ) error("only hooks can have priorities", n); else if ( auto x = prio->valueAsInteger(); ! x ) error(x.error(), n); } + + if ( ! n->body() && ! is_hook && ! attrs->has(hilti::attribute::Kind::Cxxname) ) + error(fmt("function '%s' must have a body or be declared with &cxxname", n->id()), n); } } diff --git a/tests/Baseline/hilti.ast.imported-id/output b/tests/Baseline/hilti.ast.imported-id/output index 2ed852b79..6c7856f09 100644 --- a/tests/Baseline/hilti.ast.imported-id/output +++ b/tests/Baseline/hilti.ast.imported-id/output @@ -20,7 +20,7 @@ [debug/compiler] [HILTI] building scopes [debug/compiler] [HILTI] resolving AST [debug/resolver] -> [T11] type::Bool | bool (foo.hlt:6:20-6:23) -[debug/resolver] [foo.hlt:9:20-9:23] type::Name "Foo1" -> set resolved type to T11 +[debug/resolver] [foo.hlt:9:21-9:24] type::Name "Foo1" -> set resolved type to T11 [debug/resolver] -> [T12] type::Vector | vector (hilti.hlt:16:24-16:36) [debug/resolver] [hilti.hlt:20:12-20:19] type::Name "Captures" -> set resolved type to T12 [debug/resolver] -> [T13] type::Library | __library_type("::hilti::rt::Profiler") (hilti.hlt:17:24-17:60) @@ -47,16 +47,16 @@ [debug/resolver] [foo.hlt:6:20-6:23] type::Bool "bool" -> set type's declaration to D1 [debug/resolver] [foo.hlt:7:1-7:29] declaration::Type "public type Foo2 = Bar::Bar1;" -> set declaration's canonical ID to Foo::Foo2 [debug/resolver] [foo.hlt:7:1-7:29] declaration::Type "public type Foo2 = Bar::Bar1;" -> set declaration's fully qualified ID to Foo::Foo2 -[debug/resolver] [foo.hlt:9:20-9:27] declaration::Parameter "bool foo" -> set declaration's canonical ID to Foo::foo -[debug/resolver] [foo.hlt:9:20-9:27] declaration::Parameter "bool foo" -> set declaration's fully qualified ID to foo -[debug/resolver] [foo.hlt:9:30-9:42] declaration::Parameter "Bar::Bar1 bar" -> set declaration's canonical ID to Foo::bar -[debug/resolver] [foo.hlt:9:30-9:42] declaration::Parameter "Bar::Bar1 bar" -> set declaration's fully qualified ID to bar -[debug/resolver] [foo.hlt:9:1-9:44] declaration::Function "declare function string foo(bool foo, Bar::Bar1 bar);" -> set declaration's canonical ID to Foo::foo_2 -[debug/resolver] [foo.hlt:9:1-9:44] declaration::Function "declare function string foo(bool foo, Bar::Bar1 bar);" -> set declaration's fully qualified ID to Foo::foo -[debug/resolver] [foo.hlt:2:1-11:1] declaration::Module "module Foo { import Bar; public type Foo1 = bool; public type Foo2 = Bar::Bar1; declare function string foo(bool foo, Bar::Bar1 bar); }" -> set declaration's fully qualified ID to Foo -[debug/resolver] [foo.hlt:2:1-11:1] declaration::Module "module Foo { import Bar; public type Foo1 = bool; public type Foo2 = Bar::Bar1; declare function string foo(bool foo, Bar::Bar1 bar); }" -> set module's canonical ID to Foo -[debug/resolver] -> [D2] declaration::Module Foo | module Foo { import Bar; public type Foo1 = bool; public type Foo2 = Bar::Bar1; declare function string foo(bool foo, Bar::Bar1 bar); } (foo.hlt:2:1-11:1) -[debug/resolver] [foo.hlt:2:1-11:1] declaration::Module "module Foo { import Bar; public type Foo1 = bool; public type Foo2 = Bar::Bar1; declare function string foo(bool foo, Bar::Bar1 bar); }" -> set module's declaration index to D2 +[debug/resolver] [foo.hlt:9:21-9:28] declaration::Parameter "bool foo" -> set declaration's canonical ID to Foo::foo +[debug/resolver] [foo.hlt:9:21-9:28] declaration::Parameter "bool foo" -> set declaration's fully qualified ID to foo +[debug/resolver] [foo.hlt:9:31-9:43] declaration::Parameter "Bar::Bar1 bar" -> set declaration's canonical ID to Foo::bar +[debug/resolver] [foo.hlt:9:31-9:43] declaration::Parameter "Bar::Bar1 bar" -> set declaration's fully qualified ID to bar +[debug/resolver] [foo.hlt:9:1-9:47] declaration::Function "function string foo(bool foo, Bar::Bar1 bar) { }" -> set declaration's canonical ID to Foo::foo_2 +[debug/resolver] [foo.hlt:9:1-9:47] declaration::Function "function string foo(bool foo, Bar::Bar1 bar) { }" -> set declaration's fully qualified ID to Foo::foo +[debug/resolver] [foo.hlt:2:1-11:1] declaration::Module "module Foo { import Bar; public type Foo1 = bool; public type Foo2 = Bar::Bar1; function string foo(bool foo, Bar::Bar1 bar) { } }" -> set declaration's fully qualified ID to Foo +[debug/resolver] [foo.hlt:2:1-11:1] declaration::Module "module Foo { import Bar; public type Foo1 = bool; public type Foo2 = Bar::Bar1; function string foo(bool foo, Bar::Bar1 bar) { } }" -> set module's canonical ID to Foo +[debug/resolver] -> [D2] declaration::Module Foo | module Foo { import Bar; public type Foo1 = bool; public type Foo2 = Bar::Bar1; function string foo(bool foo, Bar::Bar1 bar) { } } (foo.hlt:2:1-11:1) +[debug/resolver] [foo.hlt:2:1-11:1] declaration::Module "module Foo { import Bar; public type Foo1 = bool; public type Foo2 = Bar::Bar1; function string foo(bool foo, Bar::Bar1 bar) { } }" -> set module's declaration index to D2 [debug/resolver] [hilti.hlt:6:1-6:21] declaration::Property "" -> set declaration's canonical ID to hilti::%skip-implementation [debug/resolver] [hilti.hlt:6:1-6:21] declaration::Property "" -> set declaration's fully qualified ID to hilti::%skip-implementation [debug/resolver] [hilti.hlt:8:24-8:42] declaration::Constant "const enum { LSB0 = 0, MSB0 = 1 } LSB0 = ::LSB0;" -> set declaration's canonical ID to hilti::LSB0 @@ -304,16 +304,16 @@ [debug/resolver] [bar.hlt:6:20-6:25] type::String "string" -> set type's declaration to D21 [debug/resolver] [bar.hlt:7:1-7:29] declaration::Type "public type Bar2 = Foo::Foo1;" -> set declaration's canonical ID to Bar::Bar2 [debug/resolver] [bar.hlt:7:1-7:29] declaration::Type "public type Bar2 = Foo::Foo1;" -> set declaration's fully qualified ID to Bar::Bar2 -[debug/resolver] [bar.hlt:9:20-9:27] declaration::Parameter "Bar1 bar" -> set declaration's canonical ID to Bar::bar -[debug/resolver] [bar.hlt:9:20-9:27] declaration::Parameter "Bar1 bar" -> set declaration's fully qualified ID to bar -[debug/resolver] [bar.hlt:9:30-9:42] declaration::Parameter "Foo::Foo1 foo" -> set declaration's canonical ID to Bar::foo -[debug/resolver] [bar.hlt:9:30-9:42] declaration::Parameter "Foo::Foo1 foo" -> set declaration's fully qualified ID to foo -[debug/resolver] [bar.hlt:9:1-9:44] declaration::Function "declare function string bar(Bar1 bar, Foo::Foo1 foo);" -> set declaration's canonical ID to Bar::bar_2 -[debug/resolver] [bar.hlt:9:1-9:44] declaration::Function "declare function string bar(Bar1 bar, Foo::Foo1 foo);" -> set declaration's fully qualified ID to Bar::bar -[debug/resolver] [bar.hlt:2:1-11:1] declaration::Module "module Bar { import Foo; public type Bar1 = string; public type Bar2 = Foo::Foo1; declare function string bar(Bar1 bar, Foo::Foo1 foo); }" -> set declaration's fully qualified ID to Bar -[debug/resolver] [bar.hlt:2:1-11:1] declaration::Module "module Bar { import Foo; public type Bar1 = string; public type Bar2 = Foo::Foo1; declare function string bar(Bar1 bar, Foo::Foo1 foo); }" -> set module's canonical ID to Bar -[debug/resolver] -> [D22] declaration::Module Bar | module Bar { import Foo; public type Bar1 = string; public type Bar2 = Foo::Foo1; declare function string bar(Bar1 bar, Foo::Foo1 foo); } (bar.hlt:2:1-11:1) -[debug/resolver] [bar.hlt:2:1-11:1] declaration::Module "module Bar { import Foo; public type Bar1 = string; public type Bar2 = Foo::Foo1; declare function string bar(Bar1 bar, Foo::Foo1 foo); }" -> set module's declaration index to D22 +[debug/resolver] [bar.hlt:9:21-9:28] declaration::Parameter "Bar1 bar" -> set declaration's canonical ID to Bar::bar +[debug/resolver] [bar.hlt:9:21-9:28] declaration::Parameter "Bar1 bar" -> set declaration's fully qualified ID to bar +[debug/resolver] [bar.hlt:9:31-9:43] declaration::Parameter "Foo::Foo1 foo" -> set declaration's canonical ID to Bar::foo +[debug/resolver] [bar.hlt:9:31-9:43] declaration::Parameter "Foo::Foo1 foo" -> set declaration's fully qualified ID to foo +[debug/resolver] [bar.hlt:9:1-9:47] declaration::Function "function string bar(Bar1 bar, Foo::Foo1 foo) { }" -> set declaration's canonical ID to Bar::bar_2 +[debug/resolver] [bar.hlt:9:1-9:47] declaration::Function "function string bar(Bar1 bar, Foo::Foo1 foo) { }" -> set declaration's fully qualified ID to Bar::bar +[debug/resolver] [bar.hlt:2:1-11:1] declaration::Module "module Bar { import Foo; public type Bar1 = string; public type Bar2 = Foo::Foo1; function string bar(Bar1 bar, Foo::Foo1 foo) { } }" -> set declaration's fully qualified ID to Bar +[debug/resolver] [bar.hlt:2:1-11:1] declaration::Module "module Bar { import Foo; public type Bar1 = string; public type Bar2 = Foo::Foo1; function string bar(Bar1 bar, Foo::Foo1 foo) { } }" -> set module's canonical ID to Bar +[debug/resolver] -> [D22] declaration::Module Bar | module Bar { import Foo; public type Bar1 = string; public type Bar2 = Foo::Foo1; function string bar(Bar1 bar, Foo::Foo1 foo) { } } (bar.hlt:2:1-11:1) +[debug/resolver] [bar.hlt:2:1-11:1] declaration::Module "module Bar { import Foo; public type Bar1 = string; public type Bar2 = Foo::Foo1; function string bar(Bar1 bar, Foo::Foo1 foo) { } }" -> set module's declaration index to D22 [debug/compiler] -> modified [debug/compiler] processing ASTs, round 2 [debug/compiler] [HILTI] building scopes @@ -322,11 +322,11 @@ [debug/compiler] [HILTI] resolving AST [debug/resolver] -> [T17] type::String Bar::Bar1 | string (bar.hlt:6:20-6:25) [debug/resolver] [foo.hlt:7:20-7:28] type::Name "Bar::Bar1" -> set resolved type to T17 -[debug/resolver] [foo.hlt:9:30-9:38] type::Name "Bar::Bar1" -> set resolved type to T17 +[debug/resolver] [foo.hlt:9:31-9:39] type::Name "Bar::Bar1" -> set resolved type to T17 [debug/resolver] [bar.hlt:7:20-7:28] type::Name "Foo::Foo1" -> set resolved type to T11 -[debug/resolver] [bar.hlt:9:20-9:23] type::Name "Bar1" -> set resolved type to T17 -[debug/resolver] [bar.hlt:9:30-9:38] type::Name "Foo::Foo1" -> set resolved type to T11 -[debug/resolver] [foo.hlt:9:1-9:44] declaration::Function "declare function string foo(bool foo, string bar);" -> creating function call operator +[debug/resolver] [bar.hlt:9:21-9:24] type::Name "Bar1" -> set resolved type to T17 +[debug/resolver] [bar.hlt:9:31-9:39] type::Name "Foo::Foo1" -> set resolved type to T11 +[debug/resolver] [foo.hlt:9:1-9:47] declaration::Function "function string foo(bool foo, string bar) { }" -> creating function call operator [debug/resolver] [hilti.hlt:8:24-8:42] declaration::Constant "const hilti::BitOrder LSB0 = hilti::BitOrder::LSB0;" -> set declaration's fully qualified ID to hilti::BitOrder::LSB0 [debug/resolver] [hilti.hlt:8:24-8:42] declaration::Constant "const hilti::BitOrder MSB0 = hilti::BitOrder::MSB0;" -> set declaration's fully qualified ID to hilti::BitOrder::MSB0 [debug/resolver] [hilti.hlt:8:24-8:42] declaration::Constant "const hilti::BitOrder Undef = hilti::BitOrder::Undef;" -> set declaration's fully qualified ID to hilti::BitOrder::Undef @@ -364,7 +364,7 @@ [debug/resolver] [hilti.hlt:25:5-25:29] declaration::Field "uint<64> num_data_chunks;" -> set declaration's fully qualified ID to hilti::StreamStatistics::num_data_chunks [debug/resolver] [hilti.hlt:26:5-26:27] declaration::Field "uint<64> num_gap_bytes;" -> set declaration's fully qualified ID to hilti::StreamStatistics::num_gap_bytes [debug/resolver] [hilti.hlt:27:5-27:28] declaration::Field "uint<64> num_gap_chunks;" -> set declaration's fully qualified ID to hilti::StreamStatistics::num_gap_chunks -[debug/resolver] [bar.hlt:9:1-9:44] declaration::Function "declare function string bar(string bar, bool foo);" -> creating function call operator +[debug/resolver] [bar.hlt:9:1-9:47] declaration::Function "function string bar(string bar, bool foo) { }" -> creating function call operator [debug/compiler] -> modified [debug/compiler] processing ASTs, round 3 [debug/compiler] [HILTI] building scopes diff --git a/tests/Baseline/hilti.validation.function-proto-fail/output b/tests/Baseline/hilti.validation.function-proto-fail/output new file mode 100644 index 000000000..9a100fbb1 --- /dev/null +++ b/tests/Baseline/hilti.validation.function-proto-fail/output @@ -0,0 +1,3 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +[error] <...>/function-proto-fail.hlt:8:9-8:19: function 'fail' must have a body or be declared with &cxxname +[error] hiltic: aborting after errors diff --git a/tests/Baseline/spicy.tools.doc-strings/output b/tests/Baseline/spicy.tools.doc-strings/output index cc1e54f84..a71573ddf 100644 --- a/tests/Baseline/spicy.tools.doc-strings/output +++ b/tests/Baseline/spicy.tools.doc-strings/output @@ -36,7 +36,8 @@ global int<32> g2 = int32(2); global int<32> g3; ## Function 1. -declare function void foo1(); +function void foo1() { +} ## Function 2. function void foo2() { diff --git a/tests/hilti/ast/basic-module.hlt b/tests/hilti/ast/basic-module.hlt index 48aded267..10c4f2bd6 100644 --- a/tests/hilti/ast/basic-module.hlt +++ b/tests/hilti/ast/basic-module.hlt @@ -7,6 +7,6 @@ module Foo { type X = bool; -declare string foo(real bar); +function string foo(real bar) {} } diff --git a/tests/hilti/ast/imported-id.hlt b/tests/hilti/ast/imported-id.hlt index a2f0004de..79a8634b3 100644 --- a/tests/hilti/ast/imported-id.hlt +++ b/tests/hilti/ast/imported-id.hlt @@ -11,7 +11,7 @@ import Bar; public type Foo1 = bool; public type Foo2 = Bar::Bar1; -declare string foo(Foo1 foo, Bar::Bar1 bar); +function string foo(Foo1 foo, Bar::Bar1 bar) {} } @@ -27,7 +27,7 @@ import Foo; public type Bar1 = string; public type Bar2 = Foo::Foo1; -declare string bar(Bar1 bar, Foo::Foo1 foo); +function string bar(Bar1 bar, Foo::Foo1 foo) {} } diff --git a/tests/hilti/hiltic/jit/broken-hilti-linking.hlt b/tests/hilti/hiltic/jit/broken-hilti-linking.hlt index 4977f4e16..d1ee25e14 100644 --- a/tests/hilti/hiltic/jit/broken-hilti-linking.hlt +++ b/tests/hilti/hiltic/jit/broken-hilti-linking.hlt @@ -6,7 +6,7 @@ # compiler, we just check for a keyword being in there. module Test { -declare public time does_not_exist(); +declare public time does_not_exist() &cxxname="does_not_exist"; does_not_exist(); diff --git a/tests/hilti/validation/function-proto-fail.hlt b/tests/hilti/validation/function-proto-fail.hlt new file mode 100644 index 000000000..d335e94af --- /dev/null +++ b/tests/hilti/validation/function-proto-fail.hlt @@ -0,0 +1,10 @@ +# @TEST-EXEC-FAIL: ${HILTIC} -j %INPUT > output 2>&1 +# @TEST-EXEC: btest-diff output +# +# @TEST-DOC: Tests that function prototypes must have &cxxname + +module Foo { + +declare void fail(); + +} diff --git a/tests/spicy/tools/doc-strings.spicy b/tests/spicy/tools/doc-strings.spicy index 23d207e3c..88918de15 100644 --- a/tests/spicy/tools/doc-strings.spicy +++ b/tests/spicy/tools/doc-strings.spicy @@ -36,7 +36,7 @@ const c1 = 1; const c2: int32 = 2; ## Function 1. -function foo1(); +function foo1() {} ## Function 2. function foo2() {}