forked from OSchip/llvm-project
[Sema] Make `&function_with_enable_if_attrs` an error
This fixes a bug where one can take the address of a conditionally enabled function to drop its enable_if guards. For example: int foo(int a) __attribute__((enable_if(a > 0, ""))); int (*p)(int) = &foo; int result = p(-1); // compilation succeeds; calls foo(-1) Overloading logic has been updated to reflect this change, as well. Functions with enable_if attributes that are always true are still allowed to have their address taken. Differential Revision: http://reviews.llvm.org/D13607 llvm-svn: 250090
This commit is contained in:
parent
3320bcd815
commit
5f21c71800
|
@ -1496,7 +1496,8 @@ def err_init_conversion_failed : Error<
|
|||
"%select{none|const|restrict|const and restrict|volatile|const and volatile|"
|
||||
"volatile and restrict|const, volatile, and restrict}5 vs "
|
||||
"%select{none|const|restrict|const and restrict|volatile|const and volatile|"
|
||||
"volatile and restrict|const, volatile, and restrict}6)}4">;
|
||||
"volatile and restrict|const, volatile, and restrict}6)"
|
||||
"|: cannot take the address of a potentially disabled function}4">;
|
||||
|
||||
def err_lvalue_to_rvalue_ref : Error<"rvalue reference %diff{to type $ cannot "
|
||||
"bind to lvalue of type $|cannot bind to incompatible lvalue}0,1">;
|
||||
|
@ -2967,7 +2968,8 @@ def note_ovl_candidate : Note<"candidate "
|
|||
"%select{none|const|restrict|const and restrict|volatile|const and volatile"
|
||||
"|volatile and restrict|const, volatile, and restrict}3 but found "
|
||||
"%select{none|const|restrict|const and restrict|volatile|const and volatile"
|
||||
"|volatile and restrict|const, volatile, and restrict}4)}2">;
|
||||
"|volatile and restrict|const, volatile, and restrict}4)"
|
||||
"| made ineligible by enable_if}2">;
|
||||
|
||||
def note_ovl_candidate_inherited_constructor : Note<"inherited from here">;
|
||||
def note_ovl_candidate_illegal_constructor : Note<
|
||||
|
@ -5662,7 +5664,8 @@ def note_hidden_overloaded_virtual_declared_here : Note<
|
|||
"%select{none|const|restrict|const and restrict|volatile|const and volatile|"
|
||||
"volatile and restrict|const, volatile, and restrict}2 vs "
|
||||
"%select{none|const|restrict|const and restrict|volatile|const and volatile|"
|
||||
"volatile and restrict|const, volatile, and restrict}3)}1">;
|
||||
"volatile and restrict|const, volatile, and restrict}3)"
|
||||
"|: mismatch in enable_if attributes}1">;
|
||||
def warn_using_directive_in_header : Warning<
|
||||
"using namespace directive in global context in header">,
|
||||
InGroup<HeaderHygiene>, DefaultIgnore;
|
||||
|
@ -5899,7 +5902,8 @@ def err_typecheck_convert_incompatible : Error<
|
|||
"%select{none|const|restrict|const and restrict|volatile|const and volatile|"
|
||||
"volatile and restrict|const, volatile, and restrict}5 vs "
|
||||
"%select{none|const|restrict|const and restrict|volatile|const and volatile|"
|
||||
"volatile and restrict|const, volatile, and restrict}6)}4">;
|
||||
"volatile and restrict|const, volatile, and restrict}6)"
|
||||
"|: cannot take the address of a potentially disabled function}4">;
|
||||
def err_typecheck_missing_return_type_incompatible : Error<
|
||||
"%diff{return type $ must match previous return type $|"
|
||||
"return type must match previous return type}0,1 when %select{block "
|
||||
|
|
|
@ -2440,11 +2440,13 @@ public:
|
|||
bool PartialOverloading = false);
|
||||
|
||||
// Emit as a 'note' the specific overload candidate
|
||||
void NoteOverloadCandidate(FunctionDecl *Fn, QualType DestType = QualType());
|
||||
void NoteOverloadCandidate(FunctionDecl *Fn, QualType DestType = QualType(),
|
||||
bool TakingAddress = false);
|
||||
|
||||
// Emit as a series of 'note's all template and non-templates
|
||||
// identified by the expression Expr
|
||||
void NoteAllOverloadCandidates(Expr* E, QualType DestType = QualType());
|
||||
// Emit as a series of 'note's all template and non-templates identified by
|
||||
// the expression Expr
|
||||
void NoteAllOverloadCandidates(Expr *E, QualType DestType = QualType(),
|
||||
bool TakingAddress = false);
|
||||
|
||||
/// Check the enable_if expressions on the given function. Returns the first
|
||||
/// failing attribute, or NULL if they were all successful.
|
||||
|
|
|
@ -2236,6 +2236,16 @@ void CastOperation::CheckCStyleCast() {
|
|||
return;
|
||||
}
|
||||
|
||||
// Overloads are allowed with C extensions, so we need to support them.
|
||||
if (SrcExpr.get()->getType() == Self.Context.OverloadTy) {
|
||||
DeclAccessPair DAP;
|
||||
if (FunctionDecl *FD = Self.ResolveAddressOfOverloadedFunction(
|
||||
SrcExpr.get(), DestType, /*Complain=*/true, DAP))
|
||||
SrcExpr = Self.FixOverloadedFunctionReference(SrcExpr.get(), DAP, FD);
|
||||
else
|
||||
return;
|
||||
assert(SrcExpr.isUsable());
|
||||
}
|
||||
SrcExpr = Self.DefaultFunctionArrayLvalueConversion(SrcExpr.get());
|
||||
if (SrcExpr.isInvalid())
|
||||
return;
|
||||
|
|
|
@ -7274,6 +7274,15 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS,
|
|||
// structures.
|
||||
// FIXME: We also fall through for atomics; not sure what should
|
||||
// happen there, though.
|
||||
} else if (RHS.get()->getType() == Context.OverloadTy) {
|
||||
// As a set of extensions to C, we support overloading on functions. These
|
||||
// functions need to be resolved here.
|
||||
DeclAccessPair DAP;
|
||||
if (FunctionDecl *FD = ResolveAddressOfOverloadedFunction(
|
||||
RHS.get(), LHSType, /*Complain=*/false, DAP))
|
||||
RHS = FixOverloadedFunctionReference(RHS.get(), DAP, FD);
|
||||
else
|
||||
return Incompatible;
|
||||
}
|
||||
|
||||
// C99 6.5.16.1p1: the left operand is a pointer and the right is
|
||||
|
@ -11986,7 +11995,7 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy,
|
|||
|
||||
if (SecondType == Context.OverloadTy)
|
||||
NoteAllOverloadCandidates(OverloadExpr::find(SrcExpr).Expression,
|
||||
FirstType);
|
||||
FirstType, /*TakingAddress=*/true);
|
||||
|
||||
if (CheckInferredResultType)
|
||||
EmitRelatedResultTypeNote(SrcExpr);
|
||||
|
|
|
@ -6599,6 +6599,8 @@ InitializationSequence::Perform(Sema &S,
|
|||
|
||||
case SK_CAssignment: {
|
||||
QualType SourceType = CurInit.get()->getType();
|
||||
// Save off the initial CurInit in case we need to emit a diagnostic
|
||||
ExprResult InitialCurInit = CurInit;
|
||||
ExprResult Result = CurInit;
|
||||
Sema::AssignConvertType ConvTy =
|
||||
S.CheckSingleAssignmentConstraints(Step->Type, Result, true,
|
||||
|
@ -6621,7 +6623,7 @@ InitializationSequence::Perform(Sema &S,
|
|||
bool Complained;
|
||||
if (S.DiagnoseAssignmentResult(ConvTy, Kind.getLocation(),
|
||||
Step->Type, SourceType,
|
||||
CurInit.get(),
|
||||
InitialCurInit.get(),
|
||||
getAssignmentAction(Entity, true),
|
||||
&Complained)) {
|
||||
PrintInitLocationNote(S, Entity);
|
||||
|
|
|
@ -2508,7 +2508,8 @@ enum {
|
|||
ft_parameter_arity,
|
||||
ft_parameter_mismatch,
|
||||
ft_return_type,
|
||||
ft_qualifer_mismatch
|
||||
ft_qualifer_mismatch,
|
||||
ft_addr_enable_if
|
||||
};
|
||||
|
||||
/// HandleFunctionTypeMismatch - Gives diagnostic information for differeing
|
||||
|
@ -8683,12 +8684,28 @@ void MaybeEmitInheritedConstructorNote(Sema &S, Decl *Fn) {
|
|||
|
||||
} // end anonymous namespace
|
||||
|
||||
static bool isFunctionAlwaysEnabled(const ASTContext &Ctx,
|
||||
const FunctionDecl *FD) {
|
||||
for (auto *EnableIf : FD->specific_attrs<EnableIfAttr>()) {
|
||||
bool AlwaysTrue;
|
||||
if (!EnableIf->getCond()->EvaluateAsBooleanCondition(AlwaysTrue, Ctx))
|
||||
return false;
|
||||
if (!AlwaysTrue)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Notes the location of an overload candidate.
|
||||
void Sema::NoteOverloadCandidate(FunctionDecl *Fn, QualType DestType) {
|
||||
void Sema::NoteOverloadCandidate(FunctionDecl *Fn, QualType DestType,
|
||||
bool TakingAddress) {
|
||||
std::string FnDesc;
|
||||
OverloadCandidateKind K = ClassifyOverloadCandidate(*this, Fn, FnDesc);
|
||||
PartialDiagnostic PD = PDiag(diag::note_ovl_candidate)
|
||||
<< (unsigned) K << FnDesc;
|
||||
if (TakingAddress && !isFunctionAlwaysEnabled(Context, Fn))
|
||||
PD << ft_addr_enable_if;
|
||||
else
|
||||
HandleFunctionTypeMismatch(PD, Fn->getType(), DestType);
|
||||
Diag(Fn->getLocation(), PD);
|
||||
MaybeEmitInheritedConstructorNote(*this, Fn);
|
||||
|
@ -8696,7 +8713,8 @@ void Sema::NoteOverloadCandidate(FunctionDecl *Fn, QualType DestType) {
|
|||
|
||||
// Notes the location of all overload candidates designated through
|
||||
// OverloadedExpr
|
||||
void Sema::NoteAllOverloadCandidates(Expr* OverloadedExpr, QualType DestType) {
|
||||
void Sema::NoteAllOverloadCandidates(Expr *OverloadedExpr, QualType DestType,
|
||||
bool TakingAddress) {
|
||||
assert(OverloadedExpr->getType() == Context.OverloadTy);
|
||||
|
||||
OverloadExpr::FindResult Ovl = OverloadExpr::find(OverloadedExpr);
|
||||
|
@ -8707,10 +8725,11 @@ void Sema::NoteAllOverloadCandidates(Expr* OverloadedExpr, QualType DestType) {
|
|||
I != IEnd; ++I) {
|
||||
if (FunctionTemplateDecl *FunTmpl =
|
||||
dyn_cast<FunctionTemplateDecl>((*I)->getUnderlyingDecl()) ) {
|
||||
NoteOverloadCandidate(FunTmpl->getTemplatedDecl(), DestType);
|
||||
NoteOverloadCandidate(FunTmpl->getTemplatedDecl(), DestType,
|
||||
TakingAddress);
|
||||
} else if (FunctionDecl *Fun
|
||||
= dyn_cast<FunctionDecl>((*I)->getUnderlyingDecl()) ) {
|
||||
NoteOverloadCandidate(Fun, DestType);
|
||||
NoteOverloadCandidate(Fun, DestType, TakingAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10035,7 +10054,12 @@ private:
|
|||
Specialization = cast<FunctionDecl>(Specialization->getCanonicalDecl());
|
||||
assert(S.isSameOrCompatibleFunctionType(
|
||||
Context.getCanonicalType(Specialization->getType()),
|
||||
Context.getCanonicalType(TargetFunctionType)));
|
||||
Context.getCanonicalType(TargetFunctionType)) ||
|
||||
(!S.getLangOpts().CPlusPlus && TargetType->isVoidPointerType()));
|
||||
|
||||
if (!isFunctionAlwaysEnabled(S.Context, Specialization))
|
||||
return false;
|
||||
|
||||
Matches.push_back(std::make_pair(CurAccessFunPair, Specialization));
|
||||
return true;
|
||||
}
|
||||
|
@ -10066,13 +10090,17 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!isFunctionAlwaysEnabled(S.Context, FunDecl))
|
||||
return false;
|
||||
|
||||
QualType ResultTy;
|
||||
if (Context.hasSameUnqualifiedType(TargetFunctionType,
|
||||
FunDecl->getType()) ||
|
||||
S.IsNoReturnConversion(FunDecl->getType(), TargetFunctionType,
|
||||
ResultTy)) {
|
||||
Matches.push_back(std::make_pair(CurAccessFunPair,
|
||||
cast<FunctionDecl>(FunDecl->getCanonicalDecl())));
|
||||
ResultTy) ||
|
||||
(!S.getLangOpts().CPlusPlus && TargetType->isVoidPointerType())) {
|
||||
Matches.push_back(std::make_pair(
|
||||
CurAccessFunPair, cast<FunctionDecl>(FunDecl->getCanonicalDecl())));
|
||||
FoundNonTemplateFunction = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -10174,7 +10202,8 @@ public:
|
|||
<< OvlExpr->getName() << TargetFunctionType
|
||||
<< OvlExpr->getSourceRange();
|
||||
if (FailedCandidates.empty())
|
||||
S.NoteAllOverloadCandidates(OvlExpr, TargetFunctionType);
|
||||
S.NoteAllOverloadCandidates(OvlExpr, TargetFunctionType,
|
||||
/*TakingAddress=*/true);
|
||||
else {
|
||||
// We have some deduction failure messages. Use them to diagnose
|
||||
// the function templates, and diagnose the non-template candidates
|
||||
|
@ -10184,7 +10213,8 @@ public:
|
|||
I != IEnd; ++I)
|
||||
if (FunctionDecl *Fun =
|
||||
dyn_cast<FunctionDecl>((*I)->getUnderlyingDecl()))
|
||||
S.NoteOverloadCandidate(Fun, TargetFunctionType);
|
||||
S.NoteOverloadCandidate(Fun, TargetFunctionType,
|
||||
/*TakingAddress=*/true);
|
||||
FailedCandidates.NoteCandidates(S, OvlExpr->getLocStart());
|
||||
}
|
||||
}
|
||||
|
@ -10222,7 +10252,8 @@ public:
|
|||
S.Diag(OvlExpr->getLocStart(), diag::err_addr_ovl_ambiguous)
|
||||
<< OvlExpr->getName()
|
||||
<< OvlExpr->getSourceRange();
|
||||
S.NoteAllOverloadCandidates(OvlExpr, TargetFunctionType);
|
||||
S.NoteAllOverloadCandidates(OvlExpr, TargetFunctionType,
|
||||
/*TakingAddress=*/true);
|
||||
}
|
||||
|
||||
bool hadMultipleCandidates() const { return (OvlExpr->getNumDecls() > 1); }
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-pc-linux-gnu | FileCheck %s
|
||||
|
||||
// Verifying that we do, in fact, select the correct function in the following
|
||||
// cases.
|
||||
|
||||
void foo(int m) __attribute__((overloadable, enable_if(m > 0, "")));
|
||||
void foo(int m) __attribute__((overloadable));
|
||||
|
||||
// CHECK-LABEL: define void @test1
|
||||
void test1() {
|
||||
// CHECK: store void (i32)* @_Z3fooi
|
||||
void (*p)(int) = foo;
|
||||
// CHECK: store void (i32)* @_Z3fooi
|
||||
void (*p2)(int) = &foo;
|
||||
// CHECK: store void (i32)* @_Z3fooi
|
||||
p = foo;
|
||||
// CHECK: store void (i32)* @_Z3fooi
|
||||
p = &foo;
|
||||
|
||||
// CHECK: store i8* bitcast (void (i32)* @_Z3fooi to i8*)
|
||||
void *vp1 = (void*)&foo;
|
||||
// CHECK: store i8* bitcast (void (i32)* @_Z3fooi to i8*)
|
||||
void *vp2 = (void*)foo;
|
||||
// CHECK: store i8* bitcast (void (i32)* @_Z3fooi to i8*)
|
||||
vp1 = (void*)&foo;
|
||||
// CHECK: store i8* bitcast (void (i32)* @_Z3fooi to i8*)
|
||||
vp1 = (void*)foo;
|
||||
}
|
||||
|
||||
void bar(int m) __attribute__((overloadable, enable_if(m > 0, "")));
|
||||
void bar(int m) __attribute__((overloadable, enable_if(1, "")));
|
||||
// CHECK-LABEL: define void @test2
|
||||
void test2() {
|
||||
// CHECK: store void (i32)* @_Z3barUa9enable_ifIXLi1EEEi
|
||||
void (*p)(int) = bar;
|
||||
// CHECK: store void (i32)* @_Z3barUa9enable_ifIXLi1EEEi
|
||||
void (*p2)(int) = &bar;
|
||||
// CHECK: store void (i32)* @_Z3barUa9enable_ifIXLi1EEEi
|
||||
p = bar;
|
||||
// CHECK: store void (i32)* @_Z3barUa9enable_ifIXLi1EEEi
|
||||
p = &bar;
|
||||
|
||||
// CHECK: store i8* bitcast (void (i32)* @_Z3barUa9enable_ifIXLi1EEEi to i8*)
|
||||
void *vp1 = (void*)&bar;
|
||||
// CHECK: store i8* bitcast (void (i32)* @_Z3barUa9enable_ifIXLi1EEEi to i8*)
|
||||
void *vp2 = (void*)bar;
|
||||
// CHECK: store i8* bitcast (void (i32)* @_Z3barUa9enable_ifIXLi1EEEi to i8*)
|
||||
vp1 = (void*)&bar;
|
||||
// CHECK: store i8* bitcast (void (i32)* @_Z3barUa9enable_ifIXLi1EEEi to i8*)
|
||||
vp1 = (void*)bar;
|
||||
}
|
|
@ -91,6 +91,12 @@ void test4(int c) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void test5() {
|
||||
int (*p1)(int) = &isdigit2;
|
||||
int (*p2)(int) = isdigit2;
|
||||
void *p3 = (void *)&isdigit2;
|
||||
void *p4 = (void *)isdigit2;
|
||||
}
|
||||
|
||||
#ifndef CODEGEN
|
||||
__attribute__((enable_if(n == 0, "chosen when 'n' is zero"))) void f1(int n); // expected-error{{use of undeclared identifier 'n'}}
|
||||
|
@ -109,4 +115,23 @@ void f(int n) __attribute__((enable_if(global == 0, "chosen when 'global' is zer
|
|||
const int cst = 7;
|
||||
void return_cst(void) __attribute__((overloadable)) __attribute__((enable_if(cst == 7, "chosen when 'cst' is 7")));
|
||||
void test_return_cst() { return_cst(); }
|
||||
|
||||
void f2(void) __attribute__((overloadable)) __attribute__((enable_if(1, "always chosen")));
|
||||
void f2(void) __attribute__((overloadable)) __attribute__((enable_if(0, "never chosen")));
|
||||
void f2(void) __attribute__((overloadable));
|
||||
void test6() {
|
||||
void (*p1)(void) = &f2; // expected-error{{initializing 'void (*)(void)' with an expression of incompatible type '<overloaded function type>'}} expected-note@119{{candidate function}} expected-note@120{{candidate function made ineligible by enable_if}} expected-note@121{{candidate function}}
|
||||
void (*p2)(void) = f2; // expected-error{{initializing 'void (*)(void)' with an expression of incompatible type '<overloaded function type>'}} expected-note@119{{candidate function}} expected-note@120{{candidate function made ineligible by enable_if}} expected-note@121{{candidate function}}
|
||||
void *p3 = (void*)&f2; // expected-error{{address of overloaded function 'f2' is ambiguous}} expected-note@119{{candidate function}} expected-note@120{{candidate function made ineligible by enable_if}} expected-note@121{{candidate function}}
|
||||
void *p4 = (void*)f2; // expected-error{{address of overloaded function 'f2' is ambiguous}} expected-note@119{{candidate function}} expected-note@120{{candidate function made ineligible by enable_if}} expected-note@121{{candidate function}}
|
||||
}
|
||||
|
||||
void f3(int m) __attribute__((overloadable)) __attribute__((enable_if(m >= 0, "positive")));
|
||||
void f3(int m) __attribute__((overloadable)) __attribute__((enable_if(m < 0, "negative")));
|
||||
void test7() {
|
||||
void (*p1)(int) = &f3; // expected-error{{initializing 'void (*)(int)' with an expression of incompatible type '<overloaded function type>'}} expected-note@129{{candidate function made ineligible by enable_if}} expected-note@130{{candidate function made ineligible by enable_if}}
|
||||
void (*p2)(int) = f3; // expected-error{{initializing 'void (*)(int)' with an expression of incompatible type '<overloaded function type>'}} expected-note@129{{candidate function made ineligible by enable_if}} expected-note@130{{candidate function made ineligible by enable_if}}
|
||||
void *p3 = (void*)&f3; // expected-error{{address of overloaded function 'f3' does not match required type 'void'}} expected-note@129{{candidate function made ineligible by enable_if}} expected-note@130{{candidate function made ineligible by enable_if}}
|
||||
void *p4 = (void*)f3; // expected-error{{address of overloaded function 'f3' does not match required type 'void'}} expected-note@129{{candidate function made ineligible by enable_if}} expected-note@130{{candidate function made ineligible by enable_if}}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -163,3 +163,74 @@ namespace PR20988 {
|
|||
fn3(sizeof(T) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
namespace FnPtrs {
|
||||
int ovlFoo(int m) __attribute__((enable_if(m > 0, "")));
|
||||
int ovlFoo(int m);
|
||||
|
||||
void test() {
|
||||
// Assignment gives us a different code path than declarations, and `&foo`
|
||||
// gives us a different code path than `foo`
|
||||
int (*p)(int) = ovlFoo;
|
||||
int (*p2)(int) = &ovlFoo;
|
||||
int (*a)(int);
|
||||
a = ovlFoo;
|
||||
a = &ovlFoo;
|
||||
}
|
||||
|
||||
int ovlBar(int) __attribute__((enable_if(true, "")));
|
||||
int ovlBar(int m) __attribute__((enable_if(false, "")));
|
||||
void test2() {
|
||||
int (*p)(int) = ovlBar;
|
||||
int (*p2)(int) = &ovlBar;
|
||||
int (*a)(int);
|
||||
a = ovlBar;
|
||||
a = &ovlBar;
|
||||
}
|
||||
|
||||
int ovlConflict(int m) __attribute__((enable_if(true, "")));
|
||||
int ovlConflict(int m);
|
||||
void test3() {
|
||||
int (*p)(int) = ovlConflict; // expected-error{{address of overloaded function 'ovlConflict' is ambiguous}} expected-note@191{{candidate function}} expected-note@192{{candidate function}}
|
||||
int (*p2)(int) = &ovlConflict; // expected-error{{address of overloaded function 'ovlConflict' is ambiguous}} expected-note@191{{candidate function}} expected-note@192{{candidate function}}
|
||||
int (*a)(int);
|
||||
a = ovlConflict; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@191{{candidate function}} expected-note@192{{candidate function}}
|
||||
a = &ovlConflict; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@191{{candidate function}} expected-note@192{{candidate function}}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T templated(T m) __attribute__((enable_if(true, ""))) { return T(); }
|
||||
template <typename T>
|
||||
T templated(T m) __attribute__((enable_if(false, ""))) { return T(); }
|
||||
void test4() {
|
||||
int (*p)(int) = templated<int>;
|
||||
int (*p2)(int) = &templated<int>;
|
||||
int (*a)(int);
|
||||
a = templated<int>;
|
||||
a = &templated<int>;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T templatedBar(T m) __attribute__((enable_if(m > 0, ""))) { return T(); }
|
||||
void test5() {
|
||||
int (*p)(int) = templatedBar<int>; // expected-error{{address of overloaded function 'templatedBar' does not match required type 'int (int)'}} expected-note@214{{candidate function made ineligible by enable_if}}
|
||||
int (*p2)(int) = &templatedBar<int>; // expected-error{{address of overloaded function 'templatedBar' does not match required type 'int (int)'}} expected-note@214{{candidate function made ineligible by enable_if}}
|
||||
int (*a)(int);
|
||||
a = templatedBar<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@214{{candidate function made ineligible by enable_if}}
|
||||
a = &templatedBar<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@214{{candidate function made ineligible by enable_if}}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T templatedConflict(T m) __attribute__((enable_if(false, ""))) { return T(); }
|
||||
template <typename T>
|
||||
T templatedConflict(T m) __attribute__((enable_if(true, ""))) { return T(); }
|
||||
template <typename T>
|
||||
T templatedConflict(T m) { return T(); }
|
||||
void test6() {
|
||||
int (*p)(int) = templatedConflict<int>; // expected-error{{address of overloaded function 'templatedConflict' is ambiguous}} expected-note@224{{candidate function made ineligible by enable_if}} expected-note@226{{candidate function}} expected-note@228{{candidate function}}
|
||||
int (*p0)(int) = &templatedConflict<int>; // expected-error{{address of overloaded function 'templatedConflict' is ambiguous}} expected-note@224{{candidate function made ineligible by enable_if}} expected-note@226{{candidate function}} expected-note@228{{candidate function}}
|
||||
int (*a)(int);
|
||||
a = templatedConflict<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@226{{candidate function}} expected-note@228{{candidate function}}
|
||||
a = &templatedConflict<int>; // expected-error{{assigning to 'int (*)(int)' from incompatible type '<overloaded function type>'}} expected-note@226{{candidate function}} expected-note@228{{candidate function}}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue