This is an archive of the discontinued LLVM Phabricator instance.

Postpone instantiation of constexpr functions
Needs ReviewPublic

Authored by sepavloff on Aug 5 2017, 6:00 AM.

Details

Reviewers
rsmith
Summary

Previous behavior was to instantiate constexpr function in the point where
it is used. It causes crash when compiling the following code:

template<typename T> constexpr T foo(T x) {
  return foo((int)x);
}

The function template specialization foo<int> was instantiated because it
was used in the body of template foo<T> but definition of foo<T> is not
available yet.

With this change a constexpr function template specialization is instantiated
at the end of translation unit unless it is called in a constant expression
outside template definition.

This change also fixes PR33561.

Event Timeline

sepavloff created this revision.Aug 5 2017, 6:00 AM
rsmith requested changes to this revision.Aug 5 2017, 11:16 AM

The current behaviour is correct according to the latest discussions of CWG. We shouldn't be delaying instantiation; we just need to fix the recent regression for functions where we try to instantiate functions that don't have a complete body yet.

This revision now requires changes to proceed.Aug 5 2017, 11:16 AM
sepavloff updated this revision to Diff 109928.Aug 6 2017, 10:22 AM
sepavloff edited edge metadata.

Updated patch

I missed CWG issue 1581. It make patch description invalid, but the patch
itself can be repaired with small modification.

This fix originated from the attempt to solve the problem described in
http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20170731/199590.html.
Compiler crashed on the code:

template <class T>
constexpr void f(T) {
  f(0);
}

When parsing template body clang encounters f(0) and tries to immediately
instantiate f<int>. It is not possible, because body of the function template
f<T> is not available yet. Before r305903 (Function with unparsed body is a
definition) the function template f<T> was considered as undefined and
instantiation request was silently ignored, so no error observed. With r305903
f<T> is reported as defined but instantiation pattern is not available for it,
and the compilation crashes.

Instantiation request cannot be fulfilled during parsing of f<T>, so
the instantiation of f<int> must be postponed. CWG issue 1581 deals with
function calls which can be skipped for some reason. Return value of the call
in this case is ignored, so instantiation of such constexpr function makes
sense only if the body can trigger an error at instantiation, by static_assert,
throw or thing like T:error. If the above is true, constexpr functions does not
need to be instantiated in the point of use, it is sufficient to instantiate it
somewhere later, so that the diagnostics be be present.

According to investigation in https://bugs.llvm.org/show_bug.cgi?id=33561 too
early instantiation is a reason of hang if -fdelayed-template-parsing is used.
At least the reduced test case provided in that bug compiles successfully if
this patch is applied.

The fix was changed a bit so that constexpr functions are added to
PendingInstantiations, the call to isConstexpr is restored in SemaExpr.cpp.
The test from CWG issue 1581 was also added.

sepavloff retitled this revision from Instantiate constexpr function when it is used to Postpone instantiation of constexpr functions.Aug 6 2017, 10:26 AM
sepavloff edited the summary of this revision. (Show Details)
rsmith added inline comments.Aug 6 2017, 11:02 AM
lib/Sema/Sema.cpp
189

It's not correct/reasonable to instantiate whenever an expression happens to be evaluated, in numerous ways:

  1. You'll get the "point of instantiation" wrong -- the standard only permits instantiation (as-if) immediately at the point of use or at the end of the translation unit
  2. You risk instantiating at some points where no instantiation is permitted at all -- for instance, if SemaChecking decides it wants to try to evaluate some expression to produce a warning, and the code it tries to evaluate happens to be in an unevaluated operand, you are not permitted to instantiate things it references.
  3. It risks making the set of instantiations / effective points of instantiation dependent on the warning flags that happen to be in effect, since existing code may or may not choose to evaluate an expression depending on warning flags.

Fundamentally, either constant expression evaluation needs to be side-effect free, or we need to distinguish between places where we happen to evaluate things and places where we evaluate things because the language requires them to be constant expressions. The current direction of CWG1581 is to make the "should we instantiate?" determination based solely on the syntactic context, which means that the constant expression evaluator should never trigger any instantiations.

In order to fix the case where a function/variable template specialization that is usable in a constant expression is defined after the point of use, I think we should check for pending instantiations of that function / variable template when we complete its definition, and perform those instantiations at that time.

rnk added a subscriber: rnk.Aug 14 2017, 12:49 PM
alexfh removed a reviewer: alexfh.Mar 14 2018, 7:49 AM
alexfh added a subscriber: alexfh.