Skip to content

Implement easy C externs#1402

Open
mattisboeckle wants to merge 15 commits into
mainfrom
feature/c-externs
Open

Implement easy C externs#1402
mattisboeckle wants to merge 15 commits into
mainfrom
feature/c-externs

Conversation

@mattisboeckle

@mattisboeckle mattisboeckle commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Enables writing extern definitions such as

extern def glfwCreateWindow(width: C::Int, height: C::Int, title: C::String, monitor: C::Ptr, share: GLFWwindow): GLFWwindow = c "glfwCreateWindow"

which has to match a linked c function that has the correct name and types.

In this specific case

GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share);

This will automatically generate a wrapper function for said C function, which can then be called from effekt.
There are some selected types and utility functions in effekt.effekt

@mattisboeckle

Copy link
Copy Markdown
Contributor Author

@b-studios ping

@b-studios b-studios left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work!

What I would love to see is a simple test, but I understand that might be difficult to integrate into our testing infrastructure?

Comment on lines +58 to +62
emit(Call("ccall", Ccc(), transformedReturnType, ConstantGlobal(cFunctionName), transformedParameters.map { case Parameter(typ, name) => LocalReference(typ, name) }))

transformedReturnType match {
case VoidType() => RetVoid()
case _ => Ret(LocalReference(transformedReturnType, "ccall"))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is always just generating a single call, right? What is the purpose of the defineCWrapperFunction then? It is only called here, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just readability and consistency with defineFunction and defineLabel, but it is technically only called here. Yes.

Comment on lines +416 to +419
case (machine.Type.CTpe(machine.CType.Void), machine.Type.Positive()) =>
()
case (machine.Type.Positive(), machine.Type.CTpe(machine.CType.Void)) =>
()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these show up? This sounds dangerous since it violates that we should not use C::Void anywhere

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The coercion also happens to return values of functions.
As we cannot coerce void or even pass it to a function as an argument we cannot use the existing infrastructure and need to handle it somewhere earlier. So there were two possible solutions.

  1. Special case CTpe(Void) in machine transformer perhapsUnbox and never generate the coercion
  2. Special case here and do this no-op coercion

I thought the latter looked slightly better

case Type.Byte() => "Byte"
case Type.Double() => "Double"
case Type.Reference(tpe) => toDoc(tpe) <> "*"
case Type.CTpe(name) => s"CType(${name})"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably, I would just print name here. But this really does not matter :)

Comment thread effekt/shared/src/main/scala/effekt/machine/Transformer.scala Outdated
case Byte()
case Double()
case Reference(tpe: Type)
case CTpe(tpe: CType)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should bikeshed this constructor name here. Same actually for CType. The strings on the right hand side of a c extern actually are llvm type names. So CType is a bit of a misnomer, or am I wrong?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I thought more along the lines of types for c externs instead of c types directly, but I am open to suggestions

Comment thread libraries/common/effekt.effekt Outdated
mattisboeckle and others added 2 commits June 17, 2026 15:22
Co-authored-by: Jonathan Immanuel Brachthäuser <jonathan.brachthaeuser@uni-tuebingen.de>
More in line with the LLVM naming scheme
@mattisboeckle

Copy link
Copy Markdown
Contributor Author

To reliably test this I would need to link against some c file in the testing environment. This leads back to the config file / command line debate and I would rather not hack around that for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants