Skip to content

Commit

Permalink
Add ignoreUnusedReturnValue attribute for functions (#720)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer authored Jan 19, 2025
1 parent fa0d091 commit d9bb728
Show file tree
Hide file tree
Showing 16 changed files with 100 additions and 192 deletions.
2 changes: 1 addition & 1 deletion .run/spice run.run.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="spice run" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -O0 -d -ir ../../media/test-project/test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<configuration default="false" name="spice run" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -O2 -d ../../media/test-project/test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<envs>
<env name="LLVM_ADDITIONAL_FLAGS" value="-lole32 -lws2_32" />
<env name="LLVM_BUILD_INCLUDE_DIR" value="$PROJECT_DIR$/../llvm-project-latest/build/include" />
Expand Down
192 changes: 32 additions & 160 deletions media/test-project/test.spice
Original file line number Diff line number Diff line change
@@ -1,165 +1,37 @@
import "std/test/lifetime-object";
import "std/text/stringstream";

/*f<LifetimeObject> spawnLO() {
return LifetimeObject();
}

f<LifetimeObject> decideOnLOConstRef(bool cond, const LifetimeObject& lo1, const LifetimeObject& lo2) {
cond ? lo1 : lo2; // Should not do a copy
LifetimeObject loCopy = cond ? lo1 : lo2; // Shoud do a copy
const LifetimeObject& loRef = cond ? lo1 : lo2; // Should not do a copy
return cond ? lo1 : lo2; // Return statement should do a copy
f<int> main() {
// Basic usage with raw string literals
StringStream ss1;
ss1 << "Hello" << " World!";
assert ss1.str() == "Hello World!";

// Basic usage with String values
StringStream ss2 = StringStream(String("This "));
ss2 << String("is") << String(" a ") << String("test!") << endl();
assert ss2.str() == "This is a test!\n";

// Mixed usage
StringStream ss3 = StringStream("Hello");
ss3 << String(" fellow") << " Programmers" << endl();
assert ss3.str() == "Hello fellow Programmers\n";

// Usage spread over multiple lines
StringStream ss4 = StringStream();
ss4 << "This ";
ss4 << "is ";
ss4 << "a ";
ss4 << "multiline ";
ss4 << "test";
ss4 << endl();
assert ss4.str() == "This is a multiline test\n";

printf("All assertions passed!");
}

f<LifetimeObject> decideOnLORef(bool cond, LifetimeObject& lo1, LifetimeObject& lo2) {
cond ? lo1 : lo2; // Should not do a copy
LifetimeObject loCopy = cond ? lo1 : lo2; // Shoud do a copy
const LifetimeObject& loRef = cond ? lo1 : lo2; // Should not do a copy
return cond ? lo1 : lo2; // Return statement should do a copy
}*/

f<LifetimeObject> decideOnLOVal(bool cond, LifetimeObject lo1, LifetimeObject lo2) {
cond ? lo1 : lo2; // Shoud do a copy, although the result is ignored
LifetimeObject loCopy = cond ? lo1 : lo2; // Shoud do a copy
const LifetimeObject& loRef = cond ? lo1 : lo2; // Should not do a copy
return cond ? lo1 : lo2; // Return statement should do a copy
}
/*import "std/data/map";

f<int> main() {
// Ignored return value of ctor
/*printf("Ignored return value of ctor:\n");
{
LifetimeObject();
}

// Normal lifecycle
printf("Normal lifecycle:\n");
{
LifetimeObject lo = LifetimeObject(); // ctor call
LifetimeObject loCopy = lo; // copy ctor call
}

// Return from lambda as value
printf("Return from lambda as value:\n");
{
const f<LifetimeObject>() spawnLO = f<LifetimeObject>() {
return LifetimeObject();
};

// No return value receiver
spawnLO(); // Anonymous symbol
// Return value receiver - value
LifetimeObject lo = spawnLO(); // Assigned to lo
// Return value receiver - const ref
const LifetimeObject& loConstRef = spawnLO(); // Assigned to loConstRef
}

// Return from lambda as reference
printf("Return from lambda as reference:\n");
{
LifetimeObject loOrig = LifetimeObject();
const f<LifetimeObject&>() spawnLO = f<LifetimeObject&>() {
return loOrig;
};

// No return value receiver
spawnLO(); // Anonymous symbol
// Return value receiver - value
LifetimeObject lo = spawnLO(); // Assigned to lo
// Return value receiver - const ref
const LifetimeObject& loConstRef = spawnLO(); // Assigned to loConstRef
}

// Return from lambda as const reference
printf("Return from lambda as const reference:\n");
{
LifetimeObject loOrig = LifetimeObject();
const f<const LifetimeObject&>() spawnLO = f<const LifetimeObject&>() {
return loOrig;
};

// No return value receiver
spawnLO(); // Anonymous symbol
// Return value receiver - value
LifetimeObject lo = spawnLO(); // Assigned to lo
// Return value receiver - const ref
const LifetimeObject& loConstRef = spawnLO(); // Assigned to loConstRef
}

// Return from function as value
printf("Return from function as value:\n");
{
// No return value receiver
spawnLO(); // Anonymous symbol
// Return value receiver - value
LifetimeObject lo = spawnLO(); // Assigned to lo
// Return value receiver - const ref
const LifetimeObject& loConstRef = spawnLO(); // Assigned to loConstRef
}

printf("Ternary (true temporary, false temporary)\n");
{
bool cond = true;
LifetimeObject loCopy1 = cond ? LifetimeObject() : LifetimeObject(); // ctor calls in both branches
LifetimeObject loCopy2 = !cond ? LifetimeObject() : LifetimeObject(); // ctor calls in both branches
}

printf("Ternary (true temporary, false not temporary)\n");
{
bool cond = true;
LifetimeObject lo = LifetimeObject();
LifetimeObject loCopy1 = cond ? LifetimeObject() : lo; // ctor in true branch, copy ctor in false branch
LifetimeObject loCopy2 = !cond ? LifetimeObject() : lo; // ctor in true branch, copy ctor in false branch
}

printf("Ternary (true not temporary, false temporary)\n");
{
bool cond = true;
LifetimeObject lo = LifetimeObject();
LifetimeObject loCopy1 = cond ? lo : LifetimeObject(); // copy ctor in true branch, ctor in false branch
LifetimeObject loCopy2 = !cond ? lo : LifetimeObject(); // copy ctor in true branch, ctor in false branch
}

printf("Ternary (true not temporary, false not temporary)\n");
{
bool cond = true;
LifetimeObject lo1 = LifetimeObject();
LifetimeObject lo2 = LifetimeObject();
LifetimeObject loCopy1 = cond ? lo1 : lo2; // copy ctor call in both branches
LifetimeObject loCopy2 = !cond ? lo1 : lo2; // copy ctor call in both branches
}

printf("Ternary function (const ref) return:\n");
{
bool cond = true;
LifetimeObject lo1 = LifetimeObject();
LifetimeObject lo2 = LifetimeObject();
LifetimeObject lo3 = decideOnLOConstRef(cond, lo1, lo2);
LifetimeObject lo4 = decideOnLOConstRef(!cond, lo1, lo2);
}

printf("Ternary function (ref) return:\n");
{
bool cond = true;
LifetimeObject lo1 = LifetimeObject();
LifetimeObject lo2 = LifetimeObject();
LifetimeObject lo3 = decideOnLORef(cond, lo1, lo2);
LifetimeObject lo4 = decideOnLORef(!cond, lo1, lo2);
}*/

printf("Ternary function (val) return:\n");
{
bool cond = true;
LifetimeObject lo1 = LifetimeObject();
LifetimeObject lo2 = LifetimeObject();
LifetimeObject lo3 = decideOnLOVal(cond, lo1, lo2);
LifetimeObject lo4 = decideOnLOVal(!cond, lo1, lo2);
}

/*printf("Ternary receiving function result:\n");
{
bool cond = false;
LifetimeObject lo1 = cond ? spawnLO() : spawnLO();
LifetimeObject lo2 = !cond ? (cond ? spawnLO() : spawnLO()) : spawnLO();
}*/
}
Map<string, int> map;
map["test"] = 1;
}*/
3 changes: 2 additions & 1 deletion src-bootstrap/ast/attributes.spice
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import "bootstrap/ast/ast-nodes";
public const string ATTR_CORE_LINKER_FLAG = "core.linker.flag";
public const string ATTR_CORE_LINUX_LINKER_FLAG = "core.linux.linker.flag";
public const string ATTR_CORE_WINDOWS_LINKER_FLAG = "core.windows.linker.flag";
public const string ATTR_CORE_LINKER_ADDITIONAL_SOURCE = "core.linker.additional_source";
public const string ATTR_CORE_LINKER_ADDITIONAL_SOURCE = "core.linker.additionalSource";
public const string ATTR_CORE_LINKER_DLL = "core.linker.dll";
public const string ATTR_CORE_COMPILER_MANGLE = "core.compiler.mangle";
public const string ATTR_CORE_COMPILER_MANGLED_NAME = "core.compiler.mangledName";
Expand All @@ -21,6 +21,7 @@ public const string ATTR_TEST = "test";
public const string ATTR_TEST_NAME = "test.name";
public const string ATTR_TEST_SKIP = "test.skip";
public const string ATTR_ASYNC = "async";
public const string ATTR_IGNORE_UNUSED_RETURN_VALUE = "ignoreUnusedReturnValue";

// Structs
type AttrConfigValue struct {
Expand Down
2 changes: 1 addition & 1 deletion src-bootstrap/bindings/llvm/linker-flags.spice
Original file line number Diff line number Diff line change
Expand Up @@ -214,5 +214,5 @@
core.linker.flag = "-luuid",
core.linker.flag = "-pthread",
// Wrapper around target macro definitions
core.linker.additional_source = "target-wrapper.c"
core.linker.additionalSource = "target-wrapper.c"
]
10 changes: 9 additions & 1 deletion src/ast/Attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace spice::compiler {
static constexpr const char *const ATTR_CORE_LINKER_FLAG = "core.linker.flag";
static constexpr const char *const ATTR_CORE_LINUX_LINKER_FLAG = "core.linux.linker.flag";
static constexpr const char *const ATTR_CORE_WINDOWS_LINKER_FLAG = "core.windows.linker.flag";
static constexpr const char *const ATTR_CORE_LINKER_ADDITIONAL_SOURCE = "core.linker.additional_source";
static constexpr const char *const ATTR_CORE_LINKER_ADDITIONAL_SOURCE = "core.linker.additionalSource";
static constexpr const char *const ATTR_CORE_LINKER_DLL = "core.linker.dll";
static constexpr const char *const ATTR_CORE_COMPILER_MANGLE = "core.compiler.mangle";
static constexpr const char *const ATTR_CORE_COMPILER_MANGLED_NAME = "core.compiler.mangledName";
Expand All @@ -26,6 +26,7 @@ static constexpr const char *const ATTR_TEST = "test";
static constexpr const char *const ATTR_TEST_NAME = "test.name";
static constexpr const char *const ATTR_TEST_SKIP = "test.skip";
static constexpr const char *const ATTR_ASYNC = "async";
static constexpr const char *const ATTR_IGNORE_UNUSED_RETURN_VALUE = "ignoreUnusedReturnValue";

static constexpr CompileTimeValue DEFAULT_BOOL_COMPILE_VALUE{.boolValue = true};

Expand Down Expand Up @@ -156,6 +157,13 @@ static const std::unordered_map<std::string, AttrConfigValue> ATTR_CONFIGS = {
.type = AttrNode::AttrType::TYPE_BOOL,
},
},
{
ATTR_IGNORE_UNUSED_RETURN_VALUE,
{
.target = AttrNode::AttrTarget::TARGET_FCT_PROC | AttrNode::AttrTarget::TARGET_LAMBDA,
.type = AttrNode::AttrType::TYPE_BOOL,
},
},
};

} // namespace spice::compiler
4 changes: 2 additions & 2 deletions src/symboltablebuilder/SymbolTableBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -645,11 +645,11 @@ std::any SymbolTableBuilder::visitModAttr(ModAttrNode *node) {
for (const CompileTimeValue *value : linkerFlagValues)
resourceManager.linker.addLinkerFlag(resourceManager.compileTimeStringValues.at(value->stringValueOffset));

// core.linker.additional_source
// core.linker.additionalSource
for (const CompileTimeValue *value : attrs->getAttrValuesByName(ATTR_CORE_LINKER_ADDITIONAL_SOURCE)) {
const std::string &stringValue = resourceManager.compileTimeStringValues.at(value->stringValueOffset);
const std::filesystem::path path = sourceFile->filePath.parent_path() / stringValue;
resourceManager.linker.addAdditionalSourcePath(std::filesystem::canonical(path));
resourceManager.linker.addAdditionalSourcePath(canonical(path));
}

return nullptr;
Expand Down
5 changes: 3 additions & 2 deletions src/symboltablebuilder/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ const Type *Type::toRef(const ASTNode *node) const {
*
* @param node AST node for error messages
* @param size Size of the array
* @param skipDynCheck Skip check if array base type is dyn
* @return Array type of the current type
*/
const Type *Type::toArr(const ASTNode *node, unsigned int size, bool skipDynCheck /*=false*/) const {
Expand Down Expand Up @@ -468,6 +469,7 @@ bool Type::isOneOf(const std::initializer_list<SuperType> &superTypes) const {
/**
* Get the name of the symbol type as a string
*
* @param name Get name of type
* @param withSize Include the array size for sized types
* @return Symbol type name
*/
Expand All @@ -481,7 +483,6 @@ void Type::getName(std::stringstream &name, bool withSize) const { // NOLINT(mis
* Get the name of the symbol type as a string
*
* @param withSize Include the array size for sized types
* @param ignorePublic Ignore any potential public specifier
* @return Symbol type name
*/
std::string Type::getName(bool withSize) const {
Expand All @@ -491,7 +492,7 @@ std::string Type::getName(bool withSize) const {
}

/**
* Get the return type of a function type
* Get the return type of function type
*
* @return Function return type
*/
Expand Down
16 changes: 13 additions & 3 deletions src/typechecker/TypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1880,9 +1880,19 @@ std::any TypeChecker::visitFctCall(FctCallNode *node) {
// Remove public specifier to not have public local variables
returnType.getSpecifiers().isPublic = false;

// Check if the return value gets used
if (isFct && !node->hasReturnValueReceiver())
warnings.emplace_back(node->codeLoc, UNUSED_RETURN_VALUE, "The return value of the function call is unused");
// Check if the return value gets discarded
if (isFct && !node->hasReturnValueReceiver()) {
// Check if we want to ignore the discarded return value
bool ignoreUnusedReturnValue = false;
if (!data.isFctPtrCall()) {
assert(data.callee != nullptr);
auto fctDef = dynamic_cast<const FctDefNode *>(data.callee->declNode);
ignoreUnusedReturnValue = fctDef && fctDef->attrs && fctDef->attrs->attrLst->hasAttr(ATTR_IGNORE_UNUSED_RETURN_VALUE);
}

if (!ignoreUnusedReturnValue)
warnings.emplace_back(node->codeLoc, UNUSED_RETURN_VALUE, "The return value of the function call is unused");
}

return ExprResult{node->setEvaluatedSymbolType(returnType, manIdx), anonymousSymbol};
}
Expand Down
4 changes: 2 additions & 2 deletions std/data/binary-tree.spice
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ f<bool> BinaryTree.containsNode<T>(Node<T>* currentNode, const T& value) {
}

/**
* Recursion helper to find the smallest node in the binary tree
* Recursion helper to delete a node with a certain value
*
* @param currentNode The current node to check
* @return The smallest node
* @return The new root node
*/
f<heap Node<T>*> BinaryTree.deleteNode<T>(heap Node<T>* currentNode, const T& value) {
// We reached the end of the tree
Expand Down
2 changes: 2 additions & 0 deletions std/data/deque.spice
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ public p Deque.pushFront(const T& item) {
*
* @return First item
*/
#[ignoreUnusedReturnValue]
public f<T&> Deque.popFront() {
if this.isEmpty() { panic(Error("Deque is empty")); }

Expand All @@ -128,6 +129,7 @@ public f<T&> Deque.popFront() {
*
* @return Last item
*/
#[ignoreUnusedReturnValue]
public f<T&> Deque.popBack() {
if this.isEmpty() { panic(Error("Deque is empty")); }

Expand Down
1 change: 1 addition & 0 deletions std/data/queue.spice
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public p Queue.push(const T& item) {
*
* @return First item
*/
#[ignoreUnusedReturnValue]
public f<T&> Queue.pop() {
this.size--;
unsafe {
Expand Down
1 change: 1 addition & 0 deletions std/data/stack.spice
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public p Stack.push(const T& item) {
/**
* Retrieve item and remove it from the stack
*/
#[ignoreUnusedReturnValue]
public f<T&> Stack.pop() {
if this.isEmpty() { panic(Error("The stack is empty")); }
// Pop the element from the stack
Expand Down
Loading

0 comments on commit d9bb728

Please sign in to comment.