Coverage Report

Created: 2022-01-18 06:27

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/AST/QualTypeNames.cpp
Line
Count
Source (jump to first uncovered line)
1
//===------- QualTypeNames.cpp - Generate Complete QualType Names ---------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "clang/AST/DeclTemplate.h"
10
#include "clang/AST/DeclarationName.h"
11
#include "clang/AST/GlobalDecl.h"
12
#include "clang/AST/Mangle.h"
13
#include "clang/AST/QualTypeNames.h"
14
15
#include <stdio.h>
16
#include <memory>
17
18
namespace clang {
19
20
namespace TypeName {
21
22
/// Create a NestedNameSpecifier for Namesp and its enclosing
23
/// scopes.
24
///
25
/// \param[in] Ctx - the AST Context to be used.
26
/// \param[in] Namesp - the NamespaceDecl for which a NestedNameSpecifier
27
/// is requested.
28
/// \param[in] WithGlobalNsPrefix - Indicate whether the global namespace
29
/// specifier "::" should be prepended or not.
30
static NestedNameSpecifier *createNestedNameSpecifier(
31
    const ASTContext &Ctx,
32
    const NamespaceDecl *Namesp,
33
    bool WithGlobalNsPrefix);
34
35
/// Create a NestedNameSpecifier for TagDecl and its enclosing
36
/// scopes.
37
///
38
/// \param[in] Ctx - the AST Context to be used.
39
/// \param[in] TD - the TagDecl for which a NestedNameSpecifier is
40
/// requested.
41
/// \param[in] FullyQualify - Convert all template arguments into fully
42
/// qualified names.
43
/// \param[in] WithGlobalNsPrefix - Indicate whether the global namespace
44
/// specifier "::" should be prepended or not.
45
static NestedNameSpecifier *createNestedNameSpecifier(
46
    const ASTContext &Ctx, const TypeDecl *TD,
47
    bool FullyQualify, bool WithGlobalNsPrefix);
48
49
static NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
50
    const ASTContext &Ctx, const Decl *decl,
51
    bool FullyQualified, bool WithGlobalNsPrefix);
52
53
static NestedNameSpecifier *getFullyQualifiedNestedNameSpecifier(
54
    const ASTContext &Ctx, NestedNameSpecifier *scope, bool WithGlobalNsPrefix);
55
56
static bool getFullyQualifiedTemplateName(const ASTContext &Ctx,
57
                                          TemplateName &TName,
58
0
                                          bool WithGlobalNsPrefix) {
59
0
  bool Changed = false;
60
0
  NestedNameSpecifier *NNS = nullptr;
61
62
0
  TemplateDecl *ArgTDecl = TName.getAsTemplateDecl();
63
  // ArgTDecl won't be NULL because we asserted that this isn't a
64
  // dependent context very early in the call chain.
65
0
  assert(ArgTDecl != nullptr);
66
0
  QualifiedTemplateName *QTName = TName.getAsQualifiedTemplateName();
67
68
0
  if (QTName && !QTName->hasTemplateKeyword()) {
69
0
    NNS = QTName->getQualifier();
70
0
    NestedNameSpecifier *QNNS = getFullyQualifiedNestedNameSpecifier(
71
0
        Ctx, NNS, WithGlobalNsPrefix);
72
0
    if (QNNS != NNS) {
73
0
      Changed = true;
74
0
      NNS = QNNS;
75
0
    } else {
76
0
      NNS = nullptr;
77
0
    }
78
0
  } else {
79
0
    NNS = createNestedNameSpecifierForScopeOf(
80
0
        Ctx, ArgTDecl, true, WithGlobalNsPrefix);
81
0
  }
82
0
  if (NNS) {
83
0
    TName = Ctx.getQualifiedTemplateName(NNS,
84
0
                                         /*TemplateKeyword=*/false, ArgTDecl);
85
0
    Changed = true;
86
0
  }
87
0
  return Changed;
88
0
}
89
90
static bool getFullyQualifiedTemplateArgument(const ASTContext &Ctx,
91
                                              TemplateArgument &Arg,
92
32
                                              bool WithGlobalNsPrefix) {
93
32
  bool Changed = false;
94
95
  // Note: we do not handle TemplateArgument::Expression, to replace it
96
  // we need the information for the template instance decl.
97
98
32
  if (Arg.getKind() == TemplateArgument::Template) {
99
0
    TemplateName TName = Arg.getAsTemplate();
100
0
    Changed = getFullyQualifiedTemplateName(Ctx, TName, WithGlobalNsPrefix);
101
0
    if (Changed) {
102
0
      Arg = TemplateArgument(TName);
103
0
    }
104
32
  } else if (Arg.getKind() == TemplateArgument::Type) {
105
31
    QualType SubTy = Arg.getAsType();
106
    // Check if the type needs more desugaring and recurse.
107
31
    QualType QTFQ = getFullyQualifiedType(SubTy, Ctx, WithGlobalNsPrefix);
108
31
    if (QTFQ != SubTy) {
109
11
      Arg = TemplateArgument(QTFQ);
110
11
      Changed = true;
111
11
    }
112
31
  }
113
32
  return Changed;
114
32
}
115
116
static const Type *getFullyQualifiedTemplateType(const ASTContext &Ctx,
117
                                                 const Type *TypePtr,
118
56
                                                 bool WithGlobalNsPrefix) {
119
  // DependentTemplateTypes exist within template declarations and
120
  // definitions. Therefore we shouldn't encounter them at the end of
121
  // a translation unit. If we do, the caller has made an error.
122
56
  assert(!isa<DependentTemplateSpecializationType>(TypePtr));
123
  // In case of template specializations, iterate over the arguments
124
  // and fully qualify them as well.
125
56
  if (const auto *TST = dyn_cast<const TemplateSpecializationType>(TypePtr)) {
126
11
    bool MightHaveChanged = false;
127
11
    SmallVector<TemplateArgument, 4> FQArgs;
128
11
    for (TemplateSpecializationType::iterator I = TST->begin(), E = TST->end();
129
31
         I != E; 
++I20
) {
130
      // Cheap to copy and potentially modified by
131
      // getFullyQualifedTemplateArgument.
132
20
      TemplateArgument Arg(*I);
133
20
      MightHaveChanged |= getFullyQualifiedTemplateArgument(
134
20
          Ctx, Arg, WithGlobalNsPrefix);
135
20
      FQArgs.push_back(Arg);
136
20
    }
137
138
    // If a fully qualified arg is different from the unqualified arg,
139
    // allocate new type in the AST.
140
11
    if (MightHaveChanged) {
141
5
      QualType QT = Ctx.getTemplateSpecializationType(
142
5
          TST->getTemplateName(), FQArgs,
143
5
          TST->getCanonicalTypeInternal());
144
      // getTemplateSpecializationType returns a fully qualified
145
      // version of the specialization itself, so no need to qualify
146
      // it.
147
5
      return QT.getTypePtr();
148
5
    }
149
45
  } else if (const auto *TSTRecord = dyn_cast<const RecordType>(TypePtr)) {
150
    // We are asked to fully qualify and we have a Record Type,
151
    // which can point to a template instantiation with no sugar in any of
152
    // its template argument, however we still need to fully qualify them.
153
154
45
    if (const auto *TSTDecl =
155
45
        dyn_cast<ClassTemplateSpecializationDecl>(TSTRecord->getDecl())) {
156
9
      const TemplateArgumentList &TemplateArgs = TSTDecl->getTemplateArgs();
157
158
9
      bool MightHaveChanged = false;
159
9
      SmallVector<TemplateArgument, 4> FQArgs;
160
21
      for (unsigned int I = 0, E = TemplateArgs.size(); I != E; 
++I12
) {
161
        // cheap to copy and potentially modified by
162
        // getFullyQualifedTemplateArgument
163
12
        TemplateArgument Arg(TemplateArgs[I]);
164
12
        MightHaveChanged |= getFullyQualifiedTemplateArgument(
165
12
            Ctx, Arg, WithGlobalNsPrefix);
166
12
        FQArgs.push_back(Arg);
167
12
      }
168
169
      // If a fully qualified arg is different from the unqualified arg,
170
      // allocate new type in the AST.
171
9
      if (MightHaveChanged) {
172
1
        TemplateName TN(TSTDecl->getSpecializedTemplate());
173
1
        QualType QT = Ctx.getTemplateSpecializationType(
174
1
            TN, FQArgs,
175
1
            TSTRecord->getCanonicalTypeInternal());
176
        // getTemplateSpecializationType returns a fully qualified
177
        // version of the specialization itself, so no need to qualify
178
        // it.
179
1
        return QT.getTypePtr();
180
1
      }
181
9
    }
182
45
  }
183
50
  return TypePtr;
184
56
}
185
186
static NestedNameSpecifier *createOuterNNS(const ASTContext &Ctx, const Decl *D,
187
                                           bool FullyQualify,
188
113
                                           bool WithGlobalNsPrefix) {
189
113
  const DeclContext *DC = D->getDeclContext();
190
113
  if (const auto *NS = dyn_cast<NamespaceDecl>(DC)) {
191
47
    while (NS && 
NS->isInline()46
) {
192
      // Ignore inline namespace;
193
1
      NS = dyn_cast<NamespaceDecl>(NS->getDeclContext());
194
1
    }
195
46
    if (NS && 
NS->getDeclName()45
) {
196
45
      return createNestedNameSpecifier(Ctx, NS, WithGlobalNsPrefix);
197
45
    }
198
1
    return nullptr;  // no starting '::', no anonymous
199
67
  } else if (const auto *TD = dyn_cast<TagDecl>(DC)) {
200
0
    return createNestedNameSpecifier(Ctx, TD, FullyQualify, WithGlobalNsPrefix);
201
67
  } else if (const auto *TDD = dyn_cast<TypedefNameDecl>(DC)) {
202
0
    return createNestedNameSpecifier(
203
0
        Ctx, TDD, FullyQualify, WithGlobalNsPrefix);
204
67
  } else if (WithGlobalNsPrefix && 
DC->isTranslationUnit()5
) {
205
5
    return NestedNameSpecifier::GlobalSpecifier(Ctx);
206
5
  }
207
62
  return nullptr;  // no starting '::' if |WithGlobalNsPrefix| is false
208
113
}
209
210
/// Return a fully qualified version of this name specifier.
211
static NestedNameSpecifier *getFullyQualifiedNestedNameSpecifier(
212
    const ASTContext &Ctx, NestedNameSpecifier *Scope,
213
0
    bool WithGlobalNsPrefix) {
214
0
  switch (Scope->getKind()) {
215
0
    case NestedNameSpecifier::Global:
216
      // Already fully qualified
217
0
      return Scope;
218
0
    case NestedNameSpecifier::Namespace:
219
0
      return TypeName::createNestedNameSpecifier(
220
0
          Ctx, Scope->getAsNamespace(), WithGlobalNsPrefix);
221
0
    case NestedNameSpecifier::NamespaceAlias:
222
      // Namespace aliases are only valid for the duration of the
223
      // scope where they were introduced, and therefore are often
224
      // invalid at the end of the TU.  So use the namespace name more
225
      // likely to be valid at the end of the TU.
226
0
      return TypeName::createNestedNameSpecifier(
227
0
          Ctx,
228
0
          Scope->getAsNamespaceAlias()->getNamespace()->getCanonicalDecl(),
229
0
          WithGlobalNsPrefix);
230
0
    case NestedNameSpecifier::Identifier:
231
      // A function or some other construct that makes it un-namable
232
      // at the end of the TU. Skip the current component of the name,
233
      // but use the name of it's prefix.
234
0
      return getFullyQualifiedNestedNameSpecifier(
235
0
          Ctx, Scope->getPrefix(), WithGlobalNsPrefix);
236
0
    case NestedNameSpecifier::Super:
237
0
    case NestedNameSpecifier::TypeSpec:
238
0
    case NestedNameSpecifier::TypeSpecWithTemplate: {
239
0
      const Type *Type = Scope->getAsType();
240
      // Find decl context.
241
0
      const TagDecl *TD = nullptr;
242
0
      if (const TagType *TagDeclType = Type->getAs<TagType>()) {
243
0
        TD = TagDeclType->getDecl();
244
0
      } else {
245
0
        TD = Type->getAsCXXRecordDecl();
246
0
      }
247
0
      if (TD) {
248
0
        return TypeName::createNestedNameSpecifier(Ctx, TD,
249
0
                                                   true /*FullyQualified*/,
250
0
                                                   WithGlobalNsPrefix);
251
0
      } else if (const auto *TDD = dyn_cast<TypedefType>(Type)) {
252
0
        return TypeName::createNestedNameSpecifier(Ctx, TDD->getDecl(),
253
0
                                                   true /*FullyQualified*/,
254
0
                                                   WithGlobalNsPrefix);
255
0
      }
256
0
      return Scope;
257
0
    }
258
0
  }
259
0
  llvm_unreachable("bad NNS kind");
260
0
}
261
262
/// Create a nested name specifier for the declaring context of
263
/// the type.
264
static NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
265
    const ASTContext &Ctx, const Decl *Decl,
266
162
    bool FullyQualified, bool WithGlobalNsPrefix) {
267
162
  assert(Decl);
268
269
0
  const DeclContext *DC = Decl->getDeclContext()->getRedeclContext();
270
162
  const auto *Outer = dyn_cast_or_null<NamedDecl>(DC);
271
162
  const auto *OuterNS = dyn_cast_or_null<NamespaceDecl>(DC);
272
162
  if (Outer && 
!(72
OuterNS72
&&
OuterNS->isAnonymousNamespace()48
)) {
273
69
    if (const auto *CxxDecl = dyn_cast<CXXRecordDecl>(DC)) {
274
23
      if (ClassTemplateDecl *ClassTempl =
275
23
              CxxDecl->getDescribedClassTemplate()) {
276
        // We are in the case of a type(def) that was declared in a
277
        // class template but is *not* type dependent.  In clang, it
278
        // gets attached to the class template declaration rather than
279
        // any specific class template instantiation.  This result in
280
        // 'odd' fully qualified typename:
281
        //
282
        //    vector<_Tp,_Alloc>::size_type
283
        //
284
        // Make the situation is 'useable' but looking a bit odd by
285
        // picking a random instance as the declaring context.
286
2
        if (ClassTempl->spec_begin() != ClassTempl->spec_end()) {
287
2
          Decl = *(ClassTempl->spec_begin());
288
2
          Outer = dyn_cast<NamedDecl>(Decl);
289
2
          OuterNS = dyn_cast<NamespaceDecl>(Decl);
290
2
        }
291
2
      }
292
23
    }
293
294
69
    if (OuterNS) {
295
45
      return createNestedNameSpecifier(Ctx, OuterNS, WithGlobalNsPrefix);
296
45
    } else 
if (const auto *24
TD24
= dyn_cast<TagDecl>(Outer)) {
297
23
      return createNestedNameSpecifier(
298
23
          Ctx, TD, FullyQualified, WithGlobalNsPrefix);
299
23
    } else 
if (1
isa<TranslationUnitDecl>(Outer)1
) {
300
      // Context is the TU. Nothing needs to be done.
301
0
      return nullptr;
302
1
    } else {
303
      // Decl's context was neither the TU, a namespace, nor a
304
      // TagDecl, which means it is a type local to a scope, and not
305
      // accessible at the end of the TU.
306
1
      return nullptr;
307
1
    }
308
93
  } else if (WithGlobalNsPrefix && 
DC->isTranslationUnit()2
) {
309
1
    return NestedNameSpecifier::GlobalSpecifier(Ctx);
310
1
  }
311
92
  return nullptr;
312
162
}
313
314
/// Create a nested name specifier for the declaring context of
315
/// the type.
316
static NestedNameSpecifier *createNestedNameSpecifierForScopeOf(
317
    const ASTContext &Ctx, const Type *TypePtr,
318
185
    bool FullyQualified, bool WithGlobalNsPrefix) {
319
185
  if (!TypePtr) 
return nullptr0
;
320
321
185
  Decl *Decl = nullptr;
322
  // There are probably other cases ...
323
185
  if (const auto *TDT = dyn_cast<TypedefType>(TypePtr)) {
324
13
    Decl = TDT->getDecl();
325
172
  } else if (const auto *TagDeclType = dyn_cast<TagType>(TypePtr)) {
326
138
    Decl = TagDeclType->getDecl();
327
138
  } else 
if (const auto *34
TST34
= dyn_cast<TemplateSpecializationType>(TypePtr)) {
328
11
    Decl = TST->getTemplateName().getAsTemplateDecl();
329
23
  } else {
330
23
    Decl = TypePtr->getAsCXXRecordDecl();
331
23
  }
332
333
185
  if (!Decl) 
return nullptr23
;
334
335
162
  return createNestedNameSpecifierForScopeOf(
336
162
      Ctx, Decl, FullyQualified, WithGlobalNsPrefix);
337
185
}
338
339
NestedNameSpecifier *createNestedNameSpecifier(const ASTContext &Ctx,
340
                                               const NamespaceDecl *Namespace,
341
90
                                               bool WithGlobalNsPrefix) {
342
90
  while (Namespace && Namespace->isInline()) {
343
    // Ignore inline namespace;
344
0
    Namespace = dyn_cast<NamespaceDecl>(Namespace->getDeclContext());
345
0
  }
346
90
  if (!Namespace) 
return nullptr0
;
347
348
90
  bool FullyQualified = true;  // doesn't matter, DeclContexts are namespaces
349
90
  return NestedNameSpecifier::Create(
350
90
      Ctx,
351
90
      createOuterNNS(Ctx, Namespace, FullyQualified, WithGlobalNsPrefix),
352
90
      Namespace);
353
90
}
354
355
NestedNameSpecifier *createNestedNameSpecifier(const ASTContext &Ctx,
356
                                               const TypeDecl *TD,
357
                                               bool FullyQualify,
358
23
                                               bool WithGlobalNsPrefix) {
359
23
  const Type *TypePtr = TD->getTypeForDecl();
360
23
  if (isa<const TemplateSpecializationType>(TypePtr) ||
361
23
      isa<const RecordType>(TypePtr)) {
362
    // We are asked to fully qualify and we have a Record Type (which
363
    // may point to a template specialization) or Template
364
    // Specialization Type. We need to fully qualify their arguments.
365
366
23
    TypePtr = getFullyQualifiedTemplateType(Ctx, TypePtr, WithGlobalNsPrefix);
367
23
  }
368
369
23
  return NestedNameSpecifier::Create(
370
23
      Ctx, createOuterNNS(Ctx, TD, FullyQualify, WithGlobalNsPrefix),
371
23
      false /*No TemplateKeyword*/, TypePtr);
372
23
}
373
374
/// Return the fully qualified type, including fully-qualified
375
/// versions of any template parameters.
376
QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx,
377
191
                               bool WithGlobalNsPrefix) {
378
  // In case of myType* we need to strip the pointer first, fully
379
  // qualify and attach the pointer once again.
380
191
  if (isa<PointerType>(QT.getTypePtr())) {
381
    // Get the qualifiers.
382
2
    Qualifiers Quals = QT.getQualifiers();
383
2
    QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
384
2
    QT = Ctx.getPointerType(QT);
385
    // Add back the qualifiers.
386
2
    QT = Ctx.getQualifiedType(QT, Quals);
387
2
    return QT;
388
2
  }
389
390
189
  if (auto *MPT = dyn_cast<MemberPointerType>(QT.getTypePtr())) {
391
    // Get the qualifiers.
392
1
    Qualifiers Quals = QT.getQualifiers();
393
    // Fully qualify the pointee and class types.
394
1
    QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
395
1
    QualType Class = getFullyQualifiedType(QualType(MPT->getClass(), 0), Ctx,
396
1
                                           WithGlobalNsPrefix);
397
1
    QT = Ctx.getMemberPointerType(QT, Class.getTypePtr());
398
    // Add back the qualifiers.
399
1
    QT = Ctx.getQualifiedType(QT, Quals);
400
1
    return QT;
401
1
  }
402
403
  // In case of myType& we need to strip the reference first, fully
404
  // qualify and attach the reference once again.
405
188
  if (isa<ReferenceType>(QT.getTypePtr())) {
406
    // Get the qualifiers.
407
0
    bool IsLValueRefTy = isa<LValueReferenceType>(QT.getTypePtr());
408
0
    Qualifiers Quals = QT.getQualifiers();
409
0
    QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix);
410
    // Add the r- or l-value reference type back to the fully
411
    // qualified one.
412
0
    if (IsLValueRefTy)
413
0
      QT = Ctx.getLValueReferenceType(QT);
414
0
    else
415
0
      QT = Ctx.getRValueReferenceType(QT);
416
    // Add back the qualifiers.
417
0
    QT = Ctx.getQualifiedType(QT, Quals);
418
0
    return QT;
419
0
  }
420
421
  // We don't consider the alias introduced by `using a::X` as a new type.
422
  // The qualified name is still a::X.
423
188
  if (isa<UsingType>(QT.getTypePtr())) {
424
3
    return getFullyQualifiedType(QT.getSingleStepDesugaredType(Ctx), Ctx,
425
3
                                 WithGlobalNsPrefix);
426
3
  }
427
428
  // Remove the part of the type related to the type being a template
429
  // parameter (we won't report it as part of the 'type name' and it
430
  // is actually make the code below to be more complex (to handle
431
  // those)
432
185
  while (isa<SubstTemplateTypeParmType>(QT.getTypePtr())) {
433
    // Get the qualifiers.
434
0
    Qualifiers Quals = QT.getQualifiers();
435
436
0
    QT = cast<SubstTemplateTypeParmType>(QT.getTypePtr())->desugar();
437
438
    // Add back the qualifiers.
439
0
    QT = Ctx.getQualifiedType(QT, Quals);
440
0
  }
441
442
185
  NestedNameSpecifier *Prefix = nullptr;
443
  // Local qualifiers are attached to the QualType outside of the
444
  // elaborated type.  Retrieve them before descending into the
445
  // elaborated type.
446
185
  Qualifiers PrefixQualifiers = QT.getLocalQualifiers();
447
185
  QT = QualType(QT.getTypePtr(), 0);
448
185
  ElaboratedTypeKeyword Keyword = ETK_None;
449
185
  if (const auto *ETypeInput = dyn_cast<ElaboratedType>(QT.getTypePtr())) {
450
16
    QT = ETypeInput->getNamedType();
451
16
    assert(!QT.hasLocalQualifiers());
452
0
    Keyword = ETypeInput->getKeyword();
453
16
  }
454
  // Create a nested name specifier if needed.
455
0
  Prefix = createNestedNameSpecifierForScopeOf(Ctx, QT.getTypePtr(),
456
185
                                               true /*FullyQualified*/,
457
185
                                               WithGlobalNsPrefix);
458
459
  // In case of template specializations iterate over the arguments and
460
  // fully qualify them as well.
461
185
  if (isa<const TemplateSpecializationType>(QT.getTypePtr()) ||
462
185
      
isa<const RecordType>(QT.getTypePtr())174
) {
463
    // We are asked to fully qualify and we have a Record Type (which
464
    // may point to a template specialization) or Template
465
    // Specialization Type. We need to fully qualify their arguments.
466
467
33
    const Type *TypePtr = getFullyQualifiedTemplateType(
468
33
        Ctx, QT.getTypePtr(), WithGlobalNsPrefix);
469
33
    QT = QualType(TypePtr, 0);
470
33
  }
471
185
  if (Prefix || 
Keyword != ETK_None116
) {
472
71
    QT = Ctx.getElaboratedType(Keyword, Prefix, QT);
473
71
  }
474
185
  QT = Ctx.getQualifiedType(QT, PrefixQualifiers);
475
185
  return QT;
476
188
}
477
478
std::string getFullyQualifiedName(QualType QT,
479
                                  const ASTContext &Ctx,
480
                                  const PrintingPolicy &Policy,
481
38
                                  bool WithGlobalNsPrefix) {
482
38
  QualType FQQT = getFullyQualifiedType(QT, Ctx, WithGlobalNsPrefix);
483
38
  return FQQT.getAsString(Policy);
484
38
}
485
486
}  // end namespace TypeName
487
}  // end namespace clang