Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -319,6 +319,11 @@ def warn_unused_private_field: Warning<"private field %0 is not used">, InGroup, DefaultIgnore; +def err_return_value_linkage: Error< + "%0 has C-linkage specified, but return type %1 has internal linkage">; +def err_parameter_value_linkage: Error< + "%0 has C-linkage specified, but parameter type %1 has internal linkage">; + def warn_parameter_size: Warning< "%0 is a large (%1 bytes) pass-by-value argument; " "pass it by reference instead ?">, InGroup; Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -8206,6 +8206,25 @@ Diag(NewFD->getLocation(), diag::ext_out_of_line_declaration) << D.getCXXScopeSpec().getRange(); } + + if (NewFD->isExternC()) { + // Check return type linkage + QualType R = NewFD->getReturnType(); + if (R.getTypePtr()->getLinkage() != Linkage::ExternalLinkage) { + Diag(NewFD->getLocation(), diag::err_return_value_linkage) + << NewFD << R; + NewFD->setInvalidDecl(); + } + // Check parameter type linkage + for (auto param : NewFD->parameters()) { + QualType P = param->getOriginalType(); + if (P.getTypePtr()->getLinkage() != Linkage::ExternalLinkage) { + Diag(NewFD->getLocation(), diag::err_parameter_value_linkage) + << NewFD << P; + NewFD->setInvalidDecl(); + } + } + } } ProcessPragmaWeak(S, NewFD); Index: test/Sema/pr23090-crash-on-invalid.cpp =================================================================== --- /dev/null +++ test/Sema/pr23090-crash-on-invalid.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// Don't crash (PR23090). + +namespace { + +// check return type +struct A; +extern "C" A *foo(); // expected-error {{'foo' has C-linkage specified, but return type '(anonymous namespace)::A *' has internal linkage}} +A *foo(); + +// check parameter +struct B; +extern "C" void bar(B*); // expected-error {{'bar' has C-linkage specified, but parameter type '(anonymous namespace)::B *' has internal linkage}} +void bar(B*); + +}