[PATCH] __attribute__((enable_if)) and non-overloaded member functions

Ettore Speziale speziale.ettore at gmail.com
Wed May 27 10:44:46 PDT 2015


Hello Nick,

thanks for taking a look at the patch.

> I think this will cause the enable_if expression to be evaluated multiple times if the expression is not dependent but appertains to a function template, such as:
> 
>   template <typename T> void foo(int i, T t) __attribute__((enable_if(i==0, ""))) {}
> 
> I'm not sure how to fix that without hoisting it up to ActOnCallExpr? Could you check whether this is a real problem?

I tried to run this small example:

// RUN: clang++ -emit-llvm -S -o - test.cpp

struct foo {
  template <typename T>
  int f(int i, T t) __attribute__((enable_if(i == 0, "only when 'i' is zero")));

  template <typename T>
  int g(int i, float j, T t) __attribute__((enable_if(i == 0, "only when 'i' is zero")));
  template <typename T>
  int g(int i, double j, T t) __attribute__((enable_if(i == 0, "only when 'i' is zero"))); 
};

int bar(foo in, int ti, float tf) {
  return in.f(1, ti) + in.f(1, tf);
} 

int baz(foo in, float fj, double dj, int ti, float tf) {
  return in.g(1, fj, ti) + in.g(1, fj, tf) + in.g(1, dj, ti) + in.g(1, dj, tf);
} 

With a modified version of clang that dump the expression just before it is being computed in CheckEnableIf:

diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp
index 95af64e..361a4f3 100644
--- a/lib/Sema/SemaOverload.cpp
+++ b/lib/Sema/SemaOverload.cpp
@@ -5887,6 +5887,7 @@ EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
       continue;
     }
 
+    llvm::errs() << "EVALUATE EXPR\n"; EIA->getCond()->dump();
     if (!EIA->getCond()->EvaluateWithSubstitution(
             Result, Context, Function, llvm::makeArrayRef(ConvertedArgs))) {
       if (!ContainsValueDependentExpr)

The bar function exercise the code in my patch, the baz function exercise existing code.

This is what I get:

┌─[ettore at vesta] - [/tmp] - [2015-05-27 10:11:24]
└─[1] <> clang++ -emit-llvm -S -o - test.cpp
EVALUATE EXPR
BinaryOperator 0x7fc06507e2c8 '_Bool' '=='
|-ImplicitCastExpr 0x7fc06507e2b0 'int' <LValueToRValue>
| `-DeclRefExpr 0x7fc06507e288 'int' lvalue ParmVar 0x7fc06507df98 'i' 'int'
`-IntegerLiteral 0x7fc06507be00 'int' 0
test.cpp:13:13: error: no matching member function for call to 'f'
  return in.f(1, ti) + in.f(1, tf);
         ~~~^
test.cpp:4:7: note: candidate disabled: only when 'i' is zero
  int f(int i, T t) __attribute__((enable_if(i == 0, "only when 'i' is zero")));
      ^                                      ~~~~~~
EVALUATE EXPR
BinaryOperator 0x7fc06507e780 '_Bool' '=='
|-ImplicitCastExpr 0x7fc06507e768 'int' <LValueToRValue>
| `-DeclRefExpr 0x7fc06507e740 'int' lvalue ParmVar 0x7fc06507e480 'i' 'int'
`-IntegerLiteral 0x7fc06507be00 'int' 0
test.cpp:13:27: error: no matching member function for call to 'f'
  return in.f(1, ti) + in.f(1, tf);
                       ~~~^
test.cpp:4:7: note: candidate disabled: only when 'i' is zero
  int f(int i, T t) __attribute__((enable_if(i == 0, "only when 'i' is zero")));
      ^                                      ~~~~~~
EVALUATE EXPR
BinaryOperator 0x7fc06507f188 '_Bool' '=='
|-ImplicitCastExpr 0x7fc06507f170 'int' <LValueToRValue>
| `-DeclRefExpr 0x7fc06507f148 'int' lvalue ParmVar 0x7fc06507ee00 'i' 'int'
`-IntegerLiteral 0x7fc06507c338 'int' 0
EVALUATE EXPR
BinaryOperator 0x7fc06507f698 '_Bool' '=='
|-ImplicitCastExpr 0x7fc06507f680 'int' <LValueToRValue>
| `-DeclRefExpr 0x7fc06507f658 'int' lvalue ParmVar 0x7fc06507f308 'i' 'int'
`-IntegerLiteral 0x7fc06507c828 'int' 0
test.cpp:17:13: error: no matching member function for call to 'g'
  return in.g(1, fj, ti) + in.g(1, fj, tf) + in.g(1, dj, ti) + in.g(1, dj, tf);
         ~~~^
test.cpp:7:7: note: candidate disabled: only when 'i' is zero
  int g(int i, float j, T t) __attribute__((enable_if(i == 0, "only when 'i' is zero")));
      ^                                               ~~~~~~
test.cpp:9:7: note: candidate disabled: only when 'i' is zero
  int g(int i, double j, T t) __attribute__((enable_if(i == 0, "only when 'i' is zero"))); 
      ^                                                ~~~~~~
EVALUATE EXPR
BinaryOperator 0x7fc06507fc38 '_Bool' '=='
|-ImplicitCastExpr 0x7fc06507fc20 'int' <LValueToRValue>
| `-DeclRefExpr 0x7fc06507fbf8 'int' lvalue ParmVar 0x7fc06507f8b0 'i' 'int'
`-IntegerLiteral 0x7fc06507c338 'int' 0
EVALUATE EXPR
BinaryOperator 0x7fc0650804c8 '_Bool' '=='
|-ImplicitCastExpr 0x7fc0650804b0 'int' <LValueToRValue>
| `-DeclRefExpr 0x7fc065080488 'int' lvalue ParmVar 0x7fc06507fd30 'i' 'int'
`-IntegerLiteral 0x7fc06507c828 'int' 0
test.cpp:17:31: error: no matching member function for call to 'g'
  return in.g(1, fj, ti) + in.g(1, fj, tf) + in.g(1, dj, ti) + in.g(1, dj, tf);
                           ~~~^
test.cpp:7:7: note: candidate disabled: only when 'i' is zero
  int g(int i, float j, T t) __attribute__((enable_if(i == 0, "only when 'i' is zero")));
      ^                                               ~~~~~~
test.cpp:9:7: note: candidate disabled: only when 'i' is zero
  int g(int i, double j, T t) __attribute__((enable_if(i == 0, "only when 'i' is zero"))); 
      ^                                                ~~~~~~
EVALUATE EXPR
BinaryOperator 0x7fc06507f188 '_Bool' '=='
|-ImplicitCastExpr 0x7fc06507f170 'int' <LValueToRValue>
| `-DeclRefExpr 0x7fc06507f148 'int' lvalue ParmVar 0x7fc06507ee00 'i' 'int'
`-IntegerLiteral 0x7fc06507c338 'int' 0
EVALUATE EXPR
BinaryOperator 0x7fc06507f698 '_Bool' '=='
|-ImplicitCastExpr 0x7fc06507f680 'int' <LValueToRValue>
| `-DeclRefExpr 0x7fc06507f658 'int' lvalue ParmVar 0x7fc06507f308 'i' 'int'
`-IntegerLiteral 0x7fc06507c828 'int' 0
test.cpp:17:49: error: no matching member function for call to 'g'
  return in.g(1, fj, ti) + in.g(1, fj, tf) + in.g(1, dj, ti) + in.g(1, dj, tf);
                                             ~~~^
test.cpp:7:7: note: candidate disabled: only when 'i' is zero
  int g(int i, float j, T t) __attribute__((enable_if(i == 0, "only when 'i' is zero")));
      ^                                               ~~~~~~
test.cpp:9:7: note: candidate disabled: only when 'i' is zero
  int g(int i, double j, T t) __attribute__((enable_if(i == 0, "only when 'i' is zero"))); 
      ^                                                ~~~~~~
EVALUATE EXPR
BinaryOperator 0x7fc06507fc38 '_Bool' '=='
|-ImplicitCastExpr 0x7fc06507fc20 'int' <LValueToRValue>
| `-DeclRefExpr 0x7fc06507fbf8 'int' lvalue ParmVar 0x7fc06507f8b0 'i' 'int'
`-IntegerLiteral 0x7fc06507c338 'int' 0
EVALUATE EXPR
BinaryOperator 0x7fc0650804c8 '_Bool' '=='
|-ImplicitCastExpr 0x7fc0650804b0 'int' <LValueToRValue>
| `-DeclRefExpr 0x7fc065080488 'int' lvalue ParmVar 0x7fc06507fd30 'i' 'int'
`-IntegerLiteral 0x7fc06507c828 'int' 0
test.cpp:17:67: error: no matching member function for call to 'g'
  return in.g(1, fj, ti) + in.g(1, fj, tf) + in.g(1, dj, ti) + in.g(1, dj, tf);
                                                               ~~~^
test.cpp:7:7: note: candidate disabled: only when 'i' is zero
  int g(int i, float j, T t) __attribute__((enable_if(i == 0, "only when 'i' is zero")));
      ^                                               ~~~~~~
test.cpp:9:7: note: candidate disabled: only when 'i' is zero
  int g(int i, double j, T t) __attribute__((enable_if(i == 0, "only when 'i' is zero"))); 
      ^                                                ~~~~~~
6 errors generated.

I can see the expression being evaluated once for each call to f, and twice for each call to g, as it has two overloads. So it seems that my patch and the current implementation works in the same way from this point of view. 

Did I understand correctly?

Thanks,
Ettore Speziale



More information about the cfe-commits mailing list