This introduces `__builtin_hlsl_resource_getpointer`, which lowers to `llvm.dx.resource.getpointer` and is used to implement indexing into resources. This will only work through the backend for typed buffers at this point, but the changes to structured buffers should be correct as far as the frontend is concerned. Note: We probably want this to return a reference in the HLSL device address space, but for now we're just using address space 0. Creating a device address space and updating this code can be done later as necessary. Fixes #95956
967 lines
38 KiB
C++
967 lines
38 KiB
C++
//===--- HLSLExternalSemaSource.cpp - HLSL Sema Source --------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Sema/HLSLExternalSemaSource.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Attr.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/DeclCXX.h"
|
|
#include "clang/AST/Expr.h"
|
|
#include "clang/AST/Type.h"
|
|
#include "clang/Basic/SourceLocation.h"
|
|
#include "clang/Sema/Lookup.h"
|
|
#include "clang/Sema/Sema.h"
|
|
#include "clang/Sema/SemaHLSL.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/Frontend/HLSL/HLSLResource.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
|
|
#include <functional>
|
|
|
|
using namespace clang;
|
|
using namespace llvm::hlsl;
|
|
|
|
static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name);
|
|
|
|
namespace {
|
|
|
|
struct TemplateParameterListBuilder;
|
|
|
|
struct BuiltinTypeDeclBuilder {
|
|
Sema &SemaRef;
|
|
CXXRecordDecl *Record = nullptr;
|
|
ClassTemplateDecl *Template = nullptr;
|
|
ClassTemplateDecl *PrevTemplate = nullptr;
|
|
NamespaceDecl *HLSLNamespace = nullptr;
|
|
llvm::StringMap<FieldDecl *> Fields;
|
|
|
|
BuiltinTypeDeclBuilder(Sema &SemaRef, CXXRecordDecl *R)
|
|
: SemaRef(SemaRef), Record(R) {
|
|
Record->startDefinition();
|
|
Template = Record->getDescribedClassTemplate();
|
|
}
|
|
|
|
BuiltinTypeDeclBuilder(Sema &SemaRef, NamespaceDecl *Namespace,
|
|
StringRef Name)
|
|
: SemaRef(SemaRef), HLSLNamespace(Namespace) {
|
|
ASTContext &AST = SemaRef.getASTContext();
|
|
IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
|
|
|
|
LookupResult Result(SemaRef, &II, SourceLocation(), Sema::LookupTagName);
|
|
CXXRecordDecl *PrevDecl = nullptr;
|
|
if (SemaRef.LookupQualifiedName(Result, HLSLNamespace)) {
|
|
// Declaration already exists (from precompiled headers)
|
|
NamedDecl *Found = Result.getFoundDecl();
|
|
if (auto *TD = dyn_cast<ClassTemplateDecl>(Found)) {
|
|
PrevDecl = TD->getTemplatedDecl();
|
|
PrevTemplate = TD;
|
|
} else
|
|
PrevDecl = dyn_cast<CXXRecordDecl>(Found);
|
|
assert(PrevDecl && "Unexpected lookup result type.");
|
|
}
|
|
|
|
if (PrevDecl && PrevDecl->isCompleteDefinition()) {
|
|
Record = PrevDecl;
|
|
Template = PrevTemplate;
|
|
return;
|
|
}
|
|
|
|
Record = CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, HLSLNamespace,
|
|
SourceLocation(), SourceLocation(), &II,
|
|
PrevDecl, true);
|
|
Record->setImplicit(true);
|
|
Record->setLexicalDeclContext(HLSLNamespace);
|
|
Record->setHasExternalLexicalStorage();
|
|
|
|
// Don't let anyone derive from built-in types.
|
|
Record->addAttr(FinalAttr::CreateImplicit(AST, SourceRange(),
|
|
FinalAttr::Keyword_final));
|
|
}
|
|
|
|
~BuiltinTypeDeclBuilder() {
|
|
if (HLSLNamespace && !Template && Record->getDeclContext() == HLSLNamespace)
|
|
HLSLNamespace->addDecl(Record);
|
|
}
|
|
|
|
BuiltinTypeDeclBuilder &
|
|
addMemberVariable(StringRef Name, QualType Type, llvm::ArrayRef<Attr *> Attrs,
|
|
AccessSpecifier Access = AccessSpecifier::AS_private) {
|
|
assert(!Record->isCompleteDefinition() && "record is already complete");
|
|
assert(Record->isBeingDefined() &&
|
|
"Definition must be started before adding members!");
|
|
ASTContext &AST = Record->getASTContext();
|
|
|
|
IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
|
|
TypeSourceInfo *MemTySource =
|
|
AST.getTrivialTypeSourceInfo(Type, SourceLocation());
|
|
auto *Field = FieldDecl::Create(
|
|
AST, Record, SourceLocation(), SourceLocation(), &II, Type, MemTySource,
|
|
nullptr, false, InClassInitStyle::ICIS_NoInit);
|
|
Field->setAccess(Access);
|
|
Field->setImplicit(true);
|
|
for (Attr *A : Attrs) {
|
|
if (A)
|
|
Field->addAttr(A);
|
|
}
|
|
|
|
Record->addDecl(Field);
|
|
Fields[Name] = Field;
|
|
return *this;
|
|
}
|
|
|
|
BuiltinTypeDeclBuilder &
|
|
addHandleMember(ResourceClass RC, ResourceKind RK, bool IsROV, bool RawBuffer,
|
|
AccessSpecifier Access = AccessSpecifier::AS_private) {
|
|
assert(!Record->isCompleteDefinition() && "record is already complete");
|
|
|
|
ASTContext &Ctx = SemaRef.getASTContext();
|
|
TypeSourceInfo *ElementTypeInfo =
|
|
Ctx.getTrivialTypeSourceInfo(getHandleElementType(), SourceLocation());
|
|
|
|
// add handle member with resource type attributes
|
|
QualType AttributedResTy = QualType();
|
|
SmallVector<const Attr *> Attrs = {
|
|
HLSLResourceClassAttr::CreateImplicit(Ctx, RC),
|
|
IsROV ? HLSLROVAttr::CreateImplicit(Ctx) : nullptr,
|
|
RawBuffer ? HLSLRawBufferAttr::CreateImplicit(Ctx) : nullptr,
|
|
ElementTypeInfo
|
|
? HLSLContainedTypeAttr::CreateImplicit(Ctx, ElementTypeInfo)
|
|
: nullptr};
|
|
Attr *ResourceAttr = HLSLResourceAttr::CreateImplicit(Ctx, RK);
|
|
if (CreateHLSLAttributedResourceType(SemaRef, Ctx.HLSLResourceTy, Attrs,
|
|
AttributedResTy))
|
|
addMemberVariable("__handle", AttributedResTy, {ResourceAttr}, Access);
|
|
return *this;
|
|
}
|
|
|
|
BuiltinTypeDeclBuilder &addDefaultHandleConstructor() {
|
|
if (Record->isCompleteDefinition())
|
|
return *this;
|
|
ASTContext &AST = Record->getASTContext();
|
|
|
|
QualType ConstructorType =
|
|
AST.getFunctionType(AST.VoidTy, {}, FunctionProtoType::ExtProtoInfo());
|
|
|
|
CanQualType CanTy = Record->getTypeForDecl()->getCanonicalTypeUnqualified();
|
|
DeclarationName Name = AST.DeclarationNames.getCXXConstructorName(CanTy);
|
|
CXXConstructorDecl *Constructor = CXXConstructorDecl::Create(
|
|
AST, Record, SourceLocation(),
|
|
DeclarationNameInfo(Name, SourceLocation()), ConstructorType,
|
|
AST.getTrivialTypeSourceInfo(ConstructorType, SourceLocation()),
|
|
ExplicitSpecifier(), false, true, false,
|
|
ConstexprSpecKind::Unspecified);
|
|
|
|
Constructor->setBody(CompoundStmt::Create(
|
|
AST, {}, FPOptionsOverride(), SourceLocation(), SourceLocation()));
|
|
Constructor->setAccess(AccessSpecifier::AS_public);
|
|
Record->addDecl(Constructor);
|
|
return *this;
|
|
}
|
|
|
|
BuiltinTypeDeclBuilder &addArraySubscriptOperators() {
|
|
ASTContext &AST = Record->getASTContext();
|
|
DeclarationName Subscript =
|
|
AST.DeclarationNames.getCXXOperatorName(OO_Subscript);
|
|
|
|
addHandleAccessFunction(Subscript, /*IsConst=*/true, /*IsRef=*/true);
|
|
addHandleAccessFunction(Subscript, /*IsConst=*/false, /*IsRef=*/true);
|
|
return *this;
|
|
}
|
|
|
|
FieldDecl *getResourceHandleField() {
|
|
auto I = Fields.find("__handle");
|
|
assert(I != Fields.end() &&
|
|
I->second->getType()->isHLSLAttributedResourceType() &&
|
|
"record does not have resource handle field");
|
|
return I->second;
|
|
}
|
|
|
|
QualType getFirstTemplateTypeParam() {
|
|
assert(Template && "record it not a template");
|
|
if (const auto *TTD = dyn_cast<TemplateTypeParmDecl>(
|
|
Template->getTemplateParameters()->getParam(0))) {
|
|
return QualType(TTD->getTypeForDecl(), 0);
|
|
}
|
|
return QualType();
|
|
}
|
|
|
|
QualType getHandleElementType() {
|
|
if (Template)
|
|
return getFirstTemplateTypeParam();
|
|
// TODO: Should we default to VoidTy? Using `i8` is arguably ambiguous.
|
|
return SemaRef.getASTContext().Char8Ty;
|
|
}
|
|
|
|
BuiltinTypeDeclBuilder &startDefinition() {
|
|
assert(!Record->isCompleteDefinition() && "record is already complete");
|
|
Record->startDefinition();
|
|
return *this;
|
|
}
|
|
|
|
BuiltinTypeDeclBuilder &completeDefinition() {
|
|
assert(!Record->isCompleteDefinition() && "record is already complete");
|
|
assert(Record->isBeingDefined() &&
|
|
"Definition must be started before completing it.");
|
|
|
|
Record->completeDefinition();
|
|
return *this;
|
|
}
|
|
|
|
Expr *getConstantIntExpr(int value) {
|
|
ASTContext &AST = SemaRef.getASTContext();
|
|
return IntegerLiteral::Create(
|
|
AST, llvm::APInt(AST.getTypeSize(AST.IntTy), value, true), AST.IntTy,
|
|
SourceLocation());
|
|
}
|
|
|
|
TemplateParameterListBuilder addTemplateArgumentList();
|
|
BuiltinTypeDeclBuilder &addSimpleTemplateParams(ArrayRef<StringRef> Names,
|
|
ConceptDecl *CD);
|
|
|
|
// Builtin types methods
|
|
BuiltinTypeDeclBuilder &addIncrementCounterMethod();
|
|
BuiltinTypeDeclBuilder &addDecrementCounterMethod();
|
|
BuiltinTypeDeclBuilder &addHandleAccessFunction(DeclarationName &Name,
|
|
bool IsConst, bool IsRef);
|
|
};
|
|
|
|
struct TemplateParameterListBuilder {
|
|
BuiltinTypeDeclBuilder &Builder;
|
|
llvm::SmallVector<NamedDecl *> Params;
|
|
|
|
TemplateParameterListBuilder(BuiltinTypeDeclBuilder &RB) : Builder(RB) {}
|
|
|
|
~TemplateParameterListBuilder() { finalizeTemplateArgs(); }
|
|
|
|
TemplateParameterListBuilder &
|
|
addTypeParameter(StringRef Name, QualType DefaultValue = QualType()) {
|
|
assert(!Builder.Record->isCompleteDefinition() &&
|
|
"record is already complete");
|
|
ASTContext &AST = Builder.SemaRef.getASTContext();
|
|
unsigned Position = static_cast<unsigned>(Params.size());
|
|
auto *Decl = TemplateTypeParmDecl::Create(
|
|
AST, Builder.Record->getDeclContext(), SourceLocation(),
|
|
SourceLocation(), /* TemplateDepth */ 0, Position,
|
|
&AST.Idents.get(Name, tok::TokenKind::identifier),
|
|
/* Typename */ true,
|
|
/* ParameterPack */ false,
|
|
/* HasTypeConstraint*/ false);
|
|
if (!DefaultValue.isNull())
|
|
Decl->setDefaultArgument(AST,
|
|
Builder.SemaRef.getTrivialTemplateArgumentLoc(
|
|
DefaultValue, QualType(), SourceLocation()));
|
|
|
|
Params.emplace_back(Decl);
|
|
return *this;
|
|
}
|
|
|
|
// The concept specialization expression (CSE) constructed in
|
|
// constructConceptSpecializationExpr is constructed so that it
|
|
// matches the CSE that is constructed when parsing the below C++ code:
|
|
//
|
|
// template<typename T>
|
|
// concept is_typed_resource_element_compatible =
|
|
// __builtin_hlsl_typed_resource_element_compatible<T>
|
|
//
|
|
// template<typename element_type> requires
|
|
// is_typed_resource_element_compatible<element_type>
|
|
// struct RWBuffer {
|
|
// element_type Val;
|
|
// };
|
|
//
|
|
// int fn() {
|
|
// RWBuffer<int> Buf;
|
|
// }
|
|
//
|
|
// When dumping the AST and filtering for "RWBuffer", the resulting AST
|
|
// structure is what we're trying to construct below, specifically the
|
|
// CSE portion.
|
|
ConceptSpecializationExpr *
|
|
constructConceptSpecializationExpr(Sema &S, ConceptDecl *CD) {
|
|
ASTContext &Context = S.getASTContext();
|
|
SourceLocation Loc = Builder.Record->getBeginLoc();
|
|
DeclarationNameInfo DNI(CD->getDeclName(), Loc);
|
|
NestedNameSpecifierLoc NNSLoc;
|
|
DeclContext *DC = Builder.Record->getDeclContext();
|
|
TemplateArgumentListInfo TALI(Loc, Loc);
|
|
|
|
// Assume that the concept decl has just one template parameter
|
|
// This parameter should have been added when CD was constructed
|
|
// in getTypedBufferConceptDecl
|
|
assert(CD->getTemplateParameters()->size() == 1 &&
|
|
"unexpected concept decl parameter count");
|
|
TemplateTypeParmDecl *ConceptTTPD = dyn_cast<TemplateTypeParmDecl>(
|
|
CD->getTemplateParameters()->getParam(0));
|
|
|
|
// this TemplateTypeParmDecl is the template for the resource, and is
|
|
// used to construct a template argumentthat will be used
|
|
// to construct the ImplicitConceptSpecializationDecl
|
|
TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create(
|
|
Context, // AST context
|
|
Builder.Record->getDeclContext(), // DeclContext
|
|
SourceLocation(), SourceLocation(),
|
|
/*depth=*/0, // Depth in the template parameter list
|
|
/*position=*/0, // Position in the template parameter list
|
|
/*id=*/nullptr, // Identifier for 'T'
|
|
/*Typename=*/true, // Indicates this is a 'typename' or 'class'
|
|
/*ParameterPack=*/false, // Not a parameter pack
|
|
/*HasTypeConstraint=*/false // Has no type constraint
|
|
);
|
|
|
|
T->setDeclContext(DC);
|
|
|
|
QualType ConceptTType = Context.getTypeDeclType(ConceptTTPD);
|
|
|
|
// this is the 2nd template argument node, on which
|
|
// the concept constraint is actually being applied: 'element_type'
|
|
TemplateArgument ConceptTA = TemplateArgument(ConceptTType);
|
|
|
|
QualType CSETType = Context.getTypeDeclType(T);
|
|
|
|
// this is the 1st template argument node, which represents
|
|
// the abstract type that a concept would refer to: 'T'
|
|
TemplateArgument CSETA = TemplateArgument(CSETType);
|
|
|
|
ImplicitConceptSpecializationDecl *ImplicitCSEDecl =
|
|
ImplicitConceptSpecializationDecl::Create(
|
|
Context, Builder.Record->getDeclContext(), Loc, {CSETA});
|
|
|
|
// Constraint satisfaction is used to construct the
|
|
// ConceptSpecailizationExpr, and represents the 2nd Template Argument,
|
|
// located at the bottom of the sample AST above.
|
|
const ConstraintSatisfaction CS(CD, {ConceptTA});
|
|
TemplateArgumentLoc TAL = S.getTrivialTemplateArgumentLoc(
|
|
ConceptTA, QualType(), SourceLocation());
|
|
|
|
TALI.addArgument(TAL);
|
|
const ASTTemplateArgumentListInfo *ATALI =
|
|
ASTTemplateArgumentListInfo::Create(Context, TALI);
|
|
|
|
// In the concept reference, ATALI is what adds the extra
|
|
// TemplateArgument node underneath CSE
|
|
ConceptReference *CR =
|
|
ConceptReference::Create(Context, NNSLoc, Loc, DNI, CD, CD, ATALI);
|
|
|
|
ConceptSpecializationExpr *CSE =
|
|
ConceptSpecializationExpr::Create(Context, CR, ImplicitCSEDecl, &CS);
|
|
|
|
return CSE;
|
|
}
|
|
|
|
BuiltinTypeDeclBuilder &finalizeTemplateArgs(ConceptDecl *CD = nullptr) {
|
|
if (Params.empty())
|
|
return Builder;
|
|
|
|
ASTContext &AST = Builder.SemaRef.Context;
|
|
ConceptSpecializationExpr *CSE =
|
|
CD ? constructConceptSpecializationExpr(Builder.SemaRef, CD) : nullptr;
|
|
auto *ParamList = TemplateParameterList::Create(
|
|
AST, SourceLocation(), SourceLocation(), Params, SourceLocation(), CSE);
|
|
Builder.Template = ClassTemplateDecl::Create(
|
|
AST, Builder.Record->getDeclContext(), SourceLocation(),
|
|
DeclarationName(Builder.Record->getIdentifier()), ParamList,
|
|
Builder.Record);
|
|
|
|
Builder.Record->setDescribedClassTemplate(Builder.Template);
|
|
Builder.Template->setImplicit(true);
|
|
Builder.Template->setLexicalDeclContext(Builder.Record->getDeclContext());
|
|
|
|
// NOTE: setPreviousDecl before addDecl so new decl replace old decl when
|
|
// make visible.
|
|
Builder.Template->setPreviousDecl(Builder.PrevTemplate);
|
|
Builder.Record->getDeclContext()->addDecl(Builder.Template);
|
|
Params.clear();
|
|
|
|
QualType T = Builder.Template->getInjectedClassNameSpecialization();
|
|
T = AST.getInjectedClassNameType(Builder.Record, T);
|
|
|
|
return Builder;
|
|
}
|
|
};
|
|
|
|
// Builder for methods of builtin types. Allows adding methods to builtin types
|
|
// using the builder pattern like this:
|
|
//
|
|
// BuiltinTypeMethodBuilder(RecordBuilder, "MethodName", ReturnType)
|
|
// .addParam("param_name", Type, InOutModifier)
|
|
// .callBuiltin("builtin_name", BuiltinParams...)
|
|
// .finalizeMethod();
|
|
//
|
|
// The builder needs to have all of the method parameters before it can create
|
|
// a CXXMethodDecl. It collects them in addParam calls and when a first
|
|
// method that builds the body is called or when access to 'this` is needed it
|
|
// creates the CXXMethodDecl and ParmVarDecls instances. These can then be
|
|
// referenced from the body building methods. Destructor or an explicit call to
|
|
// finalizeMethod() will complete the method definition.
|
|
//
|
|
// The callBuiltin helper method accepts constants via `Expr *` or placeholder
|
|
// value arguments to indicate which function arguments to forward to the
|
|
// builtin.
|
|
//
|
|
// If the method that is being built has a non-void return type the
|
|
// finalizeMethod will create a return statent with the value of the last
|
|
// statement (unless the last statement is already a ReturnStmt).
|
|
struct BuiltinTypeMethodBuilder {
|
|
struct MethodParam {
|
|
const IdentifierInfo &NameII;
|
|
QualType Ty;
|
|
HLSLParamModifierAttr::Spelling Modifier;
|
|
MethodParam(const IdentifierInfo &NameII, QualType Ty,
|
|
HLSLParamModifierAttr::Spelling Modifier)
|
|
: NameII(NameII), Ty(Ty), Modifier(Modifier) {}
|
|
};
|
|
|
|
BuiltinTypeDeclBuilder &DeclBuilder;
|
|
DeclarationNameInfo NameInfo;
|
|
QualType ReturnTy;
|
|
CXXMethodDecl *Method;
|
|
bool IsConst;
|
|
llvm::SmallVector<MethodParam> Params;
|
|
llvm::SmallVector<Stmt *> StmtsList;
|
|
|
|
// Argument placeholders, inspired by std::placeholder. These are the indices
|
|
// of arguments to forward to `callBuiltin`, and additionally `Handle` which
|
|
// refers to the resource handle.
|
|
enum class PlaceHolder { _0, _1, _2, _3, Handle = 127 };
|
|
|
|
Expr *convertPlaceholder(PlaceHolder PH) {
|
|
if (PH == PlaceHolder::Handle)
|
|
return getResourceHandleExpr();
|
|
|
|
ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
|
|
ParmVarDecl *ParamDecl = Method->getParamDecl(static_cast<unsigned>(PH));
|
|
return DeclRefExpr::Create(
|
|
AST, NestedNameSpecifierLoc(), SourceLocation(), ParamDecl, false,
|
|
DeclarationNameInfo(ParamDecl->getDeclName(), SourceLocation()),
|
|
ParamDecl->getType(), VK_PRValue);
|
|
}
|
|
Expr *convertPlaceholder(Expr *E) { return E; }
|
|
|
|
public:
|
|
BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, DeclarationName &Name,
|
|
QualType ReturnTy, bool IsConst = false)
|
|
: DeclBuilder(DB), NameInfo(DeclarationNameInfo(Name, SourceLocation())),
|
|
ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst) {}
|
|
|
|
BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, StringRef Name,
|
|
QualType ReturnTy, bool IsConst = false)
|
|
: DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst) {
|
|
const IdentifierInfo &II =
|
|
DB.SemaRef.getASTContext().Idents.get(Name, tok::TokenKind::identifier);
|
|
NameInfo = DeclarationNameInfo(DeclarationName(&II), SourceLocation());
|
|
}
|
|
|
|
BuiltinTypeMethodBuilder &addParam(StringRef Name, QualType Ty,
|
|
HLSLParamModifierAttr::Spelling Modifier =
|
|
HLSLParamModifierAttr::Keyword_in) {
|
|
assert(Method == nullptr && "Cannot add param, method already created");
|
|
const IdentifierInfo &II = DeclBuilder.SemaRef.getASTContext().Idents.get(
|
|
Name, tok::TokenKind::identifier);
|
|
Params.emplace_back(II, Ty, Modifier);
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
void createMethodDecl() {
|
|
assert(Method == nullptr && "Method already created");
|
|
|
|
// create method type
|
|
ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
|
|
SmallVector<QualType> ParamTypes;
|
|
for (MethodParam &MP : Params)
|
|
ParamTypes.emplace_back(MP.Ty);
|
|
|
|
FunctionProtoType::ExtProtoInfo ExtInfo;
|
|
if (IsConst)
|
|
ExtInfo.TypeQuals.addConst();
|
|
|
|
QualType MethodTy = AST.getFunctionType(ReturnTy, ParamTypes, ExtInfo);
|
|
|
|
// create method decl
|
|
auto *TSInfo = AST.getTrivialTypeSourceInfo(MethodTy, SourceLocation());
|
|
Method =
|
|
CXXMethodDecl::Create(AST, DeclBuilder.Record, SourceLocation(),
|
|
NameInfo, MethodTy, TSInfo, SC_None, false, false,
|
|
ConstexprSpecKind::Unspecified, SourceLocation());
|
|
|
|
// create params & set them to the function prototype
|
|
SmallVector<ParmVarDecl *> ParmDecls;
|
|
auto FnProtoLoc =
|
|
Method->getTypeSourceInfo()->getTypeLoc().getAs<FunctionProtoTypeLoc>();
|
|
for (int I = 0, E = Params.size(); I != E; I++) {
|
|
MethodParam &MP = Params[I];
|
|
ParmVarDecl *Parm = ParmVarDecl::Create(
|
|
AST, Method->getDeclContext(), SourceLocation(), SourceLocation(),
|
|
&MP.NameII, MP.Ty,
|
|
AST.getTrivialTypeSourceInfo(MP.Ty, SourceLocation()), SC_None,
|
|
nullptr);
|
|
if (MP.Modifier != HLSLParamModifierAttr::Keyword_in) {
|
|
auto *Mod =
|
|
HLSLParamModifierAttr::Create(AST, SourceRange(), MP.Modifier);
|
|
Parm->addAttr(Mod);
|
|
}
|
|
ParmDecls.push_back(Parm);
|
|
FnProtoLoc.setParam(I, Parm);
|
|
}
|
|
Method->setParams({ParmDecls});
|
|
}
|
|
|
|
public:
|
|
~BuiltinTypeMethodBuilder() { finalizeMethod(); }
|
|
|
|
Expr *getResourceHandleExpr() {
|
|
// The first statement added to a method or access to 'this' creates the
|
|
// declaration.
|
|
if (!Method)
|
|
createMethodDecl();
|
|
|
|
ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
|
|
CXXThisExpr *This = CXXThisExpr::Create(
|
|
AST, SourceLocation(), Method->getFunctionObjectParameterType(), true);
|
|
FieldDecl *HandleField = DeclBuilder.getResourceHandleField();
|
|
return MemberExpr::CreateImplicit(AST, This, false, HandleField,
|
|
HandleField->getType(), VK_LValue,
|
|
OK_Ordinary);
|
|
}
|
|
|
|
template <typename... Ts>
|
|
BuiltinTypeMethodBuilder &callBuiltin(StringRef BuiltinName,
|
|
QualType ReturnType, Ts... ArgSpecs) {
|
|
std::array<Expr *, sizeof...(ArgSpecs)> Args{
|
|
convertPlaceholder(std::forward<Ts>(ArgSpecs))...};
|
|
|
|
// The first statement added to a method or access to 'this` creates the
|
|
// declaration.
|
|
if (!Method)
|
|
createMethodDecl();
|
|
|
|
ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
|
|
FunctionDecl *FD = lookupBuiltinFunction(DeclBuilder.SemaRef, BuiltinName);
|
|
DeclRefExpr *DRE = DeclRefExpr::Create(
|
|
AST, NestedNameSpecifierLoc(), SourceLocation(), FD, false,
|
|
FD->getNameInfo(), AST.BuiltinFnTy, VK_PRValue);
|
|
|
|
if (ReturnType.isNull())
|
|
ReturnType = FD->getReturnType();
|
|
|
|
Expr *Call = CallExpr::Create(AST, DRE, Args, ReturnType, VK_PRValue,
|
|
SourceLocation(), FPOptionsOverride());
|
|
StmtsList.push_back(Call);
|
|
return *this;
|
|
}
|
|
|
|
BuiltinTypeMethodBuilder &dereference() {
|
|
assert(!StmtsList.empty() && "Nothing to dereference");
|
|
ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
|
|
|
|
Expr *LastExpr = dyn_cast<Expr>(StmtsList.back());
|
|
assert(LastExpr && "No expression to dereference");
|
|
Expr *Deref = UnaryOperator::Create(
|
|
AST, LastExpr, UO_Deref, LastExpr->getType()->getPointeeType(),
|
|
VK_PRValue, OK_Ordinary, SourceLocation(),
|
|
/*CanOverflow=*/false, FPOptionsOverride());
|
|
StmtsList.pop_back();
|
|
StmtsList.push_back(Deref);
|
|
return *this;
|
|
}
|
|
|
|
BuiltinTypeDeclBuilder &finalizeMethod() {
|
|
assert(!DeclBuilder.Record->isCompleteDefinition() &&
|
|
"record is already complete");
|
|
assert(
|
|
Method != nullptr &&
|
|
"method decl not created; are you missing a call to build the body?");
|
|
|
|
if (!Method->hasBody()) {
|
|
ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
|
|
assert((ReturnTy == AST.VoidTy || !StmtsList.empty()) &&
|
|
"nothing to return from non-void method");
|
|
if (ReturnTy != AST.VoidTy) {
|
|
if (Expr *LastExpr = dyn_cast<Expr>(StmtsList.back())) {
|
|
assert(AST.hasSameUnqualifiedType(LastExpr->getType(),
|
|
ReturnTy.getNonReferenceType()) &&
|
|
"Return type of the last statement must match the return type "
|
|
"of the method");
|
|
if (!isa<ReturnStmt>(LastExpr)) {
|
|
StmtsList.pop_back();
|
|
StmtsList.push_back(
|
|
ReturnStmt::Create(AST, SourceLocation(), LastExpr, nullptr));
|
|
}
|
|
}
|
|
}
|
|
|
|
Method->setBody(CompoundStmt::Create(AST, StmtsList, FPOptionsOverride(),
|
|
SourceLocation(), SourceLocation()));
|
|
Method->setLexicalDeclContext(DeclBuilder.Record);
|
|
Method->setAccess(AccessSpecifier::AS_public);
|
|
Method->addAttr(AlwaysInlineAttr::CreateImplicit(
|
|
AST, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline));
|
|
DeclBuilder.Record->addDecl(Method);
|
|
}
|
|
return DeclBuilder;
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TemplateParameterListBuilder BuiltinTypeDeclBuilder::addTemplateArgumentList() {
|
|
return TemplateParameterListBuilder(*this);
|
|
}
|
|
|
|
BuiltinTypeDeclBuilder &
|
|
BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
|
|
ConceptDecl *CD = nullptr) {
|
|
if (Record->isCompleteDefinition()) {
|
|
assert(Template && "existing record it not a template");
|
|
assert(Template->getTemplateParameters()->size() == Names.size() &&
|
|
"template param count mismatch");
|
|
return *this;
|
|
}
|
|
|
|
TemplateParameterListBuilder Builder = this->addTemplateArgumentList();
|
|
for (StringRef Name : Names)
|
|
Builder.addTypeParameter(Name);
|
|
return Builder.finalizeTemplateArgs(CD);
|
|
}
|
|
|
|
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() {
|
|
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
|
|
return BuiltinTypeMethodBuilder(*this, "IncrementCounter",
|
|
SemaRef.getASTContext().UnsignedIntTy)
|
|
.callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
|
|
PH::Handle, getConstantIntExpr(1))
|
|
.finalizeMethod();
|
|
}
|
|
|
|
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() {
|
|
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
|
|
return BuiltinTypeMethodBuilder(*this, "DecrementCounter",
|
|
SemaRef.getASTContext().UnsignedIntTy)
|
|
.callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(),
|
|
PH::Handle, getConstantIntExpr(-1))
|
|
.finalizeMethod();
|
|
}
|
|
|
|
BuiltinTypeDeclBuilder &
|
|
BuiltinTypeDeclBuilder::addHandleAccessFunction(DeclarationName &Name,
|
|
bool IsConst, bool IsRef) {
|
|
assert(!Record->isCompleteDefinition() && "record is already complete");
|
|
ASTContext &AST = SemaRef.getASTContext();
|
|
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
|
|
|
|
QualType ElemTy = getHandleElementType();
|
|
// TODO: Map to an hlsl_device address space.
|
|
QualType ElemPtrTy = AST.getPointerType(ElemTy);
|
|
QualType ReturnTy = ElemTy;
|
|
if (IsConst)
|
|
ReturnTy.addConst();
|
|
if (IsRef)
|
|
ReturnTy = AST.getLValueReferenceType(ReturnTy);
|
|
|
|
return BuiltinTypeMethodBuilder(*this, Name, ReturnTy, IsConst)
|
|
.addParam("Index", AST.UnsignedIntTy)
|
|
.callBuiltin("__builtin_hlsl_resource_getpointer", ElemPtrTy, PH::Handle,
|
|
PH::_0)
|
|
.dereference()
|
|
.finalizeMethod();
|
|
}
|
|
|
|
HLSLExternalSemaSource::~HLSLExternalSemaSource() {}
|
|
|
|
void HLSLExternalSemaSource::InitializeSema(Sema &S) {
|
|
SemaPtr = &S;
|
|
ASTContext &AST = SemaPtr->getASTContext();
|
|
// If the translation unit has external storage force external decls to load.
|
|
if (AST.getTranslationUnitDecl()->hasExternalLexicalStorage())
|
|
(void)AST.getTranslationUnitDecl()->decls_begin();
|
|
|
|
IdentifierInfo &HLSL = AST.Idents.get("hlsl", tok::TokenKind::identifier);
|
|
LookupResult Result(S, &HLSL, SourceLocation(), Sema::LookupNamespaceName);
|
|
NamespaceDecl *PrevDecl = nullptr;
|
|
if (S.LookupQualifiedName(Result, AST.getTranslationUnitDecl()))
|
|
PrevDecl = Result.getAsSingle<NamespaceDecl>();
|
|
HLSLNamespace = NamespaceDecl::Create(
|
|
AST, AST.getTranslationUnitDecl(), /*Inline=*/false, SourceLocation(),
|
|
SourceLocation(), &HLSL, PrevDecl, /*Nested=*/false);
|
|
HLSLNamespace->setImplicit(true);
|
|
HLSLNamespace->setHasExternalLexicalStorage();
|
|
AST.getTranslationUnitDecl()->addDecl(HLSLNamespace);
|
|
|
|
// Force external decls in the HLSL namespace to load from the PCH.
|
|
(void)HLSLNamespace->getCanonicalDecl()->decls_begin();
|
|
defineTrivialHLSLTypes();
|
|
defineHLSLTypesWithForwardDeclarations();
|
|
|
|
// This adds a `using namespace hlsl` directive. In DXC, we don't put HLSL's
|
|
// built in types inside a namespace, but we are planning to change that in
|
|
// the near future. In order to be source compatible older versions of HLSL
|
|
// will need to implicitly use the hlsl namespace. For now in clang everything
|
|
// will get added to the namespace, and we can remove the using directive for
|
|
// future language versions to match HLSL's evolution.
|
|
auto *UsingDecl = UsingDirectiveDecl::Create(
|
|
AST, AST.getTranslationUnitDecl(), SourceLocation(), SourceLocation(),
|
|
NestedNameSpecifierLoc(), SourceLocation(), HLSLNamespace,
|
|
AST.getTranslationUnitDecl());
|
|
|
|
AST.getTranslationUnitDecl()->addDecl(UsingDecl);
|
|
}
|
|
|
|
void HLSLExternalSemaSource::defineHLSLVectorAlias() {
|
|
ASTContext &AST = SemaPtr->getASTContext();
|
|
|
|
llvm::SmallVector<NamedDecl *> TemplateParams;
|
|
|
|
auto *TypeParam = TemplateTypeParmDecl::Create(
|
|
AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 0,
|
|
&AST.Idents.get("element", tok::TokenKind::identifier), false, false);
|
|
TypeParam->setDefaultArgument(
|
|
AST, SemaPtr->getTrivialTemplateArgumentLoc(
|
|
TemplateArgument(AST.FloatTy), QualType(), SourceLocation()));
|
|
|
|
TemplateParams.emplace_back(TypeParam);
|
|
|
|
auto *SizeParam = NonTypeTemplateParmDecl::Create(
|
|
AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 1,
|
|
&AST.Idents.get("element_count", tok::TokenKind::identifier), AST.IntTy,
|
|
false, AST.getTrivialTypeSourceInfo(AST.IntTy));
|
|
llvm::APInt Val(AST.getIntWidth(AST.IntTy), 4);
|
|
TemplateArgument Default(AST, llvm::APSInt(std::move(Val)), AST.IntTy,
|
|
/*IsDefaulted=*/true);
|
|
SizeParam->setDefaultArgument(
|
|
AST, SemaPtr->getTrivialTemplateArgumentLoc(Default, AST.IntTy,
|
|
SourceLocation(), SizeParam));
|
|
TemplateParams.emplace_back(SizeParam);
|
|
|
|
auto *ParamList =
|
|
TemplateParameterList::Create(AST, SourceLocation(), SourceLocation(),
|
|
TemplateParams, SourceLocation(), nullptr);
|
|
|
|
IdentifierInfo &II = AST.Idents.get("vector", tok::TokenKind::identifier);
|
|
|
|
QualType AliasType = AST.getDependentSizedExtVectorType(
|
|
AST.getTemplateTypeParmType(0, 0, false, TypeParam),
|
|
DeclRefExpr::Create(
|
|
AST, NestedNameSpecifierLoc(), SourceLocation(), SizeParam, false,
|
|
DeclarationNameInfo(SizeParam->getDeclName(), SourceLocation()),
|
|
AST.IntTy, VK_LValue),
|
|
SourceLocation());
|
|
|
|
auto *Record = TypeAliasDecl::Create(AST, HLSLNamespace, SourceLocation(),
|
|
SourceLocation(), &II,
|
|
AST.getTrivialTypeSourceInfo(AliasType));
|
|
Record->setImplicit(true);
|
|
|
|
auto *Template =
|
|
TypeAliasTemplateDecl::Create(AST, HLSLNamespace, SourceLocation(),
|
|
Record->getIdentifier(), ParamList, Record);
|
|
|
|
Record->setDescribedAliasTemplate(Template);
|
|
Template->setImplicit(true);
|
|
Template->setLexicalDeclContext(Record->getDeclContext());
|
|
HLSLNamespace->addDecl(Template);
|
|
}
|
|
|
|
void HLSLExternalSemaSource::defineTrivialHLSLTypes() {
|
|
defineHLSLVectorAlias();
|
|
}
|
|
|
|
/// Set up common members and attributes for buffer types
|
|
static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
|
|
ResourceClass RC, ResourceKind RK,
|
|
bool IsROV, bool RawBuffer) {
|
|
return BuiltinTypeDeclBuilder(S, Decl)
|
|
.addHandleMember(RC, RK, IsROV, RawBuffer)
|
|
.addDefaultHandleConstructor();
|
|
}
|
|
|
|
static Expr *constructTypedBufferConstraintExpr(Sema &S, SourceLocation NameLoc,
|
|
TemplateTypeParmDecl *T) {
|
|
ASTContext &Context = S.getASTContext();
|
|
|
|
// Obtain the QualType for 'unsigned long'
|
|
QualType BoolTy = Context.BoolTy;
|
|
|
|
// Create a QualType that points to this TemplateTypeParmDecl
|
|
QualType TType = Context.getTypeDeclType(T);
|
|
|
|
// Create a TypeSourceInfo for the template type parameter 'T'
|
|
TypeSourceInfo *TTypeSourceInfo =
|
|
Context.getTrivialTypeSourceInfo(TType, NameLoc);
|
|
|
|
TypeTraitExpr *TypedResExpr = TypeTraitExpr::Create(
|
|
Context, BoolTy, NameLoc, UTT_IsTypedResourceElementCompatible,
|
|
{TTypeSourceInfo}, NameLoc, true);
|
|
|
|
return TypedResExpr;
|
|
}
|
|
|
|
static ConceptDecl *constructTypedBufferConceptDecl(Sema &S,
|
|
NamespaceDecl *NSD) {
|
|
ASTContext &Context = S.getASTContext();
|
|
DeclContext *DC = NSD->getDeclContext();
|
|
SourceLocation DeclLoc = SourceLocation();
|
|
|
|
IdentifierInfo &ElementTypeII = Context.Idents.get("element_type");
|
|
TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create(
|
|
Context, NSD->getDeclContext(), DeclLoc, DeclLoc,
|
|
/*depth=*/0,
|
|
/*position=*/0,
|
|
/*id=*/&ElementTypeII,
|
|
/*Typename=*/true,
|
|
/*ParameterPack=*/false);
|
|
|
|
T->setDeclContext(DC);
|
|
T->setReferenced();
|
|
|
|
// Create and Attach Template Parameter List to ConceptDecl
|
|
TemplateParameterList *ConceptParams = TemplateParameterList::Create(
|
|
Context, DeclLoc, DeclLoc, {T}, DeclLoc, nullptr);
|
|
|
|
DeclarationName DeclName = DeclarationName(
|
|
&Context.Idents.get("__is_typed_resource_element_compatible"));
|
|
Expr *ConstraintExpr = constructTypedBufferConstraintExpr(S, DeclLoc, T);
|
|
|
|
// Create a ConceptDecl
|
|
ConceptDecl *CD =
|
|
ConceptDecl::Create(Context, NSD->getDeclContext(), DeclLoc, DeclName,
|
|
ConceptParams, ConstraintExpr);
|
|
|
|
// Attach the template parameter list to the ConceptDecl
|
|
CD->setTemplateParameters(ConceptParams);
|
|
|
|
// Add the concept declaration to the Translation Unit Decl
|
|
NSD->getDeclContext()->addDecl(CD);
|
|
|
|
return CD;
|
|
}
|
|
|
|
void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
|
|
CXXRecordDecl *Decl;
|
|
ConceptDecl *TypedBufferConcept =
|
|
constructTypedBufferConceptDecl(*SemaPtr, HLSLNamespace);
|
|
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
|
|
.addSimpleTemplateParams({"element_type"}, TypedBufferConcept)
|
|
.Record;
|
|
|
|
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
|
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
|
|
ResourceKind::TypedBuffer, /*IsROV=*/false,
|
|
/*RawBuffer=*/false)
|
|
.addArraySubscriptOperators()
|
|
.completeDefinition();
|
|
});
|
|
|
|
Decl =
|
|
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RasterizerOrderedBuffer")
|
|
.addSimpleTemplateParams({"element_type"})
|
|
.Record;
|
|
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
|
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
|
|
ResourceKind::TypedBuffer, /*IsROV=*/true,
|
|
/*RawBuffer=*/false)
|
|
.addArraySubscriptOperators()
|
|
.completeDefinition();
|
|
});
|
|
|
|
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "StructuredBuffer")
|
|
.addSimpleTemplateParams({"element_type"})
|
|
.Record;
|
|
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
|
setupBufferType(Decl, *SemaPtr, ResourceClass::SRV, ResourceKind::RawBuffer,
|
|
/*IsROV=*/false, /*RawBuffer=*/true)
|
|
.addArraySubscriptOperators()
|
|
.completeDefinition();
|
|
});
|
|
|
|
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWStructuredBuffer")
|
|
.addSimpleTemplateParams({"element_type"})
|
|
.Record;
|
|
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
|
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,
|
|
/*IsROV=*/false, /*RawBuffer=*/true)
|
|
.addArraySubscriptOperators()
|
|
.addIncrementCounterMethod()
|
|
.addDecrementCounterMethod()
|
|
.completeDefinition();
|
|
});
|
|
|
|
Decl =
|
|
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "AppendStructuredBuffer")
|
|
.addSimpleTemplateParams({"element_type"})
|
|
.Record;
|
|
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
|
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,
|
|
/*IsROV=*/false, /*RawBuffer=*/true)
|
|
.completeDefinition();
|
|
});
|
|
|
|
Decl =
|
|
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "ConsumeStructuredBuffer")
|
|
.addSimpleTemplateParams({"element_type"})
|
|
.Record;
|
|
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
|
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,
|
|
/*IsROV=*/false, /*RawBuffer=*/true)
|
|
.completeDefinition();
|
|
});
|
|
|
|
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace,
|
|
"RasterizerOrderedStructuredBuffer")
|
|
.addSimpleTemplateParams({"element_type"})
|
|
.Record;
|
|
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
|
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,
|
|
/*IsROV=*/true, /*RawBuffer=*/true)
|
|
.addArraySubscriptOperators()
|
|
.addIncrementCounterMethod()
|
|
.addDecrementCounterMethod()
|
|
.completeDefinition();
|
|
});
|
|
}
|
|
|
|
void HLSLExternalSemaSource::onCompletion(CXXRecordDecl *Record,
|
|
CompletionFunction Fn) {
|
|
if (!Record->isCompleteDefinition())
|
|
Completions.insert(std::make_pair(Record->getCanonicalDecl(), Fn));
|
|
}
|
|
|
|
void HLSLExternalSemaSource::CompleteType(TagDecl *Tag) {
|
|
if (!isa<CXXRecordDecl>(Tag))
|
|
return;
|
|
auto Record = cast<CXXRecordDecl>(Tag);
|
|
|
|
// If this is a specialization, we need to get the underlying templated
|
|
// declaration and complete that.
|
|
if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(Record))
|
|
Record = TDecl->getSpecializedTemplate()->getTemplatedDecl();
|
|
Record = Record->getCanonicalDecl();
|
|
auto It = Completions.find(Record);
|
|
if (It == Completions.end())
|
|
return;
|
|
It->second(Record);
|
|
}
|
|
|
|
static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name) {
|
|
IdentifierInfo &II =
|
|
S.getASTContext().Idents.get(Name, tok::TokenKind::identifier);
|
|
DeclarationNameInfo NameInfo =
|
|
DeclarationNameInfo(DeclarationName(&II), SourceLocation());
|
|
LookupResult R(S, NameInfo, Sema::LookupOrdinaryName);
|
|
// AllowBuiltinCreation is false but LookupDirect will create
|
|
// the builtin when searching the global scope anyways...
|
|
S.LookupName(R, S.getCurScope());
|
|
// FIXME: If the builtin function was user-declared in global scope,
|
|
// this assert *will* fail. Should this call LookupBuiltin instead?
|
|
assert(R.isSingleResult() &&
|
|
"Since this is a builtin it should always resolve!");
|
|
return cast<FunctionDecl>(R.getFoundDecl());
|
|
}
|