Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -10208,15 +10208,12 @@ class Sema final : public SemaBase {
bool CStyle, bool &ObjCLifetimeConversion);

/// Determine whether the conversion from FromType to ToType is a valid
/// conversion that strips "noexcept" or "noreturn" or "cfi_unchecked_callee"
/// off the nested function type. This also checks if "cfi_unchecked_callee"
/// was added to the function type. If "cfi_unchecked_callee" is added and
/// `AddingCFIUncheckedCallee` is provided, it will be set to true. The same
/// thing applies for `DiscardingCFIUncheckedCallee` if the attribute is
/// discarded.
bool IsFunctionConversion(QualType FromType, QualType ToType,
bool *DiscardingCFIUncheckedCallee = nullptr,
bool *AddingCFIUncheckedCallee = nullptr) const;
/// conversion of ExtInfo/ExtProtoInfo on the nested function type.
/// More precisely, this method checks whether FromType can be tranformed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// More precisely, this method checks whether FromType can be tranformed
/// More precisely, this method checks whether FromType can be transformed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo corrected.

/// into an exact match for ToType, by transforming its extended function
/// type information in legal manner (e.g. by strictly stripping "noreturn"
/// or "noexcept", or by stripping "noescape" for arguments).
bool IsFunctionConversion(QualType FromType, QualType ToType) const;

/// Same as `IsFunctionConversion`, but if this would return true, it sets
/// `ResultTy` to `ToType`.
Expand Down
43 changes: 37 additions & 6 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9187,12 +9187,43 @@ static AssignConvertType checkPointerTypesForAssignment(Sema &S,
return AssignConvertType::IncompatibleFunctionPointer;
return AssignConvertType::IncompatiblePointer;
}
bool DiscardingCFIUncheckedCallee, AddingCFIUncheckedCallee;
if (!S.getLangOpts().CPlusPlus &&
S.IsFunctionConversion(ltrans, rtrans, &DiscardingCFIUncheckedCallee,
&AddingCFIUncheckedCallee)) {
// Allow conversions between CFIUncheckedCallee-ness.
if (!DiscardingCFIUncheckedCallee && !AddingCFIUncheckedCallee)
const auto *LFT = ltrans->getAs<FunctionType>();
const auto *RFT = rtrans->getAs<FunctionType>();
if (!S.getLangOpts().CPlusPlus && LFT && RFT) {
// The invocation of IsFunctionConversion below will try to transform rtrans
// to obtain an exact match for ltrans. This should not fail because of
// mismatches in result type and parameter types, they were already checked
// by typesAreCompatible above. So we will recreate rtrans using the result
// type and parameter types from ltrans, but keeping its
// ExtInfo/ExtProtoInfo.
const auto *LFPT = dyn_cast<FunctionProtoType>(LFT);
const auto *RFPT = dyn_cast<FunctionProtoType>(RFT);
if (LFPT && RFPT) {
rtrans = S.Context.getFunctionType(LFPT->getReturnType(),
LFPT->getParamTypes(),
RFPT->getExtProtoInfo());
} else if (LFPT) {
FunctionProtoType::ExtProtoInfo EPI;
EPI.ExtInfo = RFT->getExtInfo();
rtrans = S.Context.getFunctionType(LFPT->getReturnType(),
LFPT->getParamTypes(), EPI);
} else if (RFPT) {
// In this case, we want to retain rtrans as a FunctionProtoType, to keep
// all of its ExtProtoInfo. To enable an exact match, we recreate ltrans
// as a FunctionProtoType, using its own info but filling in the parameter
// types from rtrans.
FunctionProtoType::ExtProtoInfo EPI;
EPI.ExtInfo = LFT->getExtInfo();
ltrans = S.Context.getFunctionType(LFT->getReturnType(),
RFPT->getParamTypes(), EPI);
rtrans = S.Context.getFunctionType(
LFT->getReturnType(), RFPT->getParamTypes(), RFPT->getExtProtoInfo());
} else {
rtrans = S.Context.getFunctionNoProtoType(LFT->getReturnType(),
RFT->getExtInfo());
}
if (!S.Context.hasSameUnqualifiedType(rtrans, ltrans) &&
!S.IsFunctionConversion(rtrans, ltrans))
return AssignConvertType::IncompatibleFunctionPointer;
}
if (IsInvalidCmseNSCallConversion(S, ltrans, rtrans))
Expand Down
32 changes: 5 additions & 27 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1891,14 +1891,7 @@ bool Sema::TryFunctionConversion(QualType FromType, QualType ToType,
return Changed;
}

bool Sema::IsFunctionConversion(QualType FromType, QualType ToType,
bool *DiscardingCFIUncheckedCallee,
bool *AddingCFIUncheckedCallee) const {
if (DiscardingCFIUncheckedCallee)
*DiscardingCFIUncheckedCallee = false;
if (AddingCFIUncheckedCallee)
*AddingCFIUncheckedCallee = false;

bool Sema::IsFunctionConversion(QualType FromType, QualType ToType) const {
if (Context.hasSameUnqualifiedType(FromType, ToType))
return false;

Expand Down Expand Up @@ -1957,25 +1950,14 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType,
const auto *ToFPT = dyn_cast<FunctionProtoType>(ToFn);

if (FromFPT && ToFPT) {
if (FromFPT->hasCFIUncheckedCallee() && !ToFPT->hasCFIUncheckedCallee()) {
QualType NewTy = Context.getFunctionType(
FromFPT->getReturnType(), FromFPT->getParamTypes(),
FromFPT->getExtProtoInfo().withCFIUncheckedCallee(false));
FromFPT = cast<FunctionProtoType>(NewTy.getTypePtr());
FromFn = FromFPT;
Changed = true;
if (DiscardingCFIUncheckedCallee)
*DiscardingCFIUncheckedCallee = true;
} else if (!FromFPT->hasCFIUncheckedCallee() &&
ToFPT->hasCFIUncheckedCallee()) {
if (FromFPT->hasCFIUncheckedCallee() != ToFPT->hasCFIUncheckedCallee()) {
QualType NewTy = Context.getFunctionType(
FromFPT->getReturnType(), FromFPT->getParamTypes(),
FromFPT->getExtProtoInfo().withCFIUncheckedCallee(true));
FromFPT->getExtProtoInfo().withCFIUncheckedCallee(
ToFPT->hasCFIUncheckedCallee()));
FromFPT = cast<FunctionProtoType>(NewTy.getTypePtr());
FromFn = FromFPT;
Changed = true;
if (AddingCFIUncheckedCallee)
*AddingCFIUncheckedCallee = true;
}
}

Expand Down Expand Up @@ -2006,11 +1988,7 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType,
Changed = true;
}

// For C, when called from checkPointerTypesForAssignment,
// we need to not alter FromFn, or else even an innocuous cast
// like dropping effects will fail. In C++ however we do want to
// alter FromFn (because of the way PerformImplicitConversion works).
if (Context.hasAnyFunctionEffects() && getLangOpts().CPlusPlus) {
if (Context.hasAnyFunctionEffects()) {
FromFPT = cast<FunctionProtoType>(FromFn); // in case FromFn changed above

// Transparently add/drop effects; here we are concerned with
Expand Down
39 changes: 39 additions & 0 deletions clang/test/Sema/incompatible-function-pointer-types-extinfo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// RUN: %clang_cc1 -fsyntax-only %s -std=c99 -verify=expected
// RUN: %clang_cc1 -fsyntax-only %s -std=c11 -verify=expected
// RUN: %clang_cc1 -fsyntax-only %s -std=c23 -verify=expected,proto

// This verifies that -Wincompatible-function-pointer-type diagnostics for
// extended function type information are consistent, also in case of other
// allowed funcion type difference in C.
//
// Test case adapted from issue #160474.

enum E { A = -1, B };

// Case 1: assignment adding noreturn

int f1 (int);
int (*fp1a)(int) __attribute__((noreturn)) = &f1; // expected-error {{incompatible function pointer types}}
enum E (*fp1b)(int) __attribute__((noreturn)) = &f1; // expected-error {{incompatible function pointer types}}
int (*fp1c)() __attribute__((noreturn)) = &f1; // expected-error {{incompatible function pointer types}}

// Case 2: assignment adding noescape on arg

int f2 (int* ) __attribute__((noreturn));
int (*fp2a)(int* __attribute__((noescape))) __attribute__((noreturn)) = &f2; // expected-error {{incompatible function pointer types}}
int (*fp2b)(int* __attribute__((noescape))) = &f2; // expected-error {{incompatible function pointer types}}

// Case 3: assignment adding cfi_unchecked_callee

int f3 (int* );
int (*fp3a)(int* ) __attribute__((noreturn )) = &f3; // expected-error {{incompatible function pointer types}}
int (*fp3b)(int* __attribute__((noescape))) = &f3; // expected-error {{incompatible function pointer types}}
int (*fp3c)(int* ) __attribute__((noreturn,cfi_unchecked_callee)) = &f3; // expected-error {{incompatible function pointer types}}
int (*fp3d)(int* __attribute__((noescape))) __attribute__(( cfi_unchecked_callee)) = &f3; // expected-error {{incompatible function pointer types}}
int (*fp3e)(int* __attribute__((noescape))) __attribute__((noreturn,cfi_unchecked_callee)) = &f3; // expected-error {{incompatible function pointer types}}

// Case 4: assignment to function with no prototype

int f4 (int);
int (*fp4a)(int) = &f4;
int (*fp4b)() = &f4; // proto-error {{incompatible function pointer types}}
Loading