Skip to content

Commit d4ff9ef

Browse files
committed
[clang][ASTImporter] Improve import of functions with auto return type.
ASTImporter used to crash in some cases when a function is imported with `auto` return type and the return type has references into the function. The handling of such cases is improved and crash should not occur any more but it is not fully verified, there are very many different types of cases to care for. Reviewed By: martong Differential Revision: https://reviews.llvm.org/D130705
1 parent 5146f84 commit d4ff9ef

File tree

2 files changed

+429
-133
lines changed

2 files changed

+429
-133
lines changed

clang/lib/AST/ASTImporter.cpp

+144-36
Original file line numberDiff line numberDiff line change
@@ -3248,56 +3248,164 @@ static bool isAncestorDeclContextOf(const DeclContext *DC, const Stmt *S) {
32483248
return false;
32493249
}
32503250

3251-
static bool hasTypeDeclaredInsideFunction(QualType T, const FunctionDecl *FD) {
3252-
if (T.isNull())
3251+
namespace {
3252+
/// Check if a type has any reference to a declaration that is inside the body
3253+
/// of a function.
3254+
/// The \c CheckType(QualType) function should be used to determine
3255+
/// this property.
3256+
///
3257+
/// The type visitor visits one type object only (not recursive).
3258+
/// To find all referenced declarations we must discover all type objects until
3259+
/// the canonical type is reached (walk over typedef and similar objects). This
3260+
/// is done by loop over all "sugar" type objects. For every such type we must
3261+
/// check all declarations that are referenced from it. For this check the
3262+
/// visitor is used. In the visit functions all referenced declarations except
3263+
/// the one that follows in the sugar chain (if any) must be checked. For this
3264+
/// check the same visitor is re-used (it has no state-dependent data).
3265+
///
3266+
/// The visit functions have 3 possible return values:
3267+
/// - True, found a declaration inside \c ParentDC.
3268+
/// - False, found declarations only outside \c ParentDC and it is not possible
3269+
/// to find more declarations (the "sugar" chain does not continue).
3270+
/// - Empty optional value, found no declarations or only outside \c ParentDC,
3271+
/// but it is possible to find more declarations in the type "sugar" chain.
3272+
/// The loop over the "sugar" types can be implemented by using type visit
3273+
/// functions only (call \c CheckType with the desugared type). With the current
3274+
/// solution no visit function is needed if the type has only a desugared type
3275+
/// as data.
3276+
class IsTypeDeclaredInsideVisitor
3277+
: public TypeVisitor<IsTypeDeclaredInsideVisitor, Optional<bool>> {
3278+
public:
3279+
IsTypeDeclaredInsideVisitor(const FunctionDecl *ParentDC)
3280+
: ParentDC(ParentDC) {}
3281+
3282+
bool CheckType(QualType T) {
3283+
// Check the chain of "sugar" types.
3284+
// The "sugar" types are typedef or similar types that have the same
3285+
// canonical type.
3286+
if (Optional<bool> Res = Visit(T.getTypePtr()))
3287+
return *Res;
3288+
QualType DsT =
3289+
T.getSingleStepDesugaredType(ParentDC->getParentASTContext());
3290+
while (DsT != T) {
3291+
if (Optional<bool> Res = Visit(DsT.getTypePtr()))
3292+
return *Res;
3293+
T = DsT;
3294+
DsT = T.getSingleStepDesugaredType(ParentDC->getParentASTContext());
3295+
}
32533296
return false;
3297+
}
3298+
3299+
Optional<bool> VisitTagType(const TagType *T) {
3300+
if (auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(T->getDecl()))
3301+
for (const auto &Arg : Spec->getTemplateArgs().asArray())
3302+
if (checkTemplateArgument(Arg))
3303+
return true;
3304+
return isAncestorDeclContextOf(ParentDC, T->getDecl());
3305+
}
3306+
3307+
Optional<bool> VisitPointerType(const PointerType *T) {
3308+
return CheckType(T->getPointeeType());
3309+
}
3310+
3311+
Optional<bool> VisitReferenceType(const ReferenceType *T) {
3312+
return CheckType(T->getPointeeTypeAsWritten());
3313+
}
3314+
3315+
Optional<bool> VisitTypedefType(const TypedefType *T) {
3316+
const TypedefNameDecl *TD = T->getDecl();
3317+
assert(TD);
3318+
return isAncestorDeclContextOf(ParentDC, TD);
3319+
}
3320+
3321+
Optional<bool> VisitUsingType(const UsingType *T) {
3322+
if (T->getFoundDecl() &&
3323+
isAncestorDeclContextOf(ParentDC, T->getFoundDecl()))
3324+
return true;
3325+
3326+
return {};
3327+
}
3328+
3329+
Optional<bool>
3330+
VisitTemplateSpecializationType(const TemplateSpecializationType *T) {
3331+
for (const auto &Arg : T->template_arguments())
3332+
if (checkTemplateArgument(Arg))
3333+
return true;
3334+
// This type is a "sugar" to a record type, it can have a desugared type.
3335+
return {};
3336+
}
3337+
3338+
Optional<bool> VisitConstantArrayType(const ConstantArrayType *T) {
3339+
if (T->getSizeExpr() && isAncestorDeclContextOf(ParentDC, T->getSizeExpr()))
3340+
return true;
3341+
3342+
return CheckType(T->getElementType());
3343+
}
3344+
3345+
Optional<bool> VisitVariableArrayType(const VariableArrayType *T) {
3346+
llvm_unreachable(
3347+
"Variable array should not occur in deduced return type of a function");
3348+
}
3349+
3350+
Optional<bool> VisitIncompleteArrayType(const IncompleteArrayType *T) {
3351+
llvm_unreachable("Incomplete array should not occur in deduced return type "
3352+
"of a function");
3353+
}
3354+
3355+
Optional<bool> VisitDependentArrayType(const IncompleteArrayType *T) {
3356+
llvm_unreachable("Dependent array should not occur in deduced return type "
3357+
"of a function");
3358+
}
3359+
3360+
private:
3361+
const DeclContext *const ParentDC;
32543362

3255-
auto CheckTemplateArgument = [FD](const TemplateArgument &Arg) {
3363+
bool checkTemplateArgument(const TemplateArgument &Arg) {
32563364
switch (Arg.getKind()) {
3365+
case TemplateArgument::Null:
3366+
return false;
3367+
case TemplateArgument::Integral:
3368+
return CheckType(Arg.getIntegralType());
32573369
case TemplateArgument::Type:
3258-
return hasTypeDeclaredInsideFunction(Arg.getAsType(), FD);
3370+
return CheckType(Arg.getAsType());
32593371
case TemplateArgument::Expression:
3260-
return isAncestorDeclContextOf(FD, Arg.getAsExpr());
3261-
default:
3262-
// FIXME: Handle other argument kinds.
3372+
return isAncestorDeclContextOf(ParentDC, Arg.getAsExpr());
3373+
case TemplateArgument::Declaration:
3374+
// FIXME: The declaration in this case is not allowed to be in a function?
3375+
return isAncestorDeclContextOf(ParentDC, Arg.getAsDecl());
3376+
case TemplateArgument::NullPtr:
3377+
// FIXME: The type is not allowed to be in the function?
3378+
return CheckType(Arg.getNullPtrType());
3379+
case TemplateArgument::Pack:
3380+
for (const auto &PackArg : Arg.getPackAsArray())
3381+
if (checkTemplateArgument(PackArg))
3382+
return true;
3383+
return false;
3384+
case TemplateArgument::Template:
3385+
// Templates can not be defined locally in functions.
3386+
// A template passed as argument can be not in ParentDC.
3387+
return false;
3388+
case TemplateArgument::TemplateExpansion:
3389+
// Templates can not be defined locally in functions.
3390+
// A template passed as argument can be not in ParentDC.
32633391
return false;
32643392
}
32653393
};
3266-
3267-
if (const auto *RecordT = T->getAs<RecordType>()) {
3268-
const RecordDecl *RD = RecordT->getDecl();
3269-
assert(RD);
3270-
if (isAncestorDeclContextOf(FD, RD)) {
3271-
assert(RD->getLexicalDeclContext() == RD->getDeclContext());
3272-
return true;
3273-
}
3274-
if (const auto *RDTempl = dyn_cast<ClassTemplateSpecializationDecl>(RD))
3275-
if (llvm::count_if(RDTempl->getTemplateArgs().asArray(),
3276-
CheckTemplateArgument))
3277-
return true;
3278-
// Note: It is possible that T can be get as both a RecordType and a
3279-
// TemplateSpecializationType.
3280-
}
3281-
if (const auto *TST = T->getAs<TemplateSpecializationType>())
3282-
return llvm::count_if(TST->template_arguments(), CheckTemplateArgument);
3283-
3284-
return false;
3285-
}
3394+
};
3395+
} // namespace
32863396

32873397
bool ASTNodeImporter::hasAutoReturnTypeDeclaredInside(FunctionDecl *D) {
32883398
QualType FromTy = D->getType();
32893399
const auto *FromFPT = FromTy->getAs<FunctionProtoType>();
32903400
assert(FromFPT && "Must be called on FunctionProtoType");
3291-
if (const AutoType *AutoT = FromFPT->getReturnType()->getContainedAutoType())
3292-
return hasTypeDeclaredInsideFunction(AutoT->getDeducedType(), D);
3293-
if (const auto *TypedefT = FromFPT->getReturnType()->getAs<TypedefType>()) {
3294-
const TypedefNameDecl *TD = TypedefT->getDecl();
3295-
assert(TD);
3296-
if (isAncestorDeclContextOf(D, TD)) {
3297-
assert(TD->getLexicalDeclContext() == TD->getDeclContext());
3298-
return true;
3299-
}
3401+
3402+
QualType RetT = FromFPT->getReturnType();
3403+
if (isa<AutoType>(RetT.getTypePtr())) {
3404+
FunctionDecl *Def = D->getDefinition();
3405+
IsTypeDeclaredInsideVisitor Visitor(Def ? Def : D);
3406+
return Visitor.CheckType(RetT);
33003407
}
3408+
33013409
return false;
33023410
}
33033411

0 commit comments

Comments
 (0)