Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/AST/Attributes.def
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ ATTRIBUTE_KIND(Inline, "inline", FunctionDefinitionAttachment)
ATTRIBUTE_KIND(Unused, "unused", LocalAttachment)
ATTRIBUTE_KIND(Packed, "packed", StructAttachment)
ATTRIBUTE_WITH_PARAM(Alignment, "alignment", StructAttachment, LiteralExpr)
ATTRIBUTE_WITH_PARAM(LinkageName, "linkage_name", FunctionAttachment, LiteralExpr)

#undef ATTRIBUTE_KIND
#undef ATTRIBUTE_WITH_PARAM
19 changes: 17 additions & 2 deletions lib/IRGen/IRGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ struct IRGenVisitor : public glu::gil::InstVisitor<IRGenVisitor> {
ast::AttributeKind::NoManglingKind
)) {
// No mangling for functions marked as such
} else if (auto *linkageAttr = fn->getDecl()->getAttribute(
ast::AttributeKind::LinkageNameKind
)) {
// Use the specified linkage name
auto *literal
= llvm::dyn_cast<ast::LiteralExpr>(linkageAttr->getParameter());
assert(literal && "linkage_name parameter should be a literal");
assert(
std::holds_alternative<llvm::StringRef>(literal->getValue())
&& "linkage_name parameter should be a string literal"
);
linkageName = std::get<llvm::StringRef>(literal->getValue()).str();
} else {
linkageName = mangleFunctionName(fn->getDecl());
}
Expand All @@ -96,6 +108,9 @@ struct IRGenVisitor : public glu::gil::InstVisitor<IRGenVisitor> {
} else if (fn->getDecl() && fn->getDecl()->isPrivate()
&& !fn->getDecl()->hasAttribute(
ast::AttributeKind::NoManglingKind
)
&& !fn->getDecl()->hasAttribute(
ast::AttributeKind::LinkageNameKind
)) {
// Private functions should have internal linkage, unless marked as
// no_mangling
Expand Down Expand Up @@ -852,8 +867,8 @@ struct IRGenVisitor : public glu::gil::InstVisitor<IRGenVisitor> {
mapValue(inst->getResult(0), result);
}

// Macro to define visit methods for conversion instructions using the
// template
// Macro to define visit methods for conversion instructions using the
// template
#define DEFINE_CONVERSION_VISIT(InstClass, BuilderMethod) \
void visit##InstClass(glu::gil::InstClass *inst) \
{ \
Expand Down
44 changes: 34 additions & 10 deletions lib/Sema/SemanticPass/DuplicateFunctionChecker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace glu::sema {
class DuplicateFunctionChecker
: public ast::ASTWalker<DuplicateFunctionChecker, void> {
DiagnosticManager &_diagManager;
llvm::SmallVector<llvm::StringRef, 16> _noManglingFunctionNames;
llvm::SmallVector<llvm::StringRef, 16> _usedLinkageNames;

public:
explicit DuplicateFunctionChecker(DiagnosticManager &diagManager)
Expand All @@ -28,28 +28,52 @@ class DuplicateFunctionChecker
return;

if (node->hasAttribute(ast::AttributeKind::NoManglingKind))
checkNoManglingDuplicate(node);
checkLinkageNameDuplicate(node, node->getName(), "no_mangling");
else if (node->hasAttribute(ast::AttributeKind::LinkageNameKind))
checkLinkageNameFromAttribute(node);
else
checkDuplicateFunction(node);
}

private:
void checkNoManglingDuplicate(ast::FunctionDecl *node)
/// @brief Check for duplicate linkage names (used by both @no_mangling and
/// @linkage_name)
void checkLinkageNameDuplicate(
ast::FunctionDecl *node, llvm::StringRef linkageName,
llvm::StringRef attributeType
)
{
llvm::StringRef functionName = node->getName();

if (llvm::find(_noManglingFunctionNames, functionName)
!= _noManglingFunctionNames.end()) {
if (llvm::find(_usedLinkageNames, linkageName)
!= _usedLinkageNames.end()) {
_diagManager.error(
node->getLocation(),
"duplicate function with no_mangling attribute: "
+ functionName.str()
"duplicate function with " + attributeType.str()
+ " attribute '" + linkageName.str() + "'"
);
} else {
_noManglingFunctionNames.push_back(functionName);
_usedLinkageNames.push_back(linkageName);
}
}

/// @brief Handle @linkage_name attribute specifically
void checkLinkageNameFromAttribute(ast::FunctionDecl *node)
{
auto *linkageAttr
= node->getAttribute(ast::AttributeKind::LinkageNameKind);
if (!linkageAttr || !linkageAttr->getParameter())
return;

auto *literal
= llvm::dyn_cast<ast::LiteralExpr>(linkageAttr->getParameter());
if (!literal
|| !std::holds_alternative<llvm::StringRef>(literal->getValue()))
return;

llvm::StringRef linkageName
= std::get<llvm::StringRef>(literal->getValue());
checkLinkageNameDuplicate(node, linkageName, "linkage_name");
}

void checkDuplicateFunction(ast::FunctionDecl *node)
{
auto *module = node->getModule();
Expand Down
54 changes: 54 additions & 0 deletions lib/Sema/SemanticPass/ValidAttributeChecker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,41 @@ class ValidAttributeChecker
}
}

/// @brief Validates the @linkage_name attribute parameter
void validateLinkageNameAttribute(ast::Attribute *attr)
{
if (!attr->getParameter())
return;

auto *literal = llvm::dyn_cast<ast::LiteralExpr>(attr->getParameter());
if (!literal) {
_diagManager.error(
attr->getLocation(),
"Attribute '@linkage_name' expects a string literal parameter"
);
return;
}

if (!std::holds_alternative<llvm::StringRef>(literal->getValue())) {
_diagManager.error(
attr->getLocation(),
"Attribute '@linkage_name' expects a string literal, not a "
"numeric or other literal type"
);
return;
}

llvm::StringRef linkageName
= std::get<llvm::StringRef>(literal->getValue());

// Check if linkage name is non-empty
if (linkageName.empty()) {
_diagManager.error(
attr->getLocation(), "Linkage name cannot be empty"
);
}
}

/// @brief Validates attribute-specific constraints
void validateAttributeValue(ast::Attribute *attr)
{
Expand All @@ -59,6 +94,9 @@ class ValidAttributeChecker
case ast::AttributeKind::AlignmentKind:
validateAlignmentAttribute(attr);
break;
case ast::AttributeKind::LinkageNameKind:
validateLinkageNameAttribute(attr);
break;
default: break;
}
}
Expand Down Expand Up @@ -138,6 +176,22 @@ class ValidAttributeChecker
"function prototypes"
);
}

// Check for mutual exclusivity between @linkage_name and @no_mangling
if (node->getAttributes()) {
bool hasLinkageName
= node->hasAttribute(ast::AttributeKind::LinkageNameKind);
bool hasNoMangling
= node->hasAttribute(ast::AttributeKind::NoManglingKind);

if (hasLinkageName && hasNoMangling) {
_diagManager.error(
node->getLocation(),
"Attributes '@linkage_name' and '@no_mangling' are "
"mutually exclusive"
);
}
}
}

void preVisitImportDecl(ast::ImportDecl *node)
Expand Down
43 changes: 43 additions & 0 deletions test/functional/IRGen/linkage_name.glu
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// RUN: split-file %s %t
// RUN: gluc -c %t/libname.glu -o %t/libname.o
// RUN: gluc %t/tester.glu --print-llvm-ir | FileCheck -v %t/tester.glu
//
// Test that @linkage_name attribute generates correct linkage names in LLVM IR


//--- libname.glu

@linkage_name("custom_hello")
public func world() -> *Char {
return "Hello, World!";
}

//--- tester.glu

import libname::*;

// CHECK: define void @implementation_name()
@linkage_name("implementation_name")
func myFunction() {
return;
}

// CHECK: define i32 @param_func(i32 %0)
@linkage_name("param_func")
func withParams(a: Int) -> Int {
return a;
}


// CHECK: define i32 @main()
// CHECK: call ptr @custom_hello()
// CHECK: call void @implementation_name()
// CHECK: call i32 @param_func(i32 42)
func main() -> Int {
world(); // calls function with linkage name "custom_hello"
myFunction(); // calls function with linkage name "implementation_name"
return withParams(42); // calls function with linkage name "param_func"
}

// CHECK: declare {{.*}} @custom_hello()
33 changes: 33 additions & 0 deletions test/functional/Sema/linkage_name_duplicates.glu
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// RUN: not gluc -c %s -o %t.o 2>&1 | FileCheck -v %s
//
// Test duplicate linkage names and conflicts with no_mangling functions

// Test 1: Two functions with the same linkage name should fail
@linkage_name("duplicate_name")
func firstFunction();

// CHECK: error: duplicate function with linkage_name attribute 'duplicate_name'
@linkage_name("duplicate_name")
func secondFunction(param: Int);

// Test 2: @linkage_name function conflicting with @no_mangling function
@no_mangling
func conflict_func();

// CHECK: error: duplicate function with linkage_name attribute 'conflict_func'
@linkage_name("conflict_func")
func anotherFunction();

// Test 3: @no_mangling function conflicting with existing @linkage_name
@linkage_name("existing_name")
func existingLinkage();

// CHECK: error: duplicate function with no_mangling attribute 'existing_name'
@no_mangling
func existing_name();

// CHECK: error: Attributes '@linkage_name' and '@no_mangling' are mutually exclusive
@linkage_name("custom_name")
@no_mangling
func conflictingAttributes();
31 changes: 31 additions & 0 deletions test/functional/Sema/linkage_name_validation.glu
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// RUN: not gluc %s 2>&1 | FileCheck %s
//
// Test that @linkage_name attribute parameter validation works correctly

// Test 1: @linkage_name requires a string parameter
// CHECK: error: Attribute '@linkage_name' expects a parameter of type LiteralExpr
@linkage_name
func missingParam();

// Test 2: @linkage_name with integer parameter (should fail)
// CHECK: error: Attribute '@linkage_name' expects a string literal, not a numeric or other literal type
@linkage_name(42)
func wrongParamType();

// Test 3: @linkage_name with empty string (should fail)
// CHECK: error: Linkage name cannot be empty
@linkage_name("")
func emptyLinkageName();

// Test 4: @linkage_name on wrong declaration type (should fail on structs)
// CHECK: error: Attribute '@linkage_name' is not valid on structs
@linkage_name("test")
struct InvalidOnStruct {
data: UInt64
}

// Test 5: @linkage_name on wrong declaration type (should fail on variables)
// CHECK: error: Attribute '@linkage_name' is not valid on global variables
@linkage_name("test")
var invalidOnVar: Int = 5;
4 changes: 2 additions & 2 deletions test/functional/Sema/multiple_no_mangling_functions.glu
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
return;
}

// CHECK: 10:14: error: duplicate function with no_mangling attribute: toto
// CHECK: 10:14: error: duplicate function with no_mangling attribute 'toto'
@no_mangling func toto(a: Int, argv: **Char) -> Int {
return a;
}

//CHECK: 15:14: error: duplicate function with no_mangling attribute: toto
//CHECK: 15:14: error: duplicate function with no_mangling attribute 'toto'
@no_mangling func toto(wesh: Float) {
return;
}
Loading