First and foremost, here it is, a fully standard C11 solution:
#include <stdint.h>
#include <assert.h>
#define is_integral_constant_p(c) (_Generic(0 ? (void*)(uintptr_t)((c) - (c)) : (int*)(0), \
                                  int*: 1,                                            \
                                  default: 0)) 
#define foo(x, y) (is_integral_constant_p(x) ? real_foo : error_foo_x_is_not_a_constant_expression)(x, y)
extern int error_foo_x_is_not_a_constant_expression(int, int);
int real_foo(int x, int y)
{
    assert(x < 10);
    return x+y;
}
int main(void)
{
    static_assert(is_integral_constant_p(5), "5 is not a constant expression?");
    foo(5, 1);
    // int x = 0;
    // foo(x, 1);  // This will not link
}
The magic is in how the conditional expression works. If one of the operands is a null pointer constant, the type of the result is the type of the other pointer argument. Otherwise, if one argument is a void* the result is void*. Since (void*)(0) is a null pointer constant, the second operand is a null pointer constant only when (c) - (c) is an integral constant expression equal to 0. Which happens iff c is an integral constant expression.
The _Generic just selects a constant based on the type of the result, which we expect to be either int* in the true case, or void* otherwise. What's more the macro itself evaluates to an integral constant expression (and so could even be used in an assert).
The above is an adaptation of a technique used in the linux kernel (as discussed here), only without and GNU specific extension to C. Only C11 features are used.
Now, we implement foo as a macro that does our check on x, and either forwards to the "real_foo" that does the calculation, or to a function that is declared but not defined. So the program fails to link if x is not a constant.
See it live here.
As for your particular case, you can remove the assertion entirely and move the check into the foo macro. It would look something like this:
#define foo(x, y) (is_integral_constat_p(x) && (x < 10) ? real_foo : \
                   error_foo_x_is_not_a_constant_expression_or_more_than_10)(x, y)
The macro, combined with short circuit evaluation gets us the behavior you want. As you can see here.
At this point I feel I should caution you. This code may not be so straight-forward to protect in code review. So think carefully about whether or not any performance gain you may get is worth the explanations.