blob: 848d04ead09f2d03ff29db75eecd575aade75686 [file] [log] [blame]
Michael Forster7e4244a2022-04-25 00:39:01 -07001// Part of the Crubit project, under the Apache License v2.0 with LLVM
2// Exceptions. See /LICENSE for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5#include "rs_bindings_from_cc/importers/function.h"
6
Lukasz Anforowiczcec7a8a2022-04-27 10:24:51 -07007#include "absl/strings/substitute.h"
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07008#include "rs_bindings_from_cc/ast_util.h"
Lukasz Anforowicz8f590682022-05-17 12:39:20 -07009#include "clang/Sema/Sema.h"
Michael Forster7e4244a2022-04-25 00:39:01 -070010
11namespace crubit {
12
13std::optional<IR::Item> FunctionDeclImporter::Import(
14 clang::FunctionDecl* function_decl) {
15 if (!ictx_.IsFromCurrentTarget(function_decl)) return std::nullopt;
16 if (function_decl->isDeleted()) return std::nullopt;
17
18 clang::tidy::lifetimes::LifetimeSymbolTable lifetime_symbol_table;
19 llvm::Expected<clang::tidy::lifetimes::FunctionLifetimes> lifetimes =
20 clang::tidy::lifetimes::GetLifetimeAnnotations(
21 function_decl, *ictx_.invocation_.lifetime_context_,
22 &lifetime_symbol_table);
23
24 std::vector<FuncParam> params;
25 std::set<std::string> errors;
26 auto add_error = [&errors](std::string msg) {
27 auto result = errors.insert(std::move(msg));
28 CRUBIT_CHECK(result.second && "Duplicated error message");
29 };
30 if (auto* method_decl =
31 clang::dyn_cast<clang::CXXMethodDecl>(function_decl)) {
32 if (!ictx_.type_mapper_.Contains(method_decl->getParent())) {
33 return ictx_.ImportUnsupportedItem(function_decl,
34 "Couldn't import the parent");
35 }
36
37 // non-static member functions receive an implicit `this` parameter.
38 if (method_decl->isInstance()) {
39 std::optional<clang::tidy::lifetimes::ValueLifetimes> this_lifetimes;
40 if (lifetimes) {
41 this_lifetimes = lifetimes->GetThisLifetimes();
42 }
43 auto param_type = ictx_.type_mapper_.ConvertQualType(
44 method_decl->getThisType(), this_lifetimes,
45 /*nullable=*/false);
46 if (!param_type.ok()) {
47 add_error(absl::StrCat("`this` parameter is not supported: ",
48 param_type.status().message()));
49 } else {
50 params.push_back({*std::move(param_type), Identifier("__this")});
51 }
52 }
53 }
54
55 if (lifetimes) {
56 CRUBIT_CHECK(lifetimes->IsValidForDecl(function_decl));
57 }
58
59 for (unsigned i = 0; i < function_decl->getNumParams(); ++i) {
60 const clang::ParmVarDecl* param = function_decl->getParamDecl(i);
61 std::optional<clang::tidy::lifetimes::ValueLifetimes> param_lifetimes;
62 if (lifetimes) {
63 param_lifetimes = lifetimes->GetParamLifetimes(i);
64 }
65 auto param_type =
66 ictx_.type_mapper_.ConvertQualType(param->getType(), param_lifetimes);
67 if (!param_type.ok()) {
68 add_error(absl::Substitute("Parameter #$0 is not supported: $1", i,
69 param_type.status().message()));
70 continue;
71 }
72
73 if (const clang::RecordType* record_type =
74 clang::dyn_cast<clang::RecordType>(param->getType())) {
75 if (clang::RecordDecl* record_decl =
76 clang::dyn_cast<clang::RecordDecl>(record_type->getDecl())) {
77 // TODO(b/200067242): non-trivial_abi structs, when passed by value,
78 // have a different representation which needs special support. We
79 // currently do not support it.
80 if (!record_decl->canPassInRegisters()) {
81 add_error(
82 absl::Substitute("Non-trivial_abi type '$0' is not "
83 "supported by value as parameter #$1",
84 param->getType().getAsString(), i));
85 }
86 }
87 }
88
89 std::optional<Identifier> param_name = ictx_.GetTranslatedIdentifier(param);
90 CRUBIT_CHECK(param_name.has_value()); // No known failure cases.
91 params.push_back({*param_type, *std::move(param_name)});
92 }
93
Lukasz Anforowicz8f590682022-05-17 12:39:20 -070094 if (function_decl->getReturnType()->isUndeducedType()) {
95 bool still_undeduced = ictx_.sema_.DeduceReturnType(
96 function_decl, function_decl->getLocation());
97 if (still_undeduced) {
98 add_error("Couldn't deduce the return type");
99 }
100 }
101
Michael Forster7e4244a2022-04-25 00:39:01 -0700102 if (const clang::RecordType* record_return_type =
103 clang::dyn_cast<clang::RecordType>(function_decl->getReturnType())) {
104 if (clang::RecordDecl* record_decl =
105 clang::dyn_cast<clang::RecordDecl>(record_return_type->getDecl())) {
106 // TODO(b/200067242): non-trivial_abi structs, when passed by value,
107 // have a different representation which needs special support. We
108 // currently do not support it.
109 if (!record_decl->canPassInRegisters()) {
110 add_error(
111 absl::Substitute("Non-trivial_abi type '$0' is not supported "
112 "by value as a return type",
113 function_decl->getReturnType().getAsString()));
114 }
115 }
116 }
117
118 std::optional<clang::tidy::lifetimes::ValueLifetimes> return_lifetimes;
119 if (lifetimes) {
120 return_lifetimes = lifetimes->GetReturnLifetimes();
121 }
122
123 auto return_type = ictx_.type_mapper_.ConvertQualType(
124 function_decl->getReturnType(), return_lifetimes);
125 if (!return_type.ok()) {
126 add_error(absl::StrCat("Return type is not supported: ",
127 return_type.status().message()));
128 }
129
130 llvm::DenseSet<clang::tidy::lifetimes::Lifetime> all_free_lifetimes;
131 if (lifetimes) {
132 all_free_lifetimes = lifetimes->AllFreeLifetimes();
133 }
134
135 std::vector<LifetimeName> lifetime_params;
136 for (clang::tidy::lifetimes::Lifetime lifetime : all_free_lifetimes) {
137 std::optional<llvm::StringRef> name =
138 lifetime_symbol_table.LookupLifetime(lifetime);
139 CRUBIT_CHECK(name.has_value());
140 lifetime_params.push_back(
141 {.name = name->str(), .id = LifetimeId(lifetime.Id())});
142 }
143 llvm::sort(lifetime_params,
144 [](const LifetimeName& l1, const LifetimeName& l2) {
145 return l1.name < l2.name;
146 });
147
148 llvm::Optional<MemberFuncMetadata> member_func_metadata;
149 if (auto* method_decl =
150 clang::dyn_cast<clang::CXXMethodDecl>(function_decl)) {
151 switch (method_decl->getAccess()) {
152 case clang::AS_public:
153 break;
154 case clang::AS_protected:
155 case clang::AS_private:
156 case clang::AS_none:
157 // No need for IR to include Func representing private methods.
158 // TODO(lukasza): Revisit this for protected methods.
159 return std::nullopt;
160 }
161 llvm::Optional<MemberFuncMetadata::InstanceMethodMetadata>
162 instance_metadata;
163 if (method_decl->isInstance()) {
164 MemberFuncMetadata::ReferenceQualification reference;
165 switch (method_decl->getRefQualifier()) {
166 case clang::RQ_LValue:
167 reference = MemberFuncMetadata::kLValue;
168 break;
169 case clang::RQ_RValue:
170 reference = MemberFuncMetadata::kRValue;
171 break;
172 case clang::RQ_None:
173 reference = MemberFuncMetadata::kUnqualified;
174 break;
175 }
176 instance_metadata = MemberFuncMetadata::InstanceMethodMetadata{
177 .reference = reference,
178 .is_const = method_decl->isConst(),
179 .is_virtual = method_decl->isVirtual(),
180 .is_explicit_ctor = false,
181 };
182 if (auto* ctor_decl =
183 clang::dyn_cast<clang::CXXConstructorDecl>(function_decl)) {
184 instance_metadata->is_explicit_ctor = ctor_decl->isExplicit();
185 }
186 }
187
188 member_func_metadata = MemberFuncMetadata{
189 .record_id = GenerateItemId(method_decl->getParent()),
190 .instance_method_metadata = instance_metadata};
191 }
192
193 if (!errors.empty()) {
194 return ictx_.ImportUnsupportedItem(function_decl, errors);
195 }
196
197 bool has_c_calling_convention =
198 function_decl->getType()->getAs<clang::FunctionType>()->getCallConv() ==
199 clang::CC_C;
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -0700200 bool is_member_or_descendant_of_class_template =
201 IsFullClassTemplateSpecializationOrChild(function_decl);
Michael Forster7e4244a2022-04-25 00:39:01 -0700202 std::optional<UnqualifiedIdentifier> translated_name =
203 ictx_.GetTranslatedName(function_decl);
204
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -0700205 llvm::Optional<std::string> doc_comment = ictx_.GetComment(function_decl);
206 if (!doc_comment.hasValue() && is_member_or_descendant_of_class_template) {
207 // Despite `is_member_or_descendant_of_class_template` check above, we are
208 // not guaranteed that a `func_pattern` exists below. For example, it may
209 // be missing when `function_decl` is an implicitly defined constructor of a
210 // class template -- such decls are generated, not instantiated.
211 if (clang::FunctionDecl* func_pattern =
212 function_decl->getTemplateInstantiationPattern()) {
213 doc_comment = ictx_.GetComment(func_pattern);
214 }
215 }
216
217 std::string mangled_name = ictx_.GetMangledName(function_decl);
218 if (is_member_or_descendant_of_class_template) {
219 // TODO(b/222001243): Avoid calling `ConvertToCcIdentifier(target)` to
220 // distinguish multiple definitions of a template instantiation. Instead
221 // help the linker merge all the definitions into one, by defining the
222 // thunk via a function template - see "Handling thunks" section in
223 // <internal link>
224 mangled_name += '_';
225 mangled_name += ConvertToCcIdentifier(ictx_.GetOwningTarget(function_decl));
226 }
227
Michael Forster7e4244a2022-04-25 00:39:01 -0700228 // Silence ClangTidy, checked above: calling `add_error` if
229 // `!return_type.ok()` and returning early if `!errors.empty()`.
230 CRUBIT_CHECK(return_type.ok());
231
232 if (translated_name.has_value()) {
233 return Func{
234 .name = *translated_name,
235 .owning_target = ictx_.GetOwningTarget(function_decl),
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -0700236 .doc_comment = std::move(doc_comment),
237 .mangled_name = std::move(mangled_name),
Michael Forster7e4244a2022-04-25 00:39:01 -0700238 .return_type = *return_type,
239 .params = std::move(params),
240 .lifetime_params = std::move(lifetime_params),
241 .is_inline = function_decl->isInlined(),
242 .member_func_metadata = std::move(member_func_metadata),
243 .has_c_calling_convention = has_c_calling_convention,
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -0700244 .is_member_or_descendant_of_class_template =
245 is_member_or_descendant_of_class_template,
Michael Forster7e4244a2022-04-25 00:39:01 -0700246 .source_loc = ictx_.ConvertSourceLocation(function_decl->getBeginLoc()),
247 .id = GenerateItemId(function_decl),
Rosica Dejanovskae91d2992022-05-05 05:31:39 -0700248 .enclosing_namespace_id = GetEnclosingNamespaceId(function_decl),
Michael Forster7e4244a2022-04-25 00:39:01 -0700249 };
250 }
251 return std::nullopt;
252}
253
254} // namespace crubit