Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -325,6 +325,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 @@ -8218,6 +8218,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/CXX/drs/dr3xx.cpp =================================================================== --- test/CXX/drs/dr3xx.cpp +++ test/CXX/drs/dr3xx.cpp @@ -232,8 +232,8 @@ typedef struct { int i; } *ps; - extern "C" void f(ps); - void g(ps); // FIXME: ill-formed, type 'ps' has no linkage + extern "C" void f(ps); // expected-error-re {{'f' has C-linkage specified, but parameter type 'ps' (aka 'dr319::(anonymous struct {{.*}} *') has internal linkage}} + void g(ps); static enum { e } a1; enum { e2 } a2; // FIXME: ill-formed, enum type has no linkage 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*); + +} Index: test/SemaCXX/warn-unused-filescoped.cpp =================================================================== --- test/SemaCXX/warn-unused-filescoped.cpp +++ test/SemaCXX/warn-unused-filescoped.cpp @@ -121,7 +121,7 @@ namespace { struct A {}; } void test(A a); // expected-warning {{unused function}} - extern "C" void test4(A a); + extern "C" void test4(A a); // expected-error {{'test4' has C-linkage specified, but parameter type 'test4::(anonymous namespace)::A' has internal linkage}} } namespace rdar8733476 {