LILAC
Language to language Interop LAyer Compiler
Loading...
Searching...
No Matches
exportattr.cxx
Go to the documentation of this file.
1/*
2 * Copyright (C) 2024 Yeong-won Seo
3 *
4 * This file is part of LILAC.
5 *
6 * LILAC is free software: you can redistribute it and/or modify it under
7 * the terms of the GNU General Public License as published by the Free
8 * Software Foundation, either version 3, or (at your option) any later
9 * version.
10 *
11 * LILAC is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "exportattr.h"
21#include "pch.h"
22
23#define WARNMSG "; exporting skipped!"
24
25namespace
26{
27 clang::Visibility g_DefaultTypeVisibility = clang::DefaultVisibility;
28 clang::Visibility g_DefaultValueVisibility = clang::DefaultVisibility;
29
30 bool ShouldExport(clang::Sema& sema, const clang::NamedDecl* decl)
31 {
32 /************************************************************************
33 * Initialize diagnostics *
34 ************************************************************************/
35 auto& diag = sema.getDiagnostics();
36 const auto invalidVisibility = diag.getCustomDiagID(
37 clang::DiagnosticsEngine::Warning,
38 "declaration cannot be exported because of its visibility" WARNMSG);
39 const auto invalidAccess = diag.getCustomDiagID(
40 clang::DiagnosticsEngine::Warning,
41 "non-public declaration cannot be exported" WARNMSG);
42 const auto invalidSignature = sema.getDiagnostics().getCustomDiagID(
43 clang::DiagnosticsEngine::Warning,
44 "%0 function '%1' cannot be exported" WARNMSG);
45
46 /************************************************************************
47 * Ensure visibility of declaration to default *
48 ************************************************************************/
49 if (const auto vis = decl->getExplicitVisibility(clang::isa<clang::RecordDecl>(decl)
50 ? clang::TypeDecl::VisibilityForType
51 : clang::TypeDecl::VisibilityForValue);
52 (vis.has_value() && vis != clang::DefaultVisibility) ||
53 (clang::isa<clang::RecordDecl>(decl)
54 ? g_DefaultTypeVisibility
55 : g_DefaultValueVisibility) != clang::DefaultVisibility)
56 {
57 sema.Diag(decl->getLocation(), invalidVisibility);
58 return false;
59 }
60
61 /************************************************************************
62 * Check function constraints *
63 ************************************************************************/
64 if (const auto fn = clang::dyn_cast<clang::FunctionDecl>(decl))
65 {
66 if (fn->isConsteval() || fn->isConstexpr())
67 {
68 sema.Diag(fn->getDefaultLoc(), invalidSignature) << "consteval/constexpr" << decl;
69 return false;
70 }
71 if (fn->isVariadic())
72 {
73 sema.Diag(fn->getDefaultLoc(), invalidSignature) << "variadic" << decl;
74 return false;
75 }
76 if (fn->isDeleted())
77 {
78 sema.Diag(fn->getDefaultLoc(), invalidSignature) << "deleted" << decl;
79 return false;
80 }
81 if (fn->isInlined())
82 {
83 sema.Diag(fn->getDefaultLoc(), invalidSignature) << "inlined" << decl;
84 return false;
85 }
86 if (fn->isPureVirtual())
87 {
88 sema.Diag(fn->getDefaultLoc(), invalidSignature) << "pure-virtual" << decl;
89 return false;
90 }
91 }
92
93 /************************************************************************
94 * Check access modifiers *
95 ************************************************************************/
96 auto record = clang::dyn_cast<clang::CXXRecordDecl>(decl->getDeclContext());
97 if (!record)
98 return true;
99
100 auto access = clang::AS_none;
101 for (auto* member: record->decls())
102 {
103 if (clang::isa<clang::AccessSpecDecl>(member))
104 access = clang::cast<clang::AccessSpecDecl>(member)->getAccess();
105 if (clang::declaresSameEntity(member, decl))
106 break;
107 }
108
109 if (access == clang::AS_public ||
110 (record->isStruct() && access == clang::AS_none))
111 return true;
112
113 sema.Diag(decl->getLocation(), invalidAccess);
114 return false;
115 }
116}
117
118namespace lilac::cxx
119{
121 {
122 OptArgs = 0;
123
124 static constexpr Spelling S[] = {
125 { clang::ParsedAttr::AS_GNU, "__lilac_export" },
126 { clang::ParsedAttr::AS_Declspec, "__lilac_export" },
127 { clang::ParsedAttr::AS_C23, "__lilac_export" },
128 { clang::ParsedAttr::AS_C23, "lilac::export" },
129 { clang::ParsedAttr::AS_CXX11, "__lilac_export" },
130 { clang::ParsedAttr::AS_CXX11, "lilac::export" },
131 };
132
133 Spellings = S;
134 }
135
137 clang::Sema& sema,
138 const clang::ParsedAttr& attr,
139 const clang::Decl* decl) const
140 {
141 /************************************************************************
142 * Ensure whether the declaration can be exported *
143 ************************************************************************/
144 if (!clang::isa<clang::FunctionDecl>(decl) &&
145 !clang::isa<clang::RecordDecl>(decl) &&
146 !clang::isa<clang::EnumDecl>(decl))
147 {
148 const auto invalidDeclType = sema.getDiagnostics().getCustomDiagID(
149 clang::DiagnosticsEngine::Warning,
150 "'%0' attribute can be applied to only functions, enumerations and records" WARNMSG);
151
152 sema.Diag(attr.getLoc(), invalidDeclType) << attr;
153 return false;
154 }
155 if (!ShouldExport(sema, clang::cast<clang::NamedDecl>(decl)))
156 return false;
157
158 return true;
159 }
160
161 clang::ParsedAttrInfo::AttrHandling ExportAttrInfo::handleDeclAttribute(
162 clang::Sema& sema,
163 clang::Decl* decl,
164 const clang::ParsedAttr& attr) const
165 {
166 if (attr.getNumArgs())
167 {
168 const auto anyArgs = sema.getDiagnostics().getCustomDiagID(
169 clang::DiagnosticsEngine::Warning,
170 "'%0' attribute accepts no argument" WARNMSG);
171 sema.Diag(attr.getLoc(), anyArgs) << attr;
172 return AttributeNotApplied;
173 }
174
175 if (auto* const tag = clang::dyn_cast<clang::TagDecl>(decl))
176 {
177 tag->addAttr(clang::AnnotateTypeAttr::Create(sema.Context, AttrMangling, nullptr, 0));
178 }
179 else if (auto* const function = clang::dyn_cast<clang::FunctionDecl>(decl))
180 {
181 function->addAttr(clang::AnnotateAttr::Create(sema.Context, AttrMangling, nullptr, 0));
182 }
183 else
184 {
185 assert(false && "invalid decl type");
186 }
187
188 return AttributeApplied;
189 }
190
191 bool ExportAttrInfo::acceptsLangOpts(const clang::LangOptions& LO) const
192 {
193 g_DefaultTypeVisibility = LO.getTypeVisibilityMode();
194 g_DefaultValueVisibility = LO.getValueVisibilityMode();
195 return true;
196 }
197}
198
199[[maybe_unused]]
200static clang::ParsedAttrInfoRegistry::Add<lilac::cxx::ExportAttrInfo> X("apiexport", "");
bool diagAppertainsToDecl(clang::Sema &sema, const clang::ParsedAttr &attr, const clang::Decl *decl) const override
bool acceptsLangOpts(const clang::LangOptions &LO) const override
AttrHandling handleDeclAttribute(clang::Sema &sema, clang::Decl *decl, const clang::ParsedAttr &attr) const override
static constexpr std::string AttrMangling
Definition exportattr.h:37
#define WARNMSG