Skip to content

Commit ea8ee1b

Browse files
committed
[cxx-interop] Avoid crashing upon invalid function template instantiation
We were previously not handling cases where the instantiation of the **declaration** of a function template is valid, but the instantiation of its **definition** is invalid (e.g., because it contains an invalid static_cast() or static_assert()). This patch teaches ClangImporter to correctly diagnose these as compiler errors, rather than simply crashing. rdar://137456897
1 parent d68ca8d commit ea8ee1b

File tree

2 files changed

+61
-4
lines changed

2 files changed

+61
-4
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 17 additions & 4 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

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/Inputs %t/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)