Skip to content

Commit e0dc53b

Browse files
authored
Merge pull request #84724 from j-hui/invalid-func-tmpl-inst
[cxx-interop] Avoid crashing upon invalid function template instantiation
2 parents f71b57e + 4fe7180 commit e0dc53b

File tree

2 files changed

+74
-14
lines changed

2 files changed

+74
-14
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7300,10 +7300,8 @@ clang::FunctionDecl *ClangImporter::instantiateCXXFunctionTemplate(
73007300
// Instantiate a specialization of this template using the substitution map.
73017301
auto *templateArgList = clang::TemplateArgumentList::CreateCopy(
73027302
func->getASTContext(), templateSubst);
7303-
auto &sema = getClangInstance().getSema();
7304-
auto *spec = sema.InstantiateFunctionDeclaration(func, templateArgList,
7305-
clang::SourceLocation());
7306-
if (!spec) {
7303+
7304+
auto diagnoseSubstFail = [&]() {
73077305
std::string templateParams;
73087306
llvm::raw_string_ostream templateParamsStream(templateParams);
73097307
llvm::interleaveComma(templateArgList->asArray(), templateParamsStream,
@@ -7315,9 +7313,24 @@ clang::FunctionDecl *ClangImporter::instantiateCXXFunctionTemplate(
73157313
Impl.diagnose(HeaderLoc(func->getBeginLoc()),
73167314
diag::unable_to_substitute_cxx_function_template,
73177315
getFuncName(), templateParams);
7316+
};
7317+
7318+
auto &sema = getClangInstance().getSema();
7319+
auto *spec = sema.InstantiateFunctionDeclaration(func, templateArgList,
7320+
clang::SourceLocation());
7321+
if (!spec || spec->isInvalidDecl()) {
7322+
diagnoseSubstFail();
73187323
return nullptr;
73197324
}
7325+
73207326
sema.InstantiateFunctionDefinition(clang::SourceLocation(), spec);
7327+
// Even if the declaration can be instantiated, the definition may contain
7328+
// a substitution failure that renders spec invalid as a side-effect.
7329+
if (spec->isInvalidDecl()) {
7330+
diagnoseSubstFail();
7331+
return nullptr;
7332+
}
7333+
73217334
return spec;
73227335
}
73237336

@@ -7714,6 +7727,12 @@ ClangImporter::getCXXFunctionTemplateSpecialization(SubstitutionMap subst,
77147727
assert(isa<clang::FunctionTemplateDecl>(decl->getClangDecl()) &&
77157728
"This API should only be used with function templates.");
77167729

7730+
// If we hit some instantiation failure and expect the compiler to imminently
7731+
// terminate (with an error), return some reasonable-looking placeholder value
7732+
// in the meantime because callers expect this function to return some
7733+
// non-empty ConcreteDeclRef.
7734+
auto failurePlaceholder = [&]() { return ConcreteDeclRef(decl); };
7735+
77177736
auto *newFn =
77187737
decl->getASTContext()
77197738
.getClangModuleLoader()
@@ -7722,31 +7741,28 @@ ClangImporter::getCXXFunctionTemplateSpecialization(SubstitutionMap subst,
77227741
const_cast<clang::FunctionTemplateDecl *>(
77237742
cast<clang::FunctionTemplateDecl>(decl->getClangDecl())),
77247743
subst);
7725-
// We failed to specialize this function template. The compiler is going to
7726-
// exit soon. Return something valid in the meantime.
77277744
if (!newFn)
7728-
return ConcreteDeclRef(decl);
7745+
return failurePlaceholder();
77297746

77307747
auto [fnIt, inserted] =
77317748
Impl.specializedFunctionTemplates.try_emplace(newFn, nullptr);
77327749
if (!inserted)
77337750
return ConcreteDeclRef(fnIt->second);
77347751

7735-
auto newDecl = cast_or_null<ValueDecl>(
7736-
decl->getASTContext().getClangModuleLoader()->importDeclDirectly(
7737-
newFn));
7752+
auto *newDecl = cast_or_null<ValueDecl>(
7753+
decl->getASTContext().getClangModuleLoader()->importDeclDirectly(newFn));
7754+
if (!newDecl)
7755+
return failurePlaceholder();
77387756

7739-
if (auto fn = dyn_cast<AbstractFunctionDecl>(newDecl)) {
7757+
if (auto *fn = dyn_cast<AbstractFunctionDecl>(newDecl)) {
77407758
if (!subst.empty()) {
77417759
newDecl = rewriteIntegerTypes(subst, decl, fn);
77427760
}
77437761
}
77447762

7745-
if (auto fn = dyn_cast<FuncDecl>(decl)) {
7763+
if (auto *fn = dyn_cast<FuncDecl>(decl)) {
77467764
newDecl = addThunkForDependentTypes(fn, cast<FuncDecl>(newDecl));
7747-
}
77487765

7749-
if (auto fn = dyn_cast<FuncDecl>(decl)) {
77507766
if (newFn->getNumParams() != fn->getParameters()->size()) {
77517767
newDecl = generateThunkForExtraMetatypes(subst, fn,
77527768
cast<FuncDecl>(newDecl));
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: split-file %s %t
2+
// RUN: %target-swift-frontend -typecheck -verify -suppress-remarks -suppress-notes \
3+
// RUN: -cxx-interoperability-mode=default \
4+
// RUN: -I %t%{fs-sep}Inputs %t%{fs-sep}main.swift \
5+
// RUN: -verify-additional-file %t%{fs-sep}Inputs%{fs-sep}header.h
6+
7+
//--- Inputs/module.modulemap
8+
module CxxHeader {
9+
header "header.h"
10+
requires cplusplus
11+
}
12+
13+
//--- Inputs/header.h
14+
#pragma once
15+
16+
// TODO: the following diagnostics should be moved to main.swift at the call site
17+
// that triggers the instantiation
18+
// expected-error@+4 {{could not substitute parameters for C++ function template 'cxxCast': BaseT, UnrelatedT}}
19+
// expected-error@+3 {{could not substitute parameters for C++ function template 'cxxCast': BaseT, SubClassT}}
20+
// expected-error@+2 {{could not substitute parameters for C++ function template 'cxxCast': BaseT, SubProtectedT}}
21+
// expected-error@+1 {{could not substitute parameters for C++ function template 'cxxCast': BaseT, SubPrivateT}}
22+
template <class O, class I> O cxxCast(I i) { return static_cast<O>(i); }
23+
// expected-error@-1 {{cannot cast 'const SubPrivateT' to its private base class 'const BaseT'}}
24+
// expected-error@-2 {{cannot cast 'const SubProtectedT' to its protected base class 'const BaseT'}}
25+
// expected-error@-3 {{cannot cast 'const SubClassT' to its private base class 'const BaseT'}}
26+
// expected-error@-4 {{no matching conversion for static_cast from 'UnrelatedT' to 'BaseT'}}
27+
28+
struct BaseT { };
29+
30+
struct SubT : BaseT { }; // publicly inherit from BaseT
31+
struct SubPrivateT : private BaseT { }; // privately inherit from BaseT
32+
struct SubProtectedT : protected BaseT { }; // privately inherit from BaseT
33+
class SubClassT : BaseT { }; // privately inherit from BaseT
34+
struct UnrelatedT { }; // does not inherit from BaseT
35+
36+
37+
//--- main.swift
38+
import CxxHeader
39+
40+
let _: BaseT = cxxCast(SubT()) // valid: upcast to public base
41+
let _: BaseT = cxxCast(SubPrivateT()) // invalid: upcast to non-public base
42+
let _: BaseT = cxxCast(SubProtectedT()) // invalid: upcast to non-public base
43+
let _: BaseT = cxxCast(SubClassT()) // invalid: upcast to non-public base
44+
let _: BaseT = cxxCast(UnrelatedT()) // invalid: cast to unrelated type

0 commit comments

Comments
 (0)