public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Patrick Palka <ppalka@redhat.com>
To: Patrick Palka <ppalka@redhat.com>
Cc: gcc-patches@gcc.gnu.org, jason@redhat.com
Subject: Re: [PATCH] c++: merge tsubst_copy into tsubst_copy_and_build
Date: Tue, 3 Oct 2023 08:41:45 -0400 (EDT)	[thread overview]
Message-ID: <f1d2aa67-ad78-0ac4-0f69-0f086b1538e3@idea> (raw)
In-Reply-To: <20231002193738.3900509-1-ppalka@redhat.com>

On Mon, 2 Oct 2023, Patrick Palka wrote:

> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look
> OK for trunk?
> 
> -- >8 --
> 
> The relationship between tsubst_copy_and_build and tsubst_copy (two of
> the main template argument substitution routines for expression trees)
> is rather hazy.  The former is mostly a superset of the latter, with
> some differences.
> 
> The main difference is that they handle many tree codes differently, but
> much of the tree code handling in tsubst_copy appears to be dead code[1].
> This is because tsubst_copy only gets directly called in a few places
> and mostly on id-expressions.  The interesting exceptions are PARM_DECL,
> VAR_DECL, BIT_NOT_EXPR, SCOPE_REF, TEMPLATE_ID_EXPR and IDENTIFIER_NODE:
> 
>  * for PARM_DECL and VAR_DECL, tsubst_copy_and_build calls tsubst_copy
>    followed by doing some extra handling of its own
>  * for BIT_NOT_EXPR tsubst_copy implicitly handles unresolved destructor
>    calls (i.e. the first operand is an identifier or a type)
>  * for SCOPE_REF, TEMPLATE_ID_EXPR and IDENTIFIER_NODE tsubst_copy
>    refrains from doing name lookup of the terminal name
> 
> Other more minor differences are that tsubst_copy exits early when
> 'args' is null, and it calls maybe_dependent_member_ref, and finally
> it dispatches to tsubst for type trees.
> 
> Thus tsubst_copy is (at this point) similar enough to tsubst_copy_and_build
> that it makes sense to merge the two functions, with the main difference
> being the name lookup behavior[2].  So this patch merges tsubst_copy into
> tsubst_copy_and_build via a new tsubst tf_no_name_lookup which controls
> name lookup and resolution of a (top-level) id-expression.
> 
> [1]: http://thrifty.mooo.com:8008/gcc-lcov/gcc/cp/pt.cc.gcov.html#17231
> [2]: I don't know the history of tsubst_copy but I would guess it was
> added before we settled on using processing_template_decl to control
> whether our AST building routines perform semantic checking and return
> non-templated trees, and so we needed a separate tsubst routine that
> avoids semantic checking and always returns a templated tree for e.g.
> partial substitution.

Oops, this is wrong -- tsubst_copy_and_build came after tsubst_copy,
and was introduced as an optimization with the intent of getting rid
of tsubst_copy eventually:
https://gcc.gnu.org/pipermail/gcc-patches/2003-January/093659.html

> 
> gcc/cp/ChangeLog:
> 
> 	* cp-tree.h (enum tsubst_flags): Add tf_no_name_lookup.
> 	* pt.cc (tsubst_copy):
> 	(tsubst_pack_expansion): Use tsubst for substituting BASES_TYPE.
> 	(tsubst_decl) <case USING_DECL>: Use tsubst_copy_and_build with
> 	tf_no_name_lookup instead of tsubst_copy.
> 	(tsubst) <case TEMPLATE_TYPE_PARM>: Use tsubst_copy_and_build
> 	instead of tsubst_copy for substituting
> 	CLASS_PLACEHOLDER_TEMPLATE.
> 	<case TYPENAME_TYPE>: Use tsubst_copy_and_build with
> 	tf_no_name_lookup instead of tsubst_copy for substituting
> 	TYPENAME_TYPE_FULLNAME.
> 	(tsubst_qualified_id): Likewise for substituting the component
> 	name of a SCOPE_REF.
> 	(tsubst_copy): Remove.
> 	(tsubst_copy_and_build): Clear tf_no_name_lookup at the start,
> 	and remember if it was set.  Call maybe_dependent_member_ref.
> 	<case IDENTIFIER_NODE>: Don't do name lookup if tf_no_name_lookup
> 	was set.
> 	<case TEMLPATE_ID_EXPR>: Don't finish a template-id if
> 	tf_no_name_lookup was set.
> 	<case BIT_NOT_EXPR>: Handle identifier and type operand (if
> 	tf_no_name_lookup was set).
> 	<case SCOPE_REF>: Avoid trying to resolve a SCOPE_REF if
> 	tf_no_name_lookup by calling build_qualified_name directly
> 	instead of tsubst_qualified_id.
> 	<case SIZEOF_EXPR>: Handling of sizeof...  copied from tsubst_copy.
> 	<case CALL_EXPR>: Use tsubst_copy_and_build with
> 	tf_no_name_lookup instead of tsubst_copy to substitute
> 	a TEMPLATE_ID_EXPR callee naming an unresolved template.
> 	<case COMPONENT_REF>: Likewise to substitute the member.
> 	<case FUNCTION_DECL>: Copied from tsubst_copy and merged with ...
> 	<case VAR_DECL, PARM_DECL>: ... these.  Initial handling copied
> 	from tsubst_copy.  Optimize local variable substitution by
> 	trying retrieve_local_specialization before checking
> 	uses_template_parms.
> 	<case CONST_DECL>: Copied from tsubst_copy.
> 	<case FIELD_DECL>: Likewise.
> 	<case NAMESPACE_DECL>: Likewise.
> 	<case OVERLOAD>: Likewise.
> 	<case TEMPLATE_DECL>: Likewise.
> 	<case TEMPLATE_PARM_INDEX>: Likewise.
> 	<case TYPE_DECL>: Likewise.
> 	<case CLEANUP_POINT_EXPR>: Likewise.
> 	<case OFFSET_REF>: Likewise.
> 	<case EXPR_PACK_EXPANSION>: Likewise.
> 	<case NONTYPE_ARGUMENT_PACK>: Likewise.
> 	<case *_CST>: Likewise.
> 	<case *_*_FOLD_EXPR>: Likewise.
> 	<case DEBUG_BEGIN_STMT>: Likewise.
> 	<case CO_AWAIT_EXPR>: Likewise.
> 	<case TRAIT_EXPR>: Use tsubst and tsubst_copy_and_build instead
> 	of tsubst_copy.
> 	<default>: Copied from tsubst_copy.
> 	(tsubst_initializer_list): Use tsubst and tsubst_copy_and_build
> 	instead of tsubst_copy.
> ---
>  gcc/cp/cp-tree.h |    3 +
>  gcc/cp/pt.cc     | 1742 +++++++++++++++++++---------------------------
>  2 files changed, 719 insertions(+), 1026 deletions(-)
> 
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 8b9a7d58462..919eab34803 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -5619,6 +5619,9 @@ enum tsubst_flags {
>    tf_qualifying_scope = 1 << 14, /* Substituting the LHS of the :: operator.
>  				    Affects TYPENAME_TYPE resolution from
>  				    make_typename_type.  */
> +  tf_no_name_lookup = 1 << 15, /* Don't look up the terminal name of an
> +				  outermost id-expression, or resolve its
> +				  constituent template-ids or qualified-ids.  */
>    /* Convenient substitution flags combinations.  */
>    tf_warning_or_error = tf_warning | tf_error
>  };
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 4400d429b6f..e1fb20994e3 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -204,7 +204,6 @@ static void copy_default_args_to_explicit_spec (tree);
>  static bool invalid_nontype_parm_type_p (tree, tsubst_flags_t);
>  static bool dependent_template_arg_p (tree);
>  static bool dependent_type_p_r (tree);
> -static tree tsubst_copy	(tree, tree, tsubst_flags_t, tree);
>  static tree tsubst_decl (tree, tree, tsubst_flags_t, bool = true);
>  static tree tsubst_scope (tree, tree, tsubst_flags_t, tree);
>  static void perform_instantiation_time_access_checks (tree, tree);
> @@ -13373,15 +13372,11 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
>        if (TREE_CODE (parm_pack) == BASES)
>  	{
>  	  gcc_assert (parm_pack == pattern);
> +	  tree type = tsubst (BASES_TYPE (parm_pack), args, complain, in_decl);
>  	  if (BASES_DIRECT (parm_pack))
> -	    return calculate_direct_bases (tsubst_expr (BASES_TYPE (parm_pack),
> -							args, complain,
> -							in_decl),
> -					   complain);
> +	    return calculate_direct_bases (type, complain);
>  	  else
> -	    return calculate_bases (tsubst_expr (BASES_TYPE (parm_pack),
> -						 args, complain, in_decl),
> -				    complain);
> +	    return calculate_bases (type, complain);
>  	}
>        else if (builtin_pack_call_p (parm_pack))
>  	{
> @@ -15171,7 +15166,8 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain,
>  	      variadic_p = true;
>  	    }
>  	  else
> -	    name = tsubst_copy (name, args, complain, in_decl);
> +	    name = tsubst_copy_and_build (name, args,
> +					  complain | tf_no_name_lookup, in_decl);
>  
>  	  int len;
>  	  if (!variadic_p)
> @@ -16108,7 +16104,7 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>        if (template_placeholder_p (t))
>  	{
>  	  tree tmpl = CLASS_PLACEHOLDER_TEMPLATE (t);
> -	  tmpl = tsubst_copy (tmpl, args, complain, in_decl);
> +	  tmpl = tsubst_copy_and_build (tmpl, args, complain, in_decl);
>  	  if (TREE_CODE (tmpl) == TEMPLATE_TEMPLATE_PARM)
>  	    tmpl = TEMPLATE_TEMPLATE_PARM_TEMPLATE_DECL (tmpl);
>  
> @@ -16592,8 +16588,8 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>  	if (ctx == error_mark_node)
>  	  return error_mark_node;
>  
> -	tree f = tsubst_copy (TYPENAME_TYPE_FULLNAME (t), args,
> -			      complain, in_decl);
> +	tree f = tsubst_copy_and_build (TYPENAME_TYPE_FULLNAME (t), args,
> +					complain | tf_no_name_lookup, in_decl);
>  	if (f == error_mark_node)
>  	  return error_mark_node;
>  
> @@ -17045,7 +17041,8 @@ tsubst_qualified_id (tree qualified_id, tree args,
>    if (args)
>      {
>        scope = tsubst_scope (scope, args, complain, in_decl);
> -      expr = tsubst_copy (name, args, complain, in_decl);
> +      expr = tsubst_copy_and_build (name, args,
> +				    complain | tf_no_name_lookup, in_decl);
>      }
>    else
>      expr = name;
> @@ -17277,878 +17274,177 @@ maybe_dependent_member_ref (tree t, tree args, tsubst_flags_t complain,
>  				 TREE_CODE (t) == TEMPLATE_DECL);
>  }
>  
> -/* Like tsubst, but deals with expressions.  This function just replaces
> -   template parms; to finish processing the resultant expression, use
> -   tsubst_copy_and_build or tsubst_expr.  */
> +/* Helper function for tsubst_omp_clauses, used for instantiation of
> +   OMP_CLAUSE_DECL of clauses.  */
>  
>  static tree
> -tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
> +tsubst_omp_clause_decl (tree decl, tree args, tsubst_flags_t complain,
> +			tree in_decl, tree *iterator_cache)
>  {
> -  enum tree_code code;
> -  tree r;
> -
> -  if (t == NULL_TREE || t == error_mark_node || args == NULL_TREE)
> -    return t;
> -
> -  if (TYPE_P (t))
> -    return tsubst (t, args, complain, in_decl);
> -
> -  if (tree d = maybe_dependent_member_ref (t, args, complain, in_decl))
> -    return d;
> -
> -  code = TREE_CODE (t);
> +  if (decl == NULL_TREE || decl == ridpointers[RID_OMP_ALL_MEMORY])
> +    return decl;
>  
> -  switch (code)
> +  /* Handle OpenMP iterators.  */
> +  if (TREE_CODE (decl) == TREE_LIST
> +      && TREE_PURPOSE (decl)
> +      && TREE_CODE (TREE_PURPOSE (decl)) == TREE_VEC)
>      {
> -    case PARM_DECL:
> -      r = retrieve_local_specialization (t);
> -
> -      if (r == NULL_TREE)
> +      tree ret;
> +      if (iterator_cache[0] == TREE_PURPOSE (decl))
> +	ret = iterator_cache[1];
> +      else
>  	{
> -	  /* We get here for a use of 'this' in an NSDMI.  */
> -	  if (DECL_NAME (t) == this_identifier && current_class_ptr)
> -	    return current_class_ptr;
> -
> -	  /* This can happen for a parameter name used later in a function
> -	     declaration (such as in a late-specified return type).  Just
> -	     make a dummy decl, since it's only used for its type.  */
> -	  gcc_assert (cp_unevaluated_operand);
> -	  r = tsubst_decl (t, args, complain);
> -	  /* Give it the template pattern as its context; its true context
> -	     hasn't been instantiated yet and this is good enough for
> -	     mangling.  */
> -	  DECL_CONTEXT (r) = DECL_CONTEXT (t);
> +	  tree *tp = &ret;
> +	  begin_scope (sk_omp, NULL);
> +	  for (tree it = TREE_PURPOSE (decl); it; it = TREE_CHAIN (it))
> +	    {
> +	      *tp = copy_node (it);
> +	      TREE_VEC_ELT (*tp, 0)
> +		= tsubst_decl (TREE_VEC_ELT (it, 0), args, complain);
> +	      DECL_CONTEXT (TREE_VEC_ELT (*tp, 0)) = current_function_decl;
> +	      pushdecl (TREE_VEC_ELT (*tp, 0));
> +	      TREE_VEC_ELT (*tp, 1)
> +		= tsubst_expr (TREE_VEC_ELT (it, 1), args, complain, in_decl);
> +	      TREE_VEC_ELT (*tp, 2)
> +		= tsubst_expr (TREE_VEC_ELT (it, 2), args, complain, in_decl);
> +	      TREE_VEC_ELT (*tp, 3)
> +		= tsubst_expr (TREE_VEC_ELT (it, 3), args, complain, in_decl);
> +	      TREE_CHAIN (*tp) = NULL_TREE;
> +	      tp = &TREE_CHAIN (*tp);
> +	    }
> +	  TREE_VEC_ELT (ret, 5) = poplevel (1, 1, 0);
> +	  iterator_cache[0] = TREE_PURPOSE (decl);
> +	  iterator_cache[1] = ret;
>  	}
> +      return build_tree_list (ret, tsubst_omp_clause_decl (TREE_VALUE (decl),
> +							   args, complain,
> +							   in_decl, NULL));
> +    }
>  
> -      if (TREE_CODE (r) == ARGUMENT_PACK_SELECT)
> -	r = argument_pack_select_arg (r);
> -      if (!mark_used (r, complain) && !(complain & tf_error))
> -	return error_mark_node;
> -      return r;
> -
> -    case CONST_DECL:
> -      {
> -	tree enum_type;
> -	tree v;
> -
> -	if (DECL_TEMPLATE_PARM_P (t))
> -	  return tsubst_copy (DECL_INITIAL (t), args, complain, in_decl);
> -	if (!uses_template_parms (DECL_CONTEXT (t)))
> -	  return t;
> -
> -	/* Unfortunately, we cannot just call lookup_name here.
> -	   Consider:
> -
> -	     template <int I> int f() {
> -	     enum E { a = I };
> -	     struct S { void g() { E e = a; } };
> -	     };
> -
> -	   When we instantiate f<7>::S::g(), say, lookup_name is not
> -	   clever enough to find f<7>::a.  */
> -	enum_type
> -	  = tsubst_aggr_type (DECL_CONTEXT (t), args, complain, in_decl,
> -			      /*entering_scope=*/0);
> -
> -	for (v = TYPE_VALUES (enum_type);
> -	     v != NULL_TREE;
> -	     v = TREE_CHAIN (v))
> -	  if (TREE_PURPOSE (v) == DECL_NAME (t))
> -	    return TREE_VALUE (v);
> -
> -	  /* We didn't find the name.  That should never happen; if
> -	     name-lookup found it during preliminary parsing, we
> -	     should find it again here during instantiation.  */
> -	gcc_unreachable ();
> -      }
> -      return t;
> +  /* Handle an OpenMP array section represented as a TREE_LIST (or
> +     OMP_CLAUSE_DOACROSS_KIND).  An OMP_CLAUSE_DOACROSS (with a depend
> +     kind of OMP_CLAUSE_DOACROSS_SINK) can also be represented as a
> +     TREE_LIST.  We can handle it exactly the same as an array section
> +     (purpose, value, and a chain), even though the nomenclature
> +     (low_bound, length, etc) is different.  */
> +  if (TREE_CODE (decl) == TREE_LIST)
> +    {
> +      tree low_bound
> +	= tsubst_expr (TREE_PURPOSE (decl), args, complain, in_decl);
> +      tree length = tsubst_expr (TREE_VALUE (decl), args, complain, in_decl);
> +      tree chain = tsubst_omp_clause_decl (TREE_CHAIN (decl), args, complain,
> +					   in_decl, NULL);
> +      if (TREE_PURPOSE (decl) == low_bound
> +	  && TREE_VALUE (decl) == length
> +	  && TREE_CHAIN (decl) == chain)
> +	return decl;
> +      tree ret = tree_cons (low_bound, length, chain);
> +      OMP_CLAUSE_DOACROSS_SINK_NEGATIVE (ret)
> +	= OMP_CLAUSE_DOACROSS_SINK_NEGATIVE (decl);
> +      return ret;
> +    }
> +  tree ret = tsubst_expr (decl, args, complain, in_decl);
> +  /* Undo convert_from_reference tsubst_expr could have called.  */
> +  if (decl
> +      && REFERENCE_REF_P (ret)
> +      && !REFERENCE_REF_P (decl))
> +    ret = TREE_OPERAND (ret, 0);
> +  return ret;
> +}
>  
> -    case FIELD_DECL:
> -      if (DECL_CONTEXT (t))
> -	{
> -	  tree ctx;
> +/* Like tsubst_copy, but specifically for OpenMP clauses.  */
>  
> -	  ctx = tsubst_aggr_type (DECL_CONTEXT (t), args, complain, in_decl,
> -				  /*entering_scope=*/1);
> -	  if (ctx != DECL_CONTEXT (t))
> -	    {
> -	      tree r = lookup_field (ctx, DECL_NAME (t), 0, false);
> -	      if (!r)
> -		{
> -		  if (complain & tf_error)
> -		    error ("using invalid field %qD", t);
> -		  return error_mark_node;
> -		}
> -	      return r;
> -	    }
> -	}
> +static tree
> +tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort,
> +		    tree args, tsubst_flags_t complain, tree in_decl)
> +{
> +  tree new_clauses = NULL_TREE, nc, oc;
> +  tree linear_no_step = NULL_TREE;
> +  tree iterator_cache[2] = { NULL_TREE, NULL_TREE };
>  
> -      return t;
> +  for (oc = clauses; oc ; oc = OMP_CLAUSE_CHAIN (oc))
> +    {
> +      nc = copy_node (oc);
> +      OMP_CLAUSE_CHAIN (nc) = new_clauses;
> +      new_clauses = nc;
>  
> -    case VAR_DECL:
> -    case FUNCTION_DECL:
> -      if (DECL_LANG_SPECIFIC (t) && DECL_TEMPLATE_INFO (t))
> -	r = tsubst (t, args, complain, in_decl);
> -      else if (DECL_LOCAL_DECL_P (t))
> +      switch (OMP_CLAUSE_CODE (nc))
>  	{
> -	  /* Local specialization will usually have been created when
> -	     we instantiated the DECL_EXPR_DECL. */
> -	  r = retrieve_local_specialization (t);
> -	  if (!r)
> +	case OMP_CLAUSE_LASTPRIVATE:
> +	  if (OMP_CLAUSE_LASTPRIVATE_STMT (oc))
>  	    {
> -	      /* We're in a generic lambda referencing a local extern
> -		 from an outer block-scope of a non-template.  */
> -	      gcc_checking_assert (LAMBDA_FUNCTION_P (current_function_decl));
> -	      r = t;
> +	      OMP_CLAUSE_LASTPRIVATE_STMT (nc) = push_stmt_list ();
> +	      tsubst_expr (OMP_CLAUSE_LASTPRIVATE_STMT (oc), args,
> +			   complain, in_decl);
> +	      OMP_CLAUSE_LASTPRIVATE_STMT (nc)
> +		= pop_stmt_list (OMP_CLAUSE_LASTPRIVATE_STMT (nc));
>  	    }
> -	}
> -      else if (local_variable_p (t)
> -	       && uses_template_parms (DECL_CONTEXT (t)))
> -	{
> -	  r = retrieve_local_specialization (t);
> -	  if (r == NULL_TREE)
> +	  /* FALLTHRU */
> +	case OMP_CLAUSE_PRIVATE:
> +	case OMP_CLAUSE_SHARED:
> +	case OMP_CLAUSE_FIRSTPRIVATE:
> +	case OMP_CLAUSE_COPYIN:
> +	case OMP_CLAUSE_COPYPRIVATE:
> +	case OMP_CLAUSE_UNIFORM:
> +	case OMP_CLAUSE_DEPEND:
> +	case OMP_CLAUSE_DOACROSS:
> +	case OMP_CLAUSE_AFFINITY:
> +	case OMP_CLAUSE_FROM:
> +	case OMP_CLAUSE_TO:
> +	case OMP_CLAUSE_MAP:
> +	case OMP_CLAUSE__CACHE_:
> +	case OMP_CLAUSE_NONTEMPORAL:
> +	case OMP_CLAUSE_USE_DEVICE_PTR:
> +	case OMP_CLAUSE_USE_DEVICE_ADDR:
> +	case OMP_CLAUSE_IS_DEVICE_PTR:
> +	case OMP_CLAUSE_HAS_DEVICE_ADDR:
> +	case OMP_CLAUSE_INCLUSIVE:
> +	case OMP_CLAUSE_EXCLUSIVE:
> +	  OMP_CLAUSE_DECL (nc)
> +	    = tsubst_omp_clause_decl (OMP_CLAUSE_DECL (oc), args, complain,
> +				      in_decl, iterator_cache);
> +	  break;
> +	case OMP_CLAUSE_NUM_TEAMS:
> +	  if (OMP_CLAUSE_NUM_TEAMS_LOWER_EXPR (oc))
> +	    OMP_CLAUSE_NUM_TEAMS_LOWER_EXPR (nc)
> +	      = tsubst_expr (OMP_CLAUSE_NUM_TEAMS_LOWER_EXPR (oc), args,
> +			     complain, in_decl);
> +	  /* FALLTHRU */
> +	case OMP_CLAUSE_TILE:
> +	case OMP_CLAUSE_IF:
> +	case OMP_CLAUSE_NUM_THREADS:
> +	case OMP_CLAUSE_SCHEDULE:
> +	case OMP_CLAUSE_COLLAPSE:
> +	case OMP_CLAUSE_FINAL:
> +	case OMP_CLAUSE_DEVICE:
> +	case OMP_CLAUSE_DIST_SCHEDULE:
> +	case OMP_CLAUSE_THREAD_LIMIT:
> +	case OMP_CLAUSE_SAFELEN:
> +	case OMP_CLAUSE_SIMDLEN:
> +	case OMP_CLAUSE_NUM_TASKS:
> +	case OMP_CLAUSE_GRAINSIZE:
> +	case OMP_CLAUSE_PRIORITY:
> +	case OMP_CLAUSE_ORDERED:
> +	case OMP_CLAUSE_HINT:
> +	case OMP_CLAUSE_FILTER:
> +	case OMP_CLAUSE_NUM_GANGS:
> +	case OMP_CLAUSE_NUM_WORKERS:
> +	case OMP_CLAUSE_VECTOR_LENGTH:
> +	case OMP_CLAUSE_WORKER:
> +	case OMP_CLAUSE_VECTOR:
> +	case OMP_CLAUSE_ASYNC:
> +	case OMP_CLAUSE_WAIT:
> +	case OMP_CLAUSE_DETACH:
> +	  OMP_CLAUSE_OPERAND (nc, 0)
> +	    = tsubst_expr (OMP_CLAUSE_OPERAND (oc, 0), args, complain, in_decl);
> +	  break;
> +	case OMP_CLAUSE_REDUCTION:
> +	case OMP_CLAUSE_IN_REDUCTION:
> +	case OMP_CLAUSE_TASK_REDUCTION:
> +	  if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (oc))
>  	    {
> -	      /* First try name lookup to find the instantiation.  */
> -	      r = lookup_name (DECL_NAME (t));
> -	      if (r)
> -		{
> -		  if (!VAR_P (r))
> -		    {
> -		      /* During error-recovery we may find a non-variable,
> -			 even an OVERLOAD: just bail out and avoid ICEs and
> -			 duplicate diagnostics (c++/62207).  */
> -		      gcc_assert (seen_error ());
> -		      return error_mark_node;
> -		    }
> -		  if (!is_capture_proxy (r))
> -		    {
> -		      /* Make sure the one we found is the one we want.  */
> -		      tree ctx = enclosing_instantiation_of (DECL_CONTEXT (t));
> -		      if (ctx != DECL_CONTEXT (r))
> -			r = NULL_TREE;
> -		    }
> -		}
> -
> -	      if (r)
> -		/* OK */;
> -	      else
> -		{
> -		  /* This can happen for a variable used in a
> -		     late-specified return type of a local lambda, or for a
> -		     local static or constant.  Building a new VAR_DECL
> -		     should be OK in all those cases.  */
> -		  r = tsubst_decl (t, args, complain);
> -		  if (local_specializations)
> -		    /* Avoid infinite recursion (79640).  */
> -		    register_local_specialization (r, t);
> -		  if (decl_maybe_constant_var_p (r))
> -		    {
> -		      /* We can't call cp_finish_decl, so handle the
> -			 initializer by hand.  */
> -		      tree init = tsubst_init (DECL_INITIAL (t), r, args,
> -					       complain, in_decl);
> -		      if (!processing_template_decl)
> -			init = maybe_constant_init (init);
> -		      if (processing_template_decl
> -			  ? potential_constant_expression (init)
> -			  : reduced_constant_expression_p (init))
> -			DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (r)
> -			  = TREE_CONSTANT (r) = true;
> -		      DECL_INITIAL (r) = init;
> -		      if (tree auto_node = type_uses_auto (TREE_TYPE (r)))
> -			TREE_TYPE (r)
> -			  = do_auto_deduction (TREE_TYPE (r), init, auto_node,
> -					       complain, adc_variable_type);
> -		    }
> -		  gcc_assert (cp_unevaluated_operand
> -			      || processing_contract_condition
> -			      || TREE_STATIC (r)
> -			      || decl_constant_var_p (r)
> -			      || seen_error ());
> -		  if (!processing_template_decl
> -		      && !TREE_STATIC (r))
> -		    r = process_outer_var_ref (r, complain);
> -		}
> -	      /* Remember this for subsequent uses.  */
> -	      if (local_specializations)
> -		register_local_specialization (r, t);
> -	    }
> -	  if (TREE_CODE (r) == ARGUMENT_PACK_SELECT)
> -	    r = argument_pack_select_arg (r);
> -	}
> -      else
> -	r = t;
> -      if (!mark_used (r, complain))
> -	return error_mark_node;
> -      return r;
> -
> -    case NAMESPACE_DECL:
> -      return t;
> -
> -    case OVERLOAD:
> -      return t;
> -
> -    case BASELINK:
> -      return tsubst_baselink (t, current_nonlambda_class_type (),
> -			      args, complain, in_decl);
> -
> -    case TEMPLATE_DECL:
> -      if (DECL_TEMPLATE_TEMPLATE_PARM_P (t))
> -	return tsubst (TREE_TYPE (DECL_TEMPLATE_RESULT (t)),
> -		       args, complain, in_decl);
> -      else if (DECL_FUNCTION_TEMPLATE_P (t) && DECL_MEMBER_TEMPLATE_P (t))
> -	return tsubst (t, args, complain, in_decl);
> -      else if (DECL_CLASS_SCOPE_P (t)
> -	       && uses_template_parms (DECL_CONTEXT (t)))
> -	{
> -	  /* Template template argument like the following example need
> -	     special treatment:
> -
> -	       template <template <class> class TT> struct C {};
> -	       template <class T> struct D {
> -		 template <class U> struct E {};
> -		 C<E> c;				// #1
> -	       };
> -	       D<int> d;				// #2
> -
> -	     We are processing the template argument `E' in #1 for
> -	     the template instantiation #2.  Originally, `E' is a
> -	     TEMPLATE_DECL with `D<T>' as its DECL_CONTEXT.  Now we
> -	     have to substitute this with one having context `D<int>'.  */
> -
> -	  tree context = tsubst_aggr_type (DECL_CONTEXT (t), args, complain,
> -					   in_decl, /*entering_scope=*/true);
> -	  return lookup_field (context, DECL_NAME(t), 0, false);
> -	}
> -      else
> -	/* Ordinary template template argument.  */
> -	return t;
> -
> -    case NON_LVALUE_EXPR:
> -    case VIEW_CONVERT_EXPR:
> -	{
> -	  /* Handle location wrappers by substituting the wrapped node
> -	     first, *then* reusing the resulting type.  Doing the type
> -	     first ensures that we handle template parameters and
> -	     parameter pack expansions.  */
> -	  if (location_wrapper_p (t))
> -	    {
> -	      tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args,
> -				      complain, in_decl);
> -	      return maybe_wrap_with_location (op0, EXPR_LOCATION (t));
> -	    }
> -	  tree op = TREE_OPERAND (t, 0);
> -	  /* force_paren_expr can also create a VIEW_CONVERT_EXPR.  */
> -	  if (code == VIEW_CONVERT_EXPR && REF_PARENTHESIZED_P (t))
> -	    {
> -	      op = tsubst_copy (op, args, complain, in_decl);
> -	      op = build1 (code, TREE_TYPE (op), op);
> -	      REF_PARENTHESIZED_P (op) = true;
> -	      return op;
> -	    }
> -	  /* We shouldn't see any other uses of these in templates
> -	     (tsubst_copy_and_build handles C++20 tparm object wrappers).  */
> -	  gcc_unreachable ();
> -	}
> -
> -    case CAST_EXPR:
> -    case REINTERPRET_CAST_EXPR:
> -    case CONST_CAST_EXPR:
> -    case STATIC_CAST_EXPR:
> -    case DYNAMIC_CAST_EXPR:
> -    case IMPLICIT_CONV_EXPR:
> -    CASE_CONVERT:
> -      {
> -	tsubst_flags_t tcomplain = complain;
> -	if (code == CAST_EXPR)
> -	  tcomplain |= tf_tst_ok;
> -	tree type = tsubst (TREE_TYPE (t), args, tcomplain, in_decl);
> -	tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
> -	return build1 (code, type, op0);
> -      }
> -
> -    case BIT_CAST_EXPR:
> -      {
> -	tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
> -	tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
> -	r = build_min (BIT_CAST_EXPR, type, op0);
> -	SET_EXPR_LOCATION (r, EXPR_LOCATION (t));
> -	return r;
> -      }
> -
> -    case SIZEOF_EXPR:
> -      if (PACK_EXPANSION_P (TREE_OPERAND (t, 0))
> -	  || ARGUMENT_PACK_P (TREE_OPERAND (t, 0)))
> -        {
> -          tree expanded, op = TREE_OPERAND (t, 0);
> -	  int len = 0;
> -
> -	  if (SIZEOF_EXPR_TYPE_P (t))
> -	    op = TREE_TYPE (op);
> -
> -	  ++cp_unevaluated_operand;
> -	  ++c_inhibit_evaluation_warnings;
> -	  /* We only want to compute the number of arguments.  */
> -	  if (PACK_EXPANSION_P (op))
> -	    expanded = tsubst_pack_expansion (op, args, complain, in_decl);
> -	  else
> -	    expanded = tsubst_template_args (ARGUMENT_PACK_ARGS (op),
> -					     args, complain, in_decl);
> -	  --cp_unevaluated_operand;
> -	  --c_inhibit_evaluation_warnings;
> -
> -	  if (TREE_CODE (expanded) == TREE_VEC)
> -	    {
> -	      len = TREE_VEC_LENGTH (expanded);
> -	      /* Set TREE_USED for the benefit of -Wunused.  */
> -	      for (int i = 0; i < len; i++)
> -		if (DECL_P (TREE_VEC_ELT (expanded, i)))
> -		  TREE_USED (TREE_VEC_ELT (expanded, i)) = true;
> -	    }
> -
> -	  if (expanded == error_mark_node)
> -	    return error_mark_node;
> -	  else if (PACK_EXPANSION_P (expanded)
> -		   || (TREE_CODE (expanded) == TREE_VEC
> -		       && pack_expansion_args_count (expanded)))
> -
> -	    {
> -	      if (PACK_EXPANSION_P (expanded))
> -		/* OK.  */;
> -	      else if (TREE_VEC_LENGTH (expanded) == 1)
> -		expanded = TREE_VEC_ELT (expanded, 0);
> -	      else
> -		expanded = make_argument_pack (expanded);
> -
> -	      if (TYPE_P (expanded))
> -		return cxx_sizeof_or_alignof_type (input_location,
> -						   expanded, SIZEOF_EXPR,
> -						   false,
> -						   complain & tf_error);
> -	      else
> -		return cxx_sizeof_or_alignof_expr (input_location,
> -						   expanded, SIZEOF_EXPR,
> -						   false,
> -                                                   complain & tf_error);
> -	    }
> -	  else
> -	    return build_int_cst (size_type_node, len);
> -        }
> -      if (SIZEOF_EXPR_TYPE_P (t))
> -	{
> -	  r = tsubst (TREE_TYPE (TREE_OPERAND (t, 0)),
> -		      args, complain, in_decl);
> -	  r = build1 (NOP_EXPR, r, error_mark_node);
> -	  r = build1 (SIZEOF_EXPR,
> -		      tsubst (TREE_TYPE (t), args, complain, in_decl), r);
> -	  SIZEOF_EXPR_TYPE_P (r) = 1;
> -	  return r;
> -	}
> -      /* Fall through */
> -
> -    case INDIRECT_REF:
> -    case NEGATE_EXPR:
> -    case TRUTH_NOT_EXPR:
> -    case BIT_NOT_EXPR:
> -    case ADDR_EXPR:
> -    case UNARY_PLUS_EXPR:      /* Unary + */
> -    case ALIGNOF_EXPR:
> -    case AT_ENCODE_EXPR:
> -    case ARROW_EXPR:
> -    case THROW_EXPR:
> -    case TYPEID_EXPR:
> -    case REALPART_EXPR:
> -    case IMAGPART_EXPR:
> -    case PAREN_EXPR:
> -      {
> -	tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
> -	tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
> -	r = build1_loc (EXPR_LOCATION (t), code, type, op0);
> -	if (code == ALIGNOF_EXPR)
> -	  ALIGNOF_EXPR_STD_P (r) = ALIGNOF_EXPR_STD_P (t);
> -	/* For addresses of immediate functions ensure we have EXPR_LOCATION
> -	   set for possible later diagnostics.  */
> -	if (code == ADDR_EXPR
> -	    && EXPR_LOCATION (r) == UNKNOWN_LOCATION
> -	    && TREE_CODE (op0) == FUNCTION_DECL
> -	    && DECL_IMMEDIATE_FUNCTION_P (op0))
> -	  SET_EXPR_LOCATION (r, input_location);
> -	return r;
> -      }
> -
> -    case EXCESS_PRECISION_EXPR:
> -      {
> -	tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
> -	tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
> -	if (TREE_CODE (op0) == EXCESS_PRECISION_EXPR)
> -	  {
> -	    gcc_checking_assert (same_type_p (type, TREE_TYPE (op0)));
> -	    return op0;
> -	  }
> -	return build1_loc (EXPR_LOCATION (t), code, type, op0);
> -      }
> -
> -    case COMPONENT_REF:
> -      {
> -	tree object;
> -	tree name;
> -
> -	object = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
> -	name = TREE_OPERAND (t, 1);
> -	if (TREE_CODE (name) == BIT_NOT_EXPR)
> -	  {
> -	    name = tsubst_copy (TREE_OPERAND (name, 0), args,
> -				complain, in_decl);
> -	    name = build1 (BIT_NOT_EXPR, NULL_TREE, name);
> -	  }
> -	else if (TREE_CODE (name) == SCOPE_REF
> -		 && TREE_CODE (TREE_OPERAND (name, 1)) == BIT_NOT_EXPR)
> -	  {
> -	    tree base = tsubst_copy (TREE_OPERAND (name, 0), args,
> -				     complain, in_decl);
> -	    name = TREE_OPERAND (name, 1);
> -	    name = tsubst_copy (TREE_OPERAND (name, 0), args,
> -				complain, in_decl);
> -	    name = build1 (BIT_NOT_EXPR, NULL_TREE, name);
> -	    name = build_qualified_name (/*type=*/NULL_TREE,
> -					 base, name,
> -					 /*template_p=*/false);
> -	  }
> -	else if (BASELINK_P (name))
> -	  name = tsubst_baselink (name,
> -				  non_reference (TREE_TYPE (object)),
> -				  args, complain,
> -				  in_decl);
> -	else
> -	  name = tsubst_copy (name, args, complain, in_decl);
> -	return build_nt (COMPONENT_REF, object, name, NULL_TREE);
> -      }
> -
> -    case PLUS_EXPR:
> -    case MINUS_EXPR:
> -    case MULT_EXPR:
> -    case TRUNC_DIV_EXPR:
> -    case CEIL_DIV_EXPR:
> -    case FLOOR_DIV_EXPR:
> -    case ROUND_DIV_EXPR:
> -    case EXACT_DIV_EXPR:
> -    case BIT_AND_EXPR:
> -    case BIT_IOR_EXPR:
> -    case BIT_XOR_EXPR:
> -    case TRUNC_MOD_EXPR:
> -    case FLOOR_MOD_EXPR:
> -    case TRUTH_ANDIF_EXPR:
> -    case TRUTH_ORIF_EXPR:
> -    case TRUTH_AND_EXPR:
> -    case TRUTH_OR_EXPR:
> -    case RSHIFT_EXPR:
> -    case LSHIFT_EXPR:
> -    case EQ_EXPR:
> -    case NE_EXPR:
> -    case MAX_EXPR:
> -    case MIN_EXPR:
> -    case LE_EXPR:
> -    case GE_EXPR:
> -    case LT_EXPR:
> -    case GT_EXPR:
> -    case COMPOUND_EXPR:
> -    case DOTSTAR_EXPR:
> -    case MEMBER_REF:
> -    case PREDECREMENT_EXPR:
> -    case PREINCREMENT_EXPR:
> -    case POSTDECREMENT_EXPR:
> -    case POSTINCREMENT_EXPR:
> -      {
> -	tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
> -	tree op1 = tsubst_copy (TREE_OPERAND (t, 1), args, complain, in_decl);
> -	return build_nt (code, op0, op1);
> -      }
> -
> -    case SCOPE_REF:
> -      {
> -	tree op0 = tsubst_scope (TREE_OPERAND (t, 0), args, complain, in_decl);
> -	tree op1 = tsubst_copy (TREE_OPERAND (t, 1), args, complain, in_decl);
> -	return build_qualified_name (/*type=*/NULL_TREE, op0, op1,
> -				     QUALIFIED_NAME_IS_TEMPLATE (t));
> -      }
> -
> -    case ARRAY_REF:
> -      {
> -	tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
> -	tree op1 = tsubst_copy (TREE_OPERAND (t, 1), args, complain, in_decl);
> -	return build_nt (ARRAY_REF, op0, op1, NULL_TREE, NULL_TREE);
> -      }
> -
> -    case CALL_EXPR:
> -      {
> -	int n = VL_EXP_OPERAND_LENGTH (t);
> -	tree result = build_vl_exp (CALL_EXPR, n);
> -	int i;
> -	for (i = 0; i < n; i++)
> -	  TREE_OPERAND (t, i) = tsubst_copy (TREE_OPERAND (t, i), args,
> -					     complain, in_decl);
> -	return result;
> -      }
> -
> -    case COND_EXPR:
> -    case MODOP_EXPR:
> -    case PSEUDO_DTOR_EXPR:
> -    case VEC_PERM_EXPR:
> -      {
> -	tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
> -	tree op1 = tsubst_copy (TREE_OPERAND (t, 1), args, complain, in_decl);
> -	tree op2 = tsubst_copy (TREE_OPERAND (t, 2), args, complain, in_decl);
> -	r = build_nt (code, op0, op1, op2);
> -	copy_warning (r, t);
> -	return r;
> -      }
> -
> -    case NEW_EXPR:
> -      {
> -	tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
> -	tree op1 = tsubst_copy (TREE_OPERAND (t, 1), args, complain, in_decl);
> -	tree op2 = tsubst_copy (TREE_OPERAND (t, 2), args, complain, in_decl);
> -	r = build_nt (code, op0, op1, op2);
> -	NEW_EXPR_USE_GLOBAL (r) = NEW_EXPR_USE_GLOBAL (t);
> -	return r;
> -      }
> -
> -    case DELETE_EXPR:
> -      {
> -	tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
> -	tree op1 = tsubst_copy (TREE_OPERAND (t, 1), args, complain, in_decl);
> -	r = build_nt (code, op0, op1);
> -	DELETE_EXPR_USE_GLOBAL (r) = DELETE_EXPR_USE_GLOBAL (t);
> -	DELETE_EXPR_USE_VEC (r) = DELETE_EXPR_USE_VEC (t);
> -	return r;
> -      }
> -
> -    case TEMPLATE_ID_EXPR:
> -      {
> -	/* Substituted template arguments */
> -	tree tmpl = TREE_OPERAND (t, 0);
> -	tree targs = TREE_OPERAND (t, 1);
> -
> -	tmpl = tsubst_copy (tmpl, args, complain, in_decl);
> -	if (targs)
> -	  targs = tsubst_template_args (targs, args, complain, in_decl);
> -
> -	if (variable_template_p (tmpl))
> -	  return lookup_template_variable (tmpl, targs, complain);
> -	else
> -	  return lookup_template_function (tmpl, targs);
> -      }
> -
> -    case TREE_LIST:
> -      {
> -	tree purpose, value, chain;
> -
> -	if (t == void_list_node)
> -	  return t;
> -
> -	purpose = TREE_PURPOSE (t);
> -	if (purpose)
> -	  purpose = tsubst_copy (purpose, args, complain, in_decl);
> -	value = TREE_VALUE (t);
> -	if (value)
> -	  value = tsubst_copy (value, args, complain, in_decl);
> -	chain = TREE_CHAIN (t);
> -	if (chain && chain != void_type_node)
> -	  chain = tsubst_copy (chain, args, complain, in_decl);
> -	if (purpose == TREE_PURPOSE (t)
> -	    && value == TREE_VALUE (t)
> -	    && chain == TREE_CHAIN (t))
> -	  return t;
> -	return tree_cons (purpose, value, chain);
> -      }
> -
> -    case TEMPLATE_PARM_INDEX:
> -    case TYPE_DECL:
> -      return tsubst (t, args, complain, in_decl);
> -
> -    case USING_DECL:
> -      t = DECL_NAME (t);
> -      /* Fall through.  */
> -    case IDENTIFIER_NODE:
> -      if (IDENTIFIER_CONV_OP_P (t))
> -	{
> -	  tree new_type = tsubst (TREE_TYPE (t), args, complain, in_decl);
> -	  return make_conv_op_name (new_type);
> -	}
> -      else
> -	return t;
> -
> -    case CONSTRUCTOR:
> -      /* This is handled by tsubst_copy_and_build.  */
> -      gcc_unreachable ();
> -
> -    case VA_ARG_EXPR:
> -      {
> -	tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
> -	tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
> -	return build_x_va_arg (EXPR_LOCATION (t), op0, type);
> -      }
> -
> -    case CLEANUP_POINT_EXPR:
> -      /* We shouldn't have built any of these during initial template
> -	 generation.  Instead, they should be built during instantiation
> -	 in response to the saved STMT_IS_FULL_EXPR_P setting.  */
> -      gcc_unreachable ();
> -
> -    case OFFSET_REF:
> -      {
> -	tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
> -	tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
> -	tree op1 = tsubst_copy (TREE_OPERAND (t, 1), args, complain, in_decl);
> -	r = build2 (code, type, op0, op1);
> -	PTRMEM_OK_P (r) = PTRMEM_OK_P (t);
> -	if (!mark_used (TREE_OPERAND (r, 1), complain)
> -	    && !(complain & tf_error))
> -	  return error_mark_node;
> -	return r;
> -      }
> -
> -    case EXPR_PACK_EXPANSION:
> -      error ("invalid use of pack expansion expression");
> -      return error_mark_node;
> -
> -    case NONTYPE_ARGUMENT_PACK:
> -      error ("use %<...%> to expand argument pack");
> -      return error_mark_node;
> -
> -    case VOID_CST:
> -      gcc_checking_assert (t == void_node && VOID_TYPE_P (TREE_TYPE (t)));
> -      return t;
> -
> -    case INTEGER_CST:
> -    case REAL_CST:
> -    case COMPLEX_CST:
> -    case VECTOR_CST:
> -      {
> -	/* Instantiate any typedefs in the type.  */
> -	tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
> -	r = fold_convert (type, t);
> -	gcc_assert (TREE_CODE (r) == code);
> -	return r;
> -      }
> -
> -    case STRING_CST:
> -      {
> -	tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
> -	r = t;
> -	if (type != TREE_TYPE (t))
> -	  {
> -	    r = copy_node (t);
> -	    TREE_TYPE (r) = type;
> -	  }
> -	return r;
> -      }
> -
> -    case PTRMEM_CST:
> -      /* These can sometimes show up in a partial instantiation, but never
> -	 involve template parms.  */
> -      gcc_assert (!uses_template_parms (t));
> -      return t;
> -
> -    case UNARY_LEFT_FOLD_EXPR:
> -      return tsubst_unary_left_fold (t, args, complain, in_decl);
> -    case UNARY_RIGHT_FOLD_EXPR:
> -      return tsubst_unary_right_fold (t, args, complain, in_decl);
> -    case BINARY_LEFT_FOLD_EXPR:
> -      return tsubst_binary_left_fold (t, args, complain, in_decl);
> -    case BINARY_RIGHT_FOLD_EXPR:
> -      return tsubst_binary_right_fold (t, args, complain, in_decl);
> -    case PREDICT_EXPR:
> -      return t;
> -
> -    case DEBUG_BEGIN_STMT:
> -      /* ??? There's no point in copying it for now, but maybe some
> -	 day it will contain more information, such as a pointer back
> -	 to the containing function, inlined copy or so.  */
> -      return t;
> -
> -    case CO_AWAIT_EXPR:
> -      return tsubst_expr (t, args, complain, in_decl);
> -
> -    default:
> -      /* We shouldn't get here, but keep going if !flag_checking.  */
> -      if (flag_checking)
> -	gcc_unreachable ();
> -      return t;
> -    }
> -}
> -
> -/* Helper function for tsubst_omp_clauses, used for instantiation of
> -   OMP_CLAUSE_DECL of clauses.  */
> -
> -static tree
> -tsubst_omp_clause_decl (tree decl, tree args, tsubst_flags_t complain,
> -			tree in_decl, tree *iterator_cache)
> -{
> -  if (decl == NULL_TREE || decl == ridpointers[RID_OMP_ALL_MEMORY])
> -    return decl;
> -
> -  /* Handle OpenMP iterators.  */
> -  if (TREE_CODE (decl) == TREE_LIST
> -      && TREE_PURPOSE (decl)
> -      && TREE_CODE (TREE_PURPOSE (decl)) == TREE_VEC)
> -    {
> -      tree ret;
> -      if (iterator_cache[0] == TREE_PURPOSE (decl))
> -	ret = iterator_cache[1];
> -      else
> -	{
> -	  tree *tp = &ret;
> -	  begin_scope (sk_omp, NULL);
> -	  for (tree it = TREE_PURPOSE (decl); it; it = TREE_CHAIN (it))
> -	    {
> -	      *tp = copy_node (it);
> -	      TREE_VEC_ELT (*tp, 0)
> -		= tsubst_decl (TREE_VEC_ELT (it, 0), args, complain);
> -	      DECL_CONTEXT (TREE_VEC_ELT (*tp, 0)) = current_function_decl;
> -	      pushdecl (TREE_VEC_ELT (*tp, 0));
> -	      TREE_VEC_ELT (*tp, 1)
> -		= tsubst_expr (TREE_VEC_ELT (it, 1), args, complain, in_decl);
> -	      TREE_VEC_ELT (*tp, 2)
> -		= tsubst_expr (TREE_VEC_ELT (it, 2), args, complain, in_decl);
> -	      TREE_VEC_ELT (*tp, 3)
> -		= tsubst_expr (TREE_VEC_ELT (it, 3), args, complain, in_decl);
> -	      TREE_CHAIN (*tp) = NULL_TREE;
> -	      tp = &TREE_CHAIN (*tp);
> -	    }
> -	  TREE_VEC_ELT (ret, 5) = poplevel (1, 1, 0);
> -	  iterator_cache[0] = TREE_PURPOSE (decl);
> -	  iterator_cache[1] = ret;
> -	}
> -      return build_tree_list (ret, tsubst_omp_clause_decl (TREE_VALUE (decl),
> -							   args, complain,
> -							   in_decl, NULL));
> -    }
> -
> -  /* Handle an OpenMP array section represented as a TREE_LIST (or
> -     OMP_CLAUSE_DOACROSS_KIND).  An OMP_CLAUSE_DOACROSS (with a depend
> -     kind of OMP_CLAUSE_DOACROSS_SINK) can also be represented as a
> -     TREE_LIST.  We can handle it exactly the same as an array section
> -     (purpose, value, and a chain), even though the nomenclature
> -     (low_bound, length, etc) is different.  */
> -  if (TREE_CODE (decl) == TREE_LIST)
> -    {
> -      tree low_bound
> -	= tsubst_expr (TREE_PURPOSE (decl), args, complain, in_decl);
> -      tree length = tsubst_expr (TREE_VALUE (decl), args, complain, in_decl);
> -      tree chain = tsubst_omp_clause_decl (TREE_CHAIN (decl), args, complain,
> -					   in_decl, NULL);
> -      if (TREE_PURPOSE (decl) == low_bound
> -	  && TREE_VALUE (decl) == length
> -	  && TREE_CHAIN (decl) == chain)
> -	return decl;
> -      tree ret = tree_cons (low_bound, length, chain);
> -      OMP_CLAUSE_DOACROSS_SINK_NEGATIVE (ret)
> -	= OMP_CLAUSE_DOACROSS_SINK_NEGATIVE (decl);
> -      return ret;
> -    }
> -  tree ret = tsubst_expr (decl, args, complain, in_decl);
> -  /* Undo convert_from_reference tsubst_expr could have called.  */
> -  if (decl
> -      && REFERENCE_REF_P (ret)
> -      && !REFERENCE_REF_P (decl))
> -    ret = TREE_OPERAND (ret, 0);
> -  return ret;
> -}
> -
> -/* Like tsubst_copy, but specifically for OpenMP clauses.  */
> -
> -static tree
> -tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort,
> -		    tree args, tsubst_flags_t complain, tree in_decl)
> -{
> -  tree new_clauses = NULL_TREE, nc, oc;
> -  tree linear_no_step = NULL_TREE;
> -  tree iterator_cache[2] = { NULL_TREE, NULL_TREE };
> -
> -  for (oc = clauses; oc ; oc = OMP_CLAUSE_CHAIN (oc))
> -    {
> -      nc = copy_node (oc);
> -      OMP_CLAUSE_CHAIN (nc) = new_clauses;
> -      new_clauses = nc;
> -
> -      switch (OMP_CLAUSE_CODE (nc))
> -	{
> -	case OMP_CLAUSE_LASTPRIVATE:
> -	  if (OMP_CLAUSE_LASTPRIVATE_STMT (oc))
> -	    {
> -	      OMP_CLAUSE_LASTPRIVATE_STMT (nc) = push_stmt_list ();
> -	      tsubst_expr (OMP_CLAUSE_LASTPRIVATE_STMT (oc), args,
> -			   complain, in_decl);
> -	      OMP_CLAUSE_LASTPRIVATE_STMT (nc)
> -		= pop_stmt_list (OMP_CLAUSE_LASTPRIVATE_STMT (nc));
> -	    }
> -	  /* FALLTHRU */
> -	case OMP_CLAUSE_PRIVATE:
> -	case OMP_CLAUSE_SHARED:
> -	case OMP_CLAUSE_FIRSTPRIVATE:
> -	case OMP_CLAUSE_COPYIN:
> -	case OMP_CLAUSE_COPYPRIVATE:
> -	case OMP_CLAUSE_UNIFORM:
> -	case OMP_CLAUSE_DEPEND:
> -	case OMP_CLAUSE_DOACROSS:
> -	case OMP_CLAUSE_AFFINITY:
> -	case OMP_CLAUSE_FROM:
> -	case OMP_CLAUSE_TO:
> -	case OMP_CLAUSE_MAP:
> -	case OMP_CLAUSE__CACHE_:
> -	case OMP_CLAUSE_NONTEMPORAL:
> -	case OMP_CLAUSE_USE_DEVICE_PTR:
> -	case OMP_CLAUSE_USE_DEVICE_ADDR:
> -	case OMP_CLAUSE_IS_DEVICE_PTR:
> -	case OMP_CLAUSE_HAS_DEVICE_ADDR:
> -	case OMP_CLAUSE_INCLUSIVE:
> -	case OMP_CLAUSE_EXCLUSIVE:
> -	  OMP_CLAUSE_DECL (nc)
> -	    = tsubst_omp_clause_decl (OMP_CLAUSE_DECL (oc), args, complain,
> -				      in_decl, iterator_cache);
> -	  break;
> -	case OMP_CLAUSE_NUM_TEAMS:
> -	  if (OMP_CLAUSE_NUM_TEAMS_LOWER_EXPR (oc))
> -	    OMP_CLAUSE_NUM_TEAMS_LOWER_EXPR (nc)
> -	      = tsubst_expr (OMP_CLAUSE_NUM_TEAMS_LOWER_EXPR (oc), args,
> -			     complain, in_decl);
> -	  /* FALLTHRU */
> -	case OMP_CLAUSE_TILE:
> -	case OMP_CLAUSE_IF:
> -	case OMP_CLAUSE_NUM_THREADS:
> -	case OMP_CLAUSE_SCHEDULE:
> -	case OMP_CLAUSE_COLLAPSE:
> -	case OMP_CLAUSE_FINAL:
> -	case OMP_CLAUSE_DEVICE:
> -	case OMP_CLAUSE_DIST_SCHEDULE:
> -	case OMP_CLAUSE_THREAD_LIMIT:
> -	case OMP_CLAUSE_SAFELEN:
> -	case OMP_CLAUSE_SIMDLEN:
> -	case OMP_CLAUSE_NUM_TASKS:
> -	case OMP_CLAUSE_GRAINSIZE:
> -	case OMP_CLAUSE_PRIORITY:
> -	case OMP_CLAUSE_ORDERED:
> -	case OMP_CLAUSE_HINT:
> -	case OMP_CLAUSE_FILTER:
> -	case OMP_CLAUSE_NUM_GANGS:
> -	case OMP_CLAUSE_NUM_WORKERS:
> -	case OMP_CLAUSE_VECTOR_LENGTH:
> -	case OMP_CLAUSE_WORKER:
> -	case OMP_CLAUSE_VECTOR:
> -	case OMP_CLAUSE_ASYNC:
> -	case OMP_CLAUSE_WAIT:
> -	case OMP_CLAUSE_DETACH:
> -	  OMP_CLAUSE_OPERAND (nc, 0)
> -	    = tsubst_expr (OMP_CLAUSE_OPERAND (oc, 0), args, complain, in_decl);
> -	  break;
> -	case OMP_CLAUSE_REDUCTION:
> -	case OMP_CLAUSE_IN_REDUCTION:
> -	case OMP_CLAUSE_TASK_REDUCTION:
> -	  if (OMP_CLAUSE_REDUCTION_PLACEHOLDER (oc))
> -	    {
> -	      tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (oc);
> -	      if (TREE_CODE (placeholder) == SCOPE_REF)
> +	      tree placeholder = OMP_CLAUSE_REDUCTION_PLACEHOLDER (oc);
> +	      if (TREE_CODE (placeholder) == SCOPE_REF)
>  		{
>  		  tree scope = tsubst (TREE_OPERAND (placeholder, 0), args,
>  				       complain, in_decl);
> @@ -20420,6 +19716,13 @@ tsubst_copy_and_build (tree t,
>    tsubst_flags_t decltype_flag = (complain & tf_decltype);
>    complain &= ~tf_decltype;
>  
> +  /* This flag only applies to id-expressions at the top level.  */
> +  tsubst_flags_t no_name_lookup_flag = (complain & tf_no_name_lookup);
> +  complain &= ~tf_no_name_lookup;
> +
> +  if (tree d = maybe_dependent_member_ref (t, args, complain, in_decl))
> +    return d;
> +
>    switch (TREE_CODE (t))
>      {
>      case USING_DECL:
> @@ -20437,6 +19740,9 @@ tsubst_copy_and_build (tree t,
>  	    t = make_conv_op_name (new_type);
>  	  }
>  
> +	if (no_name_lookup_flag)
> +	  RETURN (t);
> +
>  	/* Look up the name.  */
>  	decl = lookup_name (t);
>  
> @@ -20471,7 +19777,8 @@ tsubst_copy_and_build (tree t,
>        {
>  	tree object;
>  	tree templ = tsubst_copy_and_build (TREE_OPERAND (t, 0), args,
> -					    complain, in_decl);
> +					    complain | no_name_lookup_flag,
> +					    in_decl);
>  	tree targs = TREE_OPERAND (t, 1);
>  
>  	if (targs)
> @@ -20505,6 +19812,9 @@ tsubst_copy_and_build (tree t,
>  
>  	if (variable_template_p (templ))
>  	  {
> +	    if (no_name_lookup_flag)
> +	      RETURN (lookup_template_variable (templ, targs, complain));
> +
>  	    tree r = lookup_and_finish_template_variable (templ, targs,
>  							  complain);
>  	    r = convert_from_reference (r);
> @@ -20526,6 +19836,8 @@ tsubst_copy_and_build (tree t,
>  	if (object)
>  	  RETURN (build3 (COMPONENT_REF, TREE_TYPE (tid),
>  			 object, tid, NULL_TREE));
> +	else if (no_name_lookup_flag)
> +	  RETURN (tid);
>  	else if (identifier_p (templ))
>  	  {
>  	    /* C++20 P0846: we can encounter an IDENTIFIER_NODE here when
> @@ -20659,10 +19971,22 @@ tsubst_copy_and_build (tree t,
>  				templated_operator_saved_lookups (t),
>  				complain|decltype_flag));
>  
> +    case BIT_NOT_EXPR:
> +      if (identifier_p (TREE_OPERAND (t, 0)))
> +	{
> +	  gcc_checking_assert (no_name_lookup_flag);
> +	  RETURN (t);
> +	}
> +      else if (TYPE_P (TREE_OPERAND (t, 0)))
> +	{
> +	  gcc_checking_assert (no_name_lookup_flag);
> +	  tree op0 = tsubst (TREE_OPERAND (t, 0), args, complain, in_decl);
> +	  RETURN (build_min_nt_loc (EXPR_LOCATION (t), BIT_NOT_EXPR, op0));
> +	}
> +      /* Fall through.  */
>      case PREDECREMENT_EXPR:
>      case PREINCREMENT_EXPR:
>      case NEGATE_EXPR:
> -    case BIT_NOT_EXPR:
>      case ABS_EXPR:
>      case TRUTH_NOT_EXPR:
>      case UNARY_PLUS_EXPR:  /* Unary + */
> @@ -20779,8 +20103,18 @@ tsubst_copy_and_build (tree t,
>        }
>  
>      case SCOPE_REF:
> -      RETURN (tsubst_qualified_id (t, args, complain, in_decl, /*done=*/true,
> -				  /*address_p=*/false));
> +      if (no_name_lookup_flag)
> +	{
> +	  tree op0 = tsubst_scope (TREE_OPERAND (t, 0), args, complain, in_decl);
> +	  tree op1 = tsubst_copy_and_build (TREE_OPERAND (t, 1), args,
> +					    complain | no_name_lookup_flag,
> +					    in_decl);
> +	  RETURN (build_qualified_name (/*type=*/NULL_TREE, op0, op1,
> +					QUALIFIED_NAME_IS_TEMPLATE (t)));
> +	}
> +      else
> +	RETURN (tsubst_qualified_id (t, args, complain, in_decl, /*done=*/true,
> +				    /*address_p=*/false));
>  
>      case BASELINK:
>        RETURN (tsubst_baselink (t, current_nonlambda_class_type (),
> @@ -20798,26 +20132,80 @@ tsubst_copy_and_build (tree t,
>  	  tsubst_copy_and_build_call_args (c, args, complain, in_decl,
>  					   index_exp_list);
>  
> -	  tree r;
> -	  if (vec_safe_length (index_exp_list) == 1
> -	      && !PACK_EXPANSION_P (index_exp_list[0]))
> -	    r = grok_array_decl (EXPR_LOCATION (t), op1,
> -				 index_exp_list[0], NULL,
> -				 complain | decltype_flag);
> +	  tree r;
> +	  if (vec_safe_length (index_exp_list) == 1
> +	      && !PACK_EXPANSION_P (index_exp_list[0]))
> +	    r = grok_array_decl (EXPR_LOCATION (t), op1,
> +				 index_exp_list[0], NULL,
> +				 complain | decltype_flag);
> +	  else
> +	    r = grok_array_decl (EXPR_LOCATION (t), op1,
> +				 NULL_TREE, &index_exp_list,
> +				 complain | decltype_flag);
> +	  RETURN (r);
> +	}
> +      RETURN (build_x_array_ref (EXPR_LOCATION (t), op1,
> +				 RECUR (TREE_OPERAND (t, 1)),
> +				 complain|decltype_flag));
> +
> +    case SIZEOF_EXPR:
> +      if (PACK_EXPANSION_P (TREE_OPERAND (t, 0))
> +	  || ARGUMENT_PACK_P (TREE_OPERAND (t, 0)))
> +	{
> +	  tree expanded, op = TREE_OPERAND (t, 0);
> +	  int len = 0;
> +
> +	  if (SIZEOF_EXPR_TYPE_P (t))
> +	    op = TREE_TYPE (op);
> +
> +	  ++cp_unevaluated_operand;
> +	  ++c_inhibit_evaluation_warnings;
> +	  /* We only want to compute the number of arguments.  */
> +	  if (PACK_EXPANSION_P (op))
> +	    expanded = tsubst_pack_expansion (op, args, complain, in_decl);
> +	  else
> +	    expanded = tsubst_template_args (ARGUMENT_PACK_ARGS (op),
> +					     args, complain, in_decl);
> +	  --cp_unevaluated_operand;
> +	  --c_inhibit_evaluation_warnings;
> +
> +	  if (TREE_CODE (expanded) == TREE_VEC)
> +	    {
> +	      len = TREE_VEC_LENGTH (expanded);
> +	      /* Set TREE_USED for the benefit of -Wunused.  */
> +	      for (int i = 0; i < len; i++)
> +		if (DECL_P (TREE_VEC_ELT (expanded, i)))
> +		  TREE_USED (TREE_VEC_ELT (expanded, i)) = true;
> +	    }
> +
> +	  if (expanded == error_mark_node)
> +	    RETURN (error_mark_node);
> +	  else if (PACK_EXPANSION_P (expanded)
> +		   || (TREE_CODE (expanded) == TREE_VEC
> +		       && pack_expansion_args_count (expanded)))
> +
> +	    {
> +	      if (PACK_EXPANSION_P (expanded))
> +		/* OK.  */;
> +	      else if (TREE_VEC_LENGTH (expanded) == 1)
> +		expanded = TREE_VEC_ELT (expanded, 0);
> +	      else
> +		expanded = make_argument_pack (expanded);
> +
> +	      if (TYPE_P (expanded))
> +		RETURN (cxx_sizeof_or_alignof_type (input_location,
> +						    expanded, SIZEOF_EXPR,
> +						    false,
> +						    complain & tf_error));
> +	      else
> +		RETURN (cxx_sizeof_or_alignof_expr (input_location,
> +						    expanded, SIZEOF_EXPR,
> +						    false,
> +						    complain & tf_error));
> +	    }
>  	  else
> -	    r = grok_array_decl (EXPR_LOCATION (t), op1,
> -				 NULL_TREE, &index_exp_list,
> -				 complain | decltype_flag);
> -	  RETURN (r);
> +	    RETURN (build_int_cst (size_type_node, len));
>  	}
> -      RETURN (build_x_array_ref (EXPR_LOCATION (t), op1,
> -				 RECUR (TREE_OPERAND (t, 1)),
> -				 complain|decltype_flag));
> -
> -    case SIZEOF_EXPR:
> -      if (PACK_EXPANSION_P (TREE_OPERAND (t, 0))
> -	  || ARGUMENT_PACK_P (TREE_OPERAND (t, 0)))
> -	RETURN (tsubst_copy (t, args, complain, in_decl));
>        /* Fall through */
>  
>      case ALIGNOF_EXPR:
> @@ -21072,10 +20460,9 @@ tsubst_copy_and_build (tree t,
>  	    qualified_p = false;
>  
>  	    if (TREE_CODE (function) == TEMPLATE_ID_EXPR)
> -	      /* Use tsubst_copy to substitute through the template arguments
> -		 of the template-id without performing unqualified lookup of
> -		 the template name.  */
> -	      function = tsubst_copy (function, args, complain, in_decl);
> +	      function = tsubst_copy_and_build (function, args,
> +						complain | tf_no_name_lookup,
> +						in_decl);
>  	  }
>  	else
>  	  {
> @@ -21473,7 +20860,8 @@ tsubst_copy_and_build (tree t,
>  				    non_reference (TREE_TYPE (object)),
>  				    args, complain, in_decl);
>  	else
> -	  member = tsubst_copy (member, args, complain, in_decl);
> +	  member = tsubst_copy_and_build (member, args,
> +					  complain | tf_no_name_lookup, in_decl);
>  	if (member == error_mark_node)
>  	  RETURN (error_mark_node);
>  
> @@ -21556,154 +20944,446 @@ tsubst_copy_and_build (tree t,
>  	    RETURN (error_mark_node);
>  	  }
>  
> -	r = finish_class_member_access_expr (object, member,
> -					     /*template_p=*/false,
> -					     complain);
> -	if (REF_PARENTHESIZED_P (t))
> -	  r = force_paren_expr (r);
> -	RETURN (r);
> +	r = finish_class_member_access_expr (object, member,
> +					     /*template_p=*/false,
> +					     complain);
> +	if (REF_PARENTHESIZED_P (t))
> +	  r = force_paren_expr (r);
> +	RETURN (r);
> +      }
> +
> +    case THROW_EXPR:
> +      RETURN (build_throw
> +       (input_location, RECUR (TREE_OPERAND (t, 0))));
> +
> +    case CONSTRUCTOR:
> +      {
> +	vec<constructor_elt, va_gc> *n;
> +	constructor_elt *ce;
> +	unsigned HOST_WIDE_INT idx;
> +	bool process_index_p;
> +        int newlen;
> +        bool need_copy_p = false;
> +	tree r;
> +
> +	tsubst_flags_t tcomplain = complain;
> +	if (COMPOUND_LITERAL_P (t))
> +	  tcomplain |= tf_tst_ok;
> +	tree type = tsubst (TREE_TYPE (t), args, tcomplain, in_decl);
> +	if (type == error_mark_node)
> +	  RETURN (error_mark_node);
> +
> +	/* We do not want to process the index of aggregate
> +	   initializers as they are identifier nodes which will be
> +	   looked up by digest_init.  */
> +	process_index_p = !(type && MAYBE_CLASS_TYPE_P (type));
> +
> +	if (null_member_pointer_value_p (t))
> +	  {
> +	    gcc_assert (same_type_p (type, TREE_TYPE (t)));
> +	    RETURN (t);
> +	  }
> +
> +	n = vec_safe_copy (CONSTRUCTOR_ELTS (t));
> +        newlen = vec_safe_length (n);
> +	FOR_EACH_VEC_SAFE_ELT (n, idx, ce)
> +	  {
> +	    if (ce->index && process_index_p
> +		/* An identifier index is looked up in the type
> +		   being initialized, not the current scope.  */
> +		&& TREE_CODE (ce->index) != IDENTIFIER_NODE)
> +	      ce->index = RECUR (ce->index);
> +
> +            if (PACK_EXPANSION_P (ce->value))
> +              {
> +                /* Substitute into the pack expansion.  */
> +                ce->value = tsubst_pack_expansion (ce->value, args, complain,
> +                                                  in_decl);
> +
> +		if (ce->value == error_mark_node
> +		    || PACK_EXPANSION_P (ce->value))
> +		  ;
> +		else if (TREE_VEC_LENGTH (ce->value) == 1)
> +                  /* Just move the argument into place.  */
> +                  ce->value = TREE_VEC_ELT (ce->value, 0);
> +                else
> +                  {
> +                    /* Update the length of the final CONSTRUCTOR
> +                       arguments vector, and note that we will need to
> +                       copy.*/
> +                    newlen = newlen + TREE_VEC_LENGTH (ce->value) - 1;
> +                    need_copy_p = true;
> +                  }
> +              }
> +            else
> +              ce->value = RECUR (ce->value);
> +	  }
> +
> +        if (need_copy_p)
> +          {
> +            vec<constructor_elt, va_gc> *old_n = n;
> +
> +            vec_alloc (n, newlen);
> +            FOR_EACH_VEC_ELT (*old_n, idx, ce)
> +              {
> +                if (TREE_CODE (ce->value) == TREE_VEC)
> +                  {
> +                    int i, len = TREE_VEC_LENGTH (ce->value);
> +                    for (i = 0; i < len; ++i)
> +                      CONSTRUCTOR_APPEND_ELT (n, 0,
> +                                              TREE_VEC_ELT (ce->value, i));
> +                  }
> +                else
> +                  CONSTRUCTOR_APPEND_ELT (n, 0, ce->value);
> +              }
> +          }
> +
> +	r = build_constructor (init_list_type_node, n);
> +	CONSTRUCTOR_IS_DIRECT_INIT (r) = CONSTRUCTOR_IS_DIRECT_INIT (t);
> +	CONSTRUCTOR_IS_DESIGNATED_INIT (r)
> +	  = CONSTRUCTOR_IS_DESIGNATED_INIT (t);
> +
> +	if (TREE_HAS_CONSTRUCTOR (t))
> +	  {
> +	    fcl_t cl = fcl_functional;
> +	    if (CONSTRUCTOR_C99_COMPOUND_LITERAL (t))
> +	      cl = fcl_c99;
> +	    RETURN (finish_compound_literal (type, r, complain, cl));
> +	  }
> +
> +	TREE_TYPE (r) = type;
> +	RETURN (r);
> +      }
> +
> +    case TYPEID_EXPR:
> +      {
> +	tree operand_0 = TREE_OPERAND (t, 0);
> +	if (TYPE_P (operand_0))
> +	  {
> +	    operand_0 = tsubst (operand_0, args, complain, in_decl);
> +	    RETURN (get_typeid (operand_0, complain));
> +	  }
> +	else
> +	  {
> +	    operand_0 = RECUR (operand_0);
> +	    RETURN (build_typeid (operand_0, complain));
> +	  }
> +      }
> +
> +    case FUNCTION_DECL:
> +    case PARM_DECL:
> +    case VAR_DECL:
> +      if (!args)
> +	RETURN (t);
> +      tree r;
> +      if (VAR_OR_FUNCTION_DECL_P (t)
> +	  && DECL_LANG_SPECIFIC (t) && DECL_TEMPLATE_INFO (t))
> +	r = tsubst_decl (t, args, complain);
> +      else if (VAR_OR_FUNCTION_DECL_P (t) && DECL_LOCAL_DECL_P (t))
> +	{
> +	  /* Local specialization will usually have been created when
> +	     we instantiated the DECL_EXPR_DECL. */
> +	  r = retrieve_local_specialization (t);
> +	  if (!r)
> +	    {
> +	      /* We're in a generic lambda referencing a local extern
> +		 from an outer block-scope of a non-template.  */
> +	      gcc_checking_assert (LAMBDA_FUNCTION_P (current_function_decl));
> +	      r = t;
> +	    }
> +	}
> +      else if (local_variable_p (t)
> +	       && ((r = retrieve_local_specialization (t))
> +		   || TREE_CODE (t) == PARM_DECL
> +		   || uses_template_parms (DECL_CONTEXT (t))))
> +	{
> +	  if (r == NULL_TREE && TREE_CODE (t) == PARM_DECL)
> +	    {
> +	      /* We get here for a use of 'this' in an NSDMI.  */
> +	      if (DECL_NAME (t) == this_identifier && current_class_ptr)
> +		RETURN (current_class_ptr);
> +
> +	      /* This can happen for a parameter name used later in a function
> +		 declaration (such as in a late-specified return type).  Just
> +		 make a dummy decl, since it's only used for its type.  */
> +	      gcc_assert (cp_unevaluated_operand);
> +	      r = tsubst_decl (t, args, complain);
> +	      /* Give it the template pattern as its context; its true context
> +		 hasn't been instantiated yet and this is good enough for
> +		 mangling.  */
> +	      DECL_CONTEXT (r) = DECL_CONTEXT (t);
> +	    }
> +	  else if (r == NULL_TREE)
> +	    {
> +	      /* First try name lookup to find the instantiation.  */
> +	      r = lookup_name (DECL_NAME (t));
> +	      if (r)
> +		{
> +		  if (!VAR_P (r))
> +		    {
> +		      /* During error-recovery we may find a non-variable,
> +			 even an OVERLOAD: just bail out and avoid ICEs and
> +			 duplicate diagnostics (c++/62207).  */
> +		      gcc_assert (seen_error ());
> +		      RETURN (error_mark_node);
> +		    }
> +		  if (!is_capture_proxy (r))
> +		    {
> +		      /* Make sure the one we found is the one we want.  */
> +		      tree ctx = enclosing_instantiation_of (DECL_CONTEXT (t));
> +		      if (ctx != DECL_CONTEXT (r))
> +			r = NULL_TREE;
> +		    }
> +		}
> +
> +	      if (r)
> +		/* OK */;
> +	      else
> +		{
> +		  /* This can happen for a variable used in a
> +		     late-specified return type of a local lambda, or for a
> +		     local static or constant.  Building a new VAR_DECL
> +		     should be OK in all those cases.  */
> +		  r = tsubst_decl (t, args, complain);
> +		  if (local_specializations)
> +		    /* Avoid infinite recursion (79640).  */
> +		    register_local_specialization (r, t);
> +		  if (decl_maybe_constant_var_p (r))
> +		    {
> +		      /* We can't call cp_finish_decl, so handle the
> +			 initializer by hand.  */
> +		      tree init = tsubst_init (DECL_INITIAL (t), r, args,
> +					       complain, in_decl);
> +		      if (!processing_template_decl)
> +			init = maybe_constant_init (init);
> +		      if (processing_template_decl
> +			  ? potential_constant_expression (init)
> +			  : reduced_constant_expression_p (init))
> +			DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (r)
> +			  = TREE_CONSTANT (r) = true;
> +		      DECL_INITIAL (r) = init;
> +		      if (tree auto_node = type_uses_auto (TREE_TYPE (r)))
> +			TREE_TYPE (r)
> +			  = do_auto_deduction (TREE_TYPE (r), init, auto_node,
> +					       complain, adc_variable_type);
> +		    }
> +		  gcc_assert (cp_unevaluated_operand
> +			      || processing_contract_condition
> +			      || TREE_STATIC (r)
> +			      || decl_constant_var_p (r)
> +			      || seen_error ());
> +		  if (!processing_template_decl
> +		      && !TREE_STATIC (r))
> +		    r = process_outer_var_ref (r, complain);
> +		}
> +	      /* Remember this for subsequent uses.  */
> +	      if (local_specializations)
> +		register_local_specialization (r, t);
> +	    }
> +	  if (TREE_CODE (r) == ARGUMENT_PACK_SELECT)
> +	    r = argument_pack_select_arg (r);
> +	}
> +      else
> +	r = t;
> +      if (!mark_used (r, complain))
> +	RETURN (error_mark_node);
> +
> +      if (!no_name_lookup_flag
> +	  && (TREE_CODE (t) == PARM_DECL || TREE_CODE (t) == VAR_DECL))
> +	{
> +	  /* ??? We're doing a subset  of finish_id_expression here.  */
> +	  if (tree wrap = maybe_get_tls_wrapper_call (r))
> +	    /* Replace an evaluated use of the thread_local variable with
> +	       a call to its wrapper.  */
> +	    r = wrap;
> +	  else if (outer_automatic_var_p (r))
> +	    r = process_outer_var_ref (r, complain);
> +
> +	  if (!TYPE_REF_P (TREE_TYPE (t)))
> +	    /* If the original type was a reference, we'll be wrapped in
> +	       the appropriate INDIRECT_REF.  */
> +	    r = convert_from_reference (r);
> +	}
> +      RETURN (r);
> +
> +    case CONST_DECL:
> +      {
> +	tree enum_type;
> +	tree v;
> +
> +	if (DECL_TEMPLATE_PARM_P (t))
> +	  RETURN (RECUR (DECL_INITIAL (t)));
> +	if (!uses_template_parms (DECL_CONTEXT (t)))
> +	  RETURN (t);
> +
> +	/* Unfortunately, we cannot just call lookup_name here.
> +	   Consider:
> +
> +	     template <int I> int f() {
> +	     enum E { a = I };
> +	     struct S { void g() { E e = a; } };
> +	     };
> +
> +	   When we instantiate f<7>::S::g(), say, lookup_name is not
> +	   clever enough to find f<7>::a.  */
> +	enum_type
> +	  = tsubst_aggr_type (DECL_CONTEXT (t), args, complain, in_decl,
> +			      /*entering_scope=*/0);
> +
> +	for (v = TYPE_VALUES (enum_type);
> +	     v != NULL_TREE;
> +	     v = TREE_CHAIN (v))
> +	  if (TREE_PURPOSE (v) == DECL_NAME (t))
> +	    RETURN (TREE_VALUE (v));
> +
> +	  /* We didn't find the name.  That should never happen; if
> +	     name-lookup found it during preliminary parsing, we
> +	     should find it again here during instantiation.  */
> +	gcc_unreachable ();
> +	RETURN (t);
>        }
>  
> -    case THROW_EXPR:
> -      RETURN (build_throw
> -       (input_location, RECUR (TREE_OPERAND (t, 0))));
> +    case FIELD_DECL:
> +      if (DECL_CONTEXT (t))
> +	{
> +	  tree ctx;
>  
> -    case CONSTRUCTOR:
> -      {
> -	vec<constructor_elt, va_gc> *n;
> -	constructor_elt *ce;
> -	unsigned HOST_WIDE_INT idx;
> -	bool process_index_p;
> -        int newlen;
> -        bool need_copy_p = false;
> -	tree r;
> +	  ctx = tsubst_aggr_type (DECL_CONTEXT (t), args, complain, in_decl,
> +				  /*entering_scope=*/1);
> +	  if (ctx != DECL_CONTEXT (t))
> +	    {
> +	      tree r = lookup_field (ctx, DECL_NAME (t), 0, false);
> +	      if (!r)
> +		{
> +		  if (complain & tf_error)
> +		    error ("using invalid field %qD", t);
> +		  RETURN (error_mark_node);
> +		}
> +	      RETURN (r);
> +	    }
> +	}
> +      RETURN (t);
>  
> -	tsubst_flags_t tcomplain = complain;
> -	if (COMPOUND_LITERAL_P (t))
> -	  tcomplain |= tf_tst_ok;
> -	tree type = tsubst (TREE_TYPE (t), args, tcomplain, in_decl);
> -	if (type == error_mark_node)
> -	  RETURN (error_mark_node);
> +    case NAMESPACE_DECL:
> +    case OVERLOAD:
> +      RETURN (t);
>  
> -	/* We do not want to process the index of aggregate
> -	   initializers as they are identifier nodes which will be
> -	   looked up by digest_init.  */
> -	process_index_p = !(type && MAYBE_CLASS_TYPE_P (type));
> +    case TEMPLATE_DECL:
> +      if (DECL_TEMPLATE_TEMPLATE_PARM_P (t))
> +	RETURN (tsubst (TREE_TYPE (DECL_TEMPLATE_RESULT (t)),
> +			args, complain, in_decl));
> +      else if (DECL_FUNCTION_TEMPLATE_P (t) && DECL_MEMBER_TEMPLATE_P (t))
> +	RETURN (tsubst (t, args, complain, in_decl));
> +      else if (DECL_CLASS_SCOPE_P (t)
> +	       && uses_template_parms (DECL_CONTEXT (t)))
> +	{
> +	  /* Template template argument like the following example need
> +	     special treatment:
>  
> -	if (null_member_pointer_value_p (t))
> -	  {
> -	    gcc_assert (same_type_p (type, TREE_TYPE (t)));
> -	    RETURN (t);
> -	  }
> +	       template <template <class> class TT> struct C {};
> +	       template <class T> struct D {
> +		 template <class U> struct E {};
> +		 C<E> c;				// #1
> +	       };
> +	       D<int> d;				// #2
>  
> -	n = vec_safe_copy (CONSTRUCTOR_ELTS (t));
> -        newlen = vec_safe_length (n);
> -	FOR_EACH_VEC_SAFE_ELT (n, idx, ce)
> -	  {
> -	    if (ce->index && process_index_p
> -		/* An identifier index is looked up in the type
> -		   being initialized, not the current scope.  */
> -		&& TREE_CODE (ce->index) != IDENTIFIER_NODE)
> -	      ce->index = RECUR (ce->index);
> +	     We are processing the template argument `E' in #1 for
> +	     the template instantiation #2.  Originally, `E' is a
> +	     TEMPLATE_DECL with `D<T>' as its DECL_CONTEXT.  Now we
> +	     have to substitute this with one having context `D<int>'.  */
>  
> -            if (PACK_EXPANSION_P (ce->value))
> -              {
> -                /* Substitute into the pack expansion.  */
> -                ce->value = tsubst_pack_expansion (ce->value, args, complain,
> -                                                  in_decl);
> +	  tree context = tsubst_aggr_type (DECL_CONTEXT (t), args, complain,
> +					   in_decl, /*entering_scope=*/true);
> +	  RETURN (lookup_field (context, DECL_NAME(t), 0, false));
> +	}
> +      else
> +	/* Ordinary template template argument.  */
> +	RETURN (t);
>  
> -		if (ce->value == error_mark_node
> -		    || PACK_EXPANSION_P (ce->value))
> -		  ;
> -		else if (TREE_VEC_LENGTH (ce->value) == 1)
> -                  /* Just move the argument into place.  */
> -                  ce->value = TREE_VEC_ELT (ce->value, 0);
> -                else
> -                  {
> -                    /* Update the length of the final CONSTRUCTOR
> -                       arguments vector, and note that we will need to
> -                       copy.*/
> -                    newlen = newlen + TREE_VEC_LENGTH (ce->value) - 1;
> -                    need_copy_p = true;
> -                  }
> -              }
> -            else
> -              ce->value = RECUR (ce->value);
> -	  }
> +    case TEMPLATE_PARM_INDEX:
> +    case TYPE_DECL:
> +      RETURN (tsubst (t, args, complain, in_decl));
>  
> -        if (need_copy_p)
> -          {
> -            vec<constructor_elt, va_gc> *old_n = n;
> +    case CLEANUP_POINT_EXPR:
> +      /* We shouldn't have built any of these during initial template
> +	 generation.  Instead, they should be built during instantiation
> +	 in response to the saved STMT_IS_FULL_EXPR_P setting.  */
> +      gcc_unreachable ();
>  
> -            vec_alloc (n, newlen);
> -            FOR_EACH_VEC_ELT (*old_n, idx, ce)
> -              {
> -                if (TREE_CODE (ce->value) == TREE_VEC)
> -                  {
> -                    int i, len = TREE_VEC_LENGTH (ce->value);
> -                    for (i = 0; i < len; ++i)
> -                      CONSTRUCTOR_APPEND_ELT (n, 0,
> -                                              TREE_VEC_ELT (ce->value, i));
> -                  }
> -                else
> -                  CONSTRUCTOR_APPEND_ELT (n, 0, ce->value);
> -              }
> -          }
> +    case OFFSET_REF:
> +      {
> +	tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
> +	tree op0 = RECUR (TREE_OPERAND (t, 0));
> +	tree op1 = RECUR (TREE_OPERAND (t, 1));
> +	r = build2 (OFFSET_REF, type, op0, op1);
> +	PTRMEM_OK_P (r) = PTRMEM_OK_P (t);
> +	if (!mark_used (TREE_OPERAND (r, 1), complain)
> +	    && !(complain & tf_error))
> +	  RETURN (error_mark_node);
> +	RETURN (r);
> +      }
>  
> -	r = build_constructor (init_list_type_node, n);
> -	CONSTRUCTOR_IS_DIRECT_INIT (r) = CONSTRUCTOR_IS_DIRECT_INIT (t);
> -	CONSTRUCTOR_IS_DESIGNATED_INIT (r)
> -	  = CONSTRUCTOR_IS_DESIGNATED_INIT (t);
> +    case EXPR_PACK_EXPANSION:
> +      error ("invalid use of pack expansion expression");
> +      RETURN (error_mark_node);
>  
> -	if (TREE_HAS_CONSTRUCTOR (t))
> -	  {
> -	    fcl_t cl = fcl_functional;
> -	    if (CONSTRUCTOR_C99_COMPOUND_LITERAL (t))
> -	      cl = fcl_c99;
> -	    RETURN (finish_compound_literal (type, r, complain, cl));
> -	  }
> +    case NONTYPE_ARGUMENT_PACK:
> +      error ("use %<...%> to expand argument pack");
> +      RETURN (error_mark_node);
>  
> -	TREE_TYPE (r) = type;
> +    case VOID_CST:
> +      gcc_checking_assert (t == void_node && VOID_TYPE_P (TREE_TYPE (t)));
> +      RETURN (t);
> +
> +    case INTEGER_CST:
> +    case REAL_CST:
> +    case COMPLEX_CST:
> +    case VECTOR_CST:
> +      {
> +	/* Instantiate any typedefs in the type.  */
> +	tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
> +	r = fold_convert (type, t);
> +	gcc_assert (TREE_CODE (r) == TREE_CODE (t));
>  	RETURN (r);
>        }
>  
> -    case TYPEID_EXPR:
> +    case STRING_CST:
>        {
> -	tree operand_0 = TREE_OPERAND (t, 0);
> -	if (TYPE_P (operand_0))
> -	  {
> -	    operand_0 = tsubst (operand_0, args, complain, in_decl);
> -	    RETURN (get_typeid (operand_0, complain));
> -	  }
> -	else
> +	tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
> +	r = t;
> +	if (type != TREE_TYPE (t))
>  	  {
> -	    operand_0 = RECUR (operand_0);
> -	    RETURN (build_typeid (operand_0, complain));
> +	    r = copy_node (t);
> +	    TREE_TYPE (r) = type;
>  	  }
> +	RETURN (r);
>        }
>  
> -    case VAR_DECL:
> -      if (!args)
> -	RETURN (t);
> -      /* Fall through */
> +    case PTRMEM_CST:
> +      /* These can sometimes show up in a partial instantiation, but never
> +	 involve template parms.  */
> +      gcc_assert (!uses_template_parms (t));
> +      RETURN (t);
>  
> -    case PARM_DECL:
> -      {
> -	tree r = tsubst_copy (t, args, complain, in_decl);
> -	/* ??? We're doing a subset of finish_id_expression here.  */
> -	if (tree wrap = maybe_get_tls_wrapper_call (r))
> -	  /* Replace an evaluated use of the thread_local variable with
> -	     a call to its wrapper.  */
> -	  r = wrap;
> -	else if (outer_automatic_var_p (r))
> -	  r = process_outer_var_ref (r, complain);
> -
> -	if (!TYPE_REF_P (TREE_TYPE (t)))
> -	  /* If the original type was a reference, we'll be wrapped in
> -	     the appropriate INDIRECT_REF.  */
> -	  r = convert_from_reference (r);
> -	RETURN (r);
> -      }
> +    case UNARY_LEFT_FOLD_EXPR:
> +      RETURN (tsubst_unary_left_fold (t, args, complain, in_decl));
> +    case UNARY_RIGHT_FOLD_EXPR:
> +      RETURN (tsubst_unary_right_fold (t, args, complain, in_decl));
> +    case BINARY_LEFT_FOLD_EXPR:
> +      RETURN (tsubst_binary_left_fold (t, args, complain, in_decl));
> +    case BINARY_RIGHT_FOLD_EXPR:
> +      RETURN (tsubst_binary_right_fold (t, args, complain, in_decl));
> +    case PREDICT_EXPR:
> +      RETURN (t);
> +
> +    case DEBUG_BEGIN_STMT:
> +      /* ??? There's no point in copying it for now, but maybe some
> +	 day it will contain more information, such as a pointer back
> +	 to the containing function, inlined copy or so.  */
> +      RETURN (t);
> +
> +    case CO_AWAIT_EXPR:
> +      RETURN (tsubst_expr (t, args, complain, in_decl));
>  
>      case VA_ARG_EXPR:
>        {
> @@ -21728,8 +21408,11 @@ tsubst_copy_and_build (tree t,
>  
>      case TRAIT_EXPR:
>        {
> -	tree type1 = tsubst_copy (TRAIT_EXPR_TYPE1 (t), args,
> -				  complain, in_decl);
> +	tree type1 = TRAIT_EXPR_TYPE1 (t);
> +	if (TYPE_P (type1))
> +	  type1 = tsubst (type1, args, complain, in_decl);
> +	else
> +	  type1 = tsubst_copy_and_build (type1, args, complain, in_decl);
>  	tree type2 = tsubst (TRAIT_EXPR_TYPE2 (t), args,
>  			     complain, in_decl);
>  	RETURN (finish_trait_expr (TRAIT_EXPR_LOCATION (t),
> @@ -21856,7 +21539,10 @@ tsubst_copy_and_build (tree t,
>  	if (subst)
>  	  RETURN (subst);
>        }
> -      RETURN (tsubst_copy (t, args, complain, in_decl));
> +      /* We shouldn't get here, but keep going if !flag_checking.  */
> +      if (flag_checking)
> +	gcc_unreachable ();
> +      RETURN (t);
>      }
>  
>  #undef RECUR
> @@ -27580,8 +27266,12 @@ tsubst_initializer_list (tree t, tree argvec)
>            else
>              {
>  	      tree tmp;
> -              decl = tsubst_copy (TREE_PURPOSE (t), argvec, 
> -                                  tf_warning_or_error, NULL_TREE);
> +	      if (TYPE_P (TREE_PURPOSE (t)))
> +		decl = tsubst (TREE_PURPOSE (t), argvec,
> +			       tf_warning_or_error, NULL_TREE);
> +	      else
> +		decl = tsubst_copy_and_build (TREE_PURPOSE (t), argvec,
> +					      tf_warning_or_error, NULL_TREE);
>  
>                decl = expand_member_init (decl);
>                if (decl && !DECL_P (decl))
> -- 
> 2.42.0.296.g493f462273
> 
> 


  reply	other threads:[~2023-10-03 12:41 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-09-25 20:43 [PATCH 1/2] c++: remove NON_DEPENDENT_EXPR, part 1 Patrick Palka
2023-09-25 20:43 ` [PATCH 2/2] c++: remove NON_DEPENDENT_EXPR, part 2 Patrick Palka
2023-10-02 19:37   ` [PATCH] c++: merge tsubst_copy into tsubst_copy_and_build Patrick Palka
2023-10-03 12:41     ` Patrick Palka [this message]
2023-10-03 21:57       ` Jason Merrill
2023-10-04 16:08         ` Patrick Palka
2023-10-04 19:23           ` [PATCH 2/1] c++: rename tsubst_copy_and_build and tsubst_expr Patrick Palka
2023-10-19 21:43             ` Jason Merrill
2023-10-19 21:43           ` [PATCH] c++: merge tsubst_copy into tsubst_copy_and_build Jason Merrill
2023-10-19 21:46   ` [PATCH 2/2] c++: remove NON_DEPENDENT_EXPR, part 2 Jason Merrill
2023-10-20 15:57   ` Andrew Pinski
2023-09-26 23:18 ` [PATCH 1/2] c++: remove NON_DEPENDENT_EXPR, part 1 Jason Merrill
2023-09-27 11:54   ` Patrick Palka

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=f1d2aa67-ad78-0ac4-0f69-0f086b1538e3@idea \
    --to=ppalka@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jason@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).