[Chapel Merge] Support renaming an extern type as a primitive C type

Branch: refs/heads/main
Revision: 090f7f02581247451de65ab5f3d7843ac10ccce2
Author: dlongnecke-cray
Link: Support renaming an extern type as a primitive C type by dlongnecke-cray · Pull Request #28649 · chapel-lang/chapel · GitHub
Log Message:
Support renaming an extern type as a primitive C type (#28649)

This PR adds support for renaming an extern type to have the same name
as a primitive C type, e.g., extern "char" type. This behavior works
out of the box with the C backend, but was non-functional under LLVM
before this effort.

pragma "opaque c type alias"
extern "char" type opaqueChar;

// Will be code generated in C as 'char**'.
type argArray = c_ptr(c_ptr(opaqueChar));

This functionality is implemented via a new pragma "opaque c type alias". See the interop test for another example.

The renaming only works when the extern type does not have an
initializer expression. Otherwise, the type alias is dropped on the
floor and replaced by its RHS after resolution. When the extern type
is not initialized, the compiler creates a PrimitiveType and holds
onto the type until code generation.

Under the C backend, the Chapel compiler assumes no prior knowledge
about C code when code-generating and blindly emits the type char for
opaqueChar where needed (which is what we want). Under LLVM, the
compiler complained about no matching type for "char" because it had
no such entry in the symbol table we build over LLVM/Clang symbols.

At first, I was quite perplexed by this error. How could LLVM not have
knowledge of char? For many reasons, actually. The primary reason
being that char is a C language construct. The other reason being that
our Clang/LLVM integration is structured in such a way that it concerns
itself mainly with C declarations. The type char is a C
primitive/builtin - there is no declaration for the Chapel compiler to
observe.

After some brainstorming, a fix became clear. We already have a type
which we map cleanly to char: the type c_char. So by manually
associating the C name "char" with dt_c_char (the compiler Type*
representing c_char, which is usually lowered to the Type* for
int(8)) a path forward became possible.

I still needed to add a pragma as there was no easy way (at present) to
make this mapping happen automatically. Design-wise, I think this is the
principled choice as it makes the special behavior obvious.

Note that right now only the "char" type is supported, because that is
the type I need to make progress on other work. There is a path forward
to automate this behavior for all the types specified in the
ChapelSysCTypes generated module, but I will leave that as future
work.

Reviewed by @jabraham17. Thanks!

Compare: Comparing 05917e10a4017a3e52c76df458e1d6047d1a657f...f73c1c51bc0434f7b0e314992220c1eedbb36df9 · chapel-lang/chapel · GitHub

Diff:
M compiler/AST/build.cpp
M compiler/codegen/cg-symbol.cpp
M compiler/llvm/clangUtil.cpp
M frontend/include/chpl/uast/PragmaList.h
A test/interop/C/OpaqueCharAlias.chpl
A test/interop/C/OpaqueCharAlias.notest
A test/interop/C/use_OpaqueCharAlias.cleanfiles
A test/interop/C/use_OpaqueCharAlias.good
A test/interop/C/use_OpaqueCharAlias.lastcompopts
A test/interop/C/use_OpaqueCharAlias.prediff
A test/interop/C/use_OpaqueCharAlias.test.c
https://github.com/chapel-lang/chapel/pull/28649.diff