From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 8E0EE3858D1E for ; Tue, 14 Feb 2023 22:49:57 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 8E0EE3858D1E Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1676414996; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=D0GZwPSVMRI1VTspzhp7XTevDG/KCswJLFcGmnXzTJs=; b=NK3+sDJBJEh0DPEPYDTV24nilZJs3pPcsBVRAP/N10Nt0nMiIxpAQyv8XRYpZ98ZPu6m+R sfsVlI+1D6as9QZvCjRb2FmqVYpQh9FJPgZE/Jy2uzz7ewbaCOzPCblTS/Y9Aa0MJ7rUlv jZQc7UBql0NCzSAWSju/SH7hTcwr+bY= Received: from mail-qt1-f200.google.com (mail-qt1-f200.google.com [209.85.160.200]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-341-G3Wj_RQmMIi5cxzne9HOBw-1; Tue, 14 Feb 2023 17:49:55 -0500 X-MC-Unique: G3Wj_RQmMIi5cxzne9HOBw-1 Received: by mail-qt1-f200.google.com with SMTP id i5-20020ac813c5000000b003b86b748aadso10263506qtj.14 for ; Tue, 14 Feb 2023 14:49:55 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:in-reply-to:references:to:from :content-language:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=D0GZwPSVMRI1VTspzhp7XTevDG/KCswJLFcGmnXzTJs=; b=P85SocbGbJBfQOWlISTy3/ANWLkybcBz1UHvKpmsjDg8/gVoD89/yvfMUi210kzPmH 3AIbQnPf/sSJC0rBWgt2twW5h0KUi8l0Nr7kiF8UBcuRoRjdKyrtzKJcEC5liGgg+mbb MHDdfylmJ3OuWQvJGnPBA5MVfKKeVtNVmVKTg4WU6UAd8N1gSTUhvJMPV5bluzVDRNFl MZi6QCBZNvn+JHGSb6tT6kUt/FHaW6+/DhhXptMVsNj57VDcQxJVrV/TXGkB/AFZjtY/ Pi5JsOKrFQa+GGgw3jO7ZiYllk1MAlvNSjFOimPGQPPyeklGsDquNzbnhNGPwp7DP4hb EZ7Q== X-Gm-Message-State: AO0yUKWoUILhirJcQULCyFNlmIs6xcmLTb241yTr7jkWxQrMKu8Uddjz Rb4FsH/FS8WN/CTzBr/AdPXzFzBLsoBCdkD6okR66P5+kGwi9D1R8bV+jZAG5qV7uXBCK2ywNl+ tiY+FNIqm1OFEiB/xiwbF3Is= X-Received: by 2002:ac8:5c52:0:b0:3b8:60b9:e75e with SMTP id j18-20020ac85c52000000b003b860b9e75emr83635qtj.4.1676414994830; Tue, 14 Feb 2023 14:49:54 -0800 (PST) X-Google-Smtp-Source: AK7set8gy+E6YF061QQ6DMv1JgPOO543F9v4UZcsLcq1qevCsW4MkIshTvts4B5zVF02893MqTaG1g== X-Received: by 2002:ac8:5c52:0:b0:3b8:60b9:e75e with SMTP id j18-20020ac85c52000000b003b860b9e75emr83598qtj.4.1676414994400; Tue, 14 Feb 2023 14:49:54 -0800 (PST) Received: from [192.168.1.108] (130-44-159-43.s15913.c3-0.arl-cbr1.sbo-arl.ma.cable.rcncustomer.com. [130.44.159.43]) by smtp.gmail.com with ESMTPSA id a186-20020a37b1c3000000b00739079104dfsm10861374qkf.37.2023.02.14.14.49.53 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 14 Feb 2023 14:49:53 -0800 (PST) Message-ID: <9e5eb102-39ca-e327-23bb-6c154ee16940@redhat.com> Date: Tue, 14 Feb 2023 17:49:51 -0500 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.7.2 Subject: Re: [PATCH 1/2] c++: factor out TYPENAME_TYPE substitution From: Jason Merrill To: Patrick Palka , gcc-patches@gcc.gnu.org References: <20230213172340.849204-1-ppalka@redhat.com> <1a709771-253a-780c-335e-a5819a063483@redhat.com> In-Reply-To: <1a709771-253a-780c-335e-a5819a063483@redhat.com> X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-11.1 required=5.0 tests=BAYES_00,BODY_8BITS,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,KAM_SHORT,NICE_REPLY_A,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On 2/14/23 14:15, Jason Merrill wrote: > On 2/13/23 09:23, Patrick Palka wrote: >> [N.B. this is a corrected version of >> https://gcc.gnu.org/pipermail/gcc-patches/2022-November/607443.html ] >> >> This patch factors out the TYPENAME_TYPE case of tsubst into a separate >> function tsubst_typename_type.  It also factors out the two tsubst flags >> controlling TYPENAME_TYPE substitution, tf_keep_type_decl and tf_tst_ok, >> into distinct boolean parameters of this new function (and of >> make_typename_type).  Consequently, callers which used to pass tf_tst_ok >> to tsubst now instead must directly call tsubst_typename_type when >> appropriate. > > Hmm, I don't love how that turns 4 lines into 8 more complex lines in > each caller.  And the previous approach of saying "a CTAD placeholder is > OK" seem like better abstraction than repeating the specific > TYPENAME_TYPE handling in each place. tsubst_maybe_ctad_type? >> In a subsequent patch we'll add another flag to >> tsubst_typename_type controlling whether we want to ignore non-types >> during the qualified lookup. >> >> gcc/cp/ChangeLog: >> >>     * cp-tree.h (enum tsubst_flags): Remove tf_keep_type_decl >>     and tf_tst_ok. >>     (make_typename_type): Add two trailing boolean parameters >>     defaulted to false. >>     * decl.cc (make_typename_type): Replace uses of >>     tf_keep_type_decl and tf_tst_ok with the corresponding new >>     boolean parameters. >>     * pt.cc (tsubst_typename_type): New, factored out from tsubst >>     and adjusted after removing tf_keep_type_decl and tf_tst_ok. >>     (tsubst_decl) : Conditionally call >>     tsubst_typename_type directly instead of using tf_tst_ok. >>     (tsubst) : Call tsubst_typename_type. >>     (tsubst_copy) : Conditionally call >>     tsubst_typename_type directly instead of using tf_tst_ok. >>     (tsubst_copy_and_build) : Likewise. >>     : Likewise. >> --- >>   gcc/cp/cp-tree.h |   9 +- >>   gcc/cp/decl.cc   |  17 ++-- >>   gcc/cp/pt.cc     | 223 +++++++++++++++++++++++++---------------------- >>   3 files changed, 134 insertions(+), 115 deletions(-) >> >> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h >> index 06bc64a6b8d..a7c5765fc33 100644 >> --- a/gcc/cp/cp-tree.h >> +++ b/gcc/cp/cp-tree.h >> @@ -5573,8 +5573,7 @@ enum tsubst_flags { >>     tf_error = 1 << 0,         /* give error messages  */ >>     tf_warning = 1 << 1,          /* give warnings too  */ >>     tf_ignore_bad_quals = 1 << 2,     /* ignore bad cvr qualifiers */ >> -  tf_keep_type_decl = 1 << 3,     /* retain typedef type decls >> -                    (make_typename_type use) */ >> +  /* 1 << 3 available */ >>     tf_ptrmem_ok = 1 << 4,     /* pointers to member ok (internal >>                       instantiate_type use) */ >>     tf_user = 1 << 5,         /* found template must be a user template >> @@ -5594,8 +5593,7 @@ enum tsubst_flags { >>                   (build_target_expr and friends) */ >>     tf_norm = 1 << 11,         /* Build diagnostic information during >>                       constraint normalization.  */ >> -  tf_tst_ok = 1 << 12,         /* Allow a typename-specifier to name >> -                    a template (C++17 or later).  */ >> +  /* 1 << 12 available */ >>     tf_dguide = 1 << 13,        /* Building a deduction guide from a >> ctor.  */ >>     /* Convenient substitution flags combinations.  */ >>     tf_warning_or_error = tf_warning | tf_error >> @@ -6846,7 +6844,8 @@ extern tree declare_local_label            (tree); >>   extern tree define_label            (location_t, tree); >>   extern void check_goto                (tree); >>   extern bool check_omp_return            (void); >> -extern tree make_typename_type            (tree, tree, enum >> tag_types, tsubst_flags_t); >> +extern tree make_typename_type            (tree, tree, enum >> tag_types, tsubst_flags_t, >> +                         bool = false, bool = false); >>   extern tree build_typename_type            (tree, tree, tree, >> tag_types); >>   extern tree make_unbound_class_template        (tree, tree, tree, >> tsubst_flags_t); >>   extern tree make_unbound_class_template_raw    (tree, tree, tree); >> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc >> index d606b31d7a7..430533606b0 100644 >> --- a/gcc/cp/decl.cc >> +++ b/gcc/cp/decl.cc >> @@ -4228,14 +4228,17 @@ build_typename_type (tree context, tree name, >> tree fullname, >>   /* Resolve `typename CONTEXT::NAME'.  TAG_TYPE indicates the tag >>      provided to name the type.  Returns an appropriate type, unless an >>      error occurs, in which case error_mark_node is returned.  If we >> -   locate a non-artificial TYPE_DECL and TF_KEEP_TYPE_DECL is set, we >> +   locate a non-artificial TYPE_DECL and KEEP_TYPE_DECL is true, we >>      return that, rather than the _TYPE it corresponds to, in other >> -   cases we look through the type decl.  If TF_ERROR is set, complain >> -   about errors, otherwise be quiet.  */ >> +   cases we look through the type decl.  If TEMPLATE_OK is true and >> +   we found a TEMPLATE_DECL then we return a CTAD placeholder for the >> +   TEMPLATE_DECL.  If TF_ERROR is set, complain about errors, otherwise >> +   be quiet.  */ >>   tree >>   make_typename_type (tree context, tree name, enum tag_types tag_type, >> -            tsubst_flags_t complain) >> +            tsubst_flags_t complain, bool keep_type_decl /* = false */, >> +            bool template_ok /* = false */) >>   { >>     tree fullname; >>     tree t; >> @@ -4352,8 +4355,8 @@ make_typename_type (tree context, tree name, >> enum tag_types tag_type, >>       } >>     if (!want_template && TREE_CODE (t) != TYPE_DECL) >>       { >> -      if ((complain & tf_tst_ok) && cxx_dialect >= cxx17 >> -      && DECL_TYPE_TEMPLATE_P (t)) >> +      if (template_ok && DECL_TYPE_TEMPLATE_P (t) >> +      && cxx_dialect >= cxx17) >>       /* The caller permits this typename-specifier to name a template >>          (because it appears in a CTAD-enabled context).  */; >>         else >> @@ -4383,7 +4386,7 @@ make_typename_type (tree context, tree name, >> enum tag_types tag_type, >>         t = TYPE_NAME (t); >>       } >> -  if (DECL_ARTIFICIAL (t) || !(complain & tf_keep_type_decl)) >> +  if (DECL_ARTIFICIAL (t) || !keep_type_decl) >>       t = TREE_TYPE (t); >>     maybe_record_typedef_use (t); >> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc >> index e89dbf47097..bc47bf15d38 100644 >> --- a/gcc/cp/pt.cc >> +++ b/gcc/cp/pt.cc >> @@ -13949,6 +13949,94 @@ tsubst_aggr_type_1 (tree t, >>       return t; >>   } >> +/* Substitute ARGS into the TYPENAME_TYPE T.  The flag TEMPLATE_OK >> +   is passed to make_typename_type.  */ >> + >> +static tree >> +tsubst_typename_type (tree t, tree args, tsubst_flags_t complain, >> tree in_decl, >> +              bool template_ok = false) >> +{ >> +  tree ctx = TYPE_CONTEXT (t); >> +  if (TREE_CODE (ctx) == TYPE_PACK_EXPANSION) >> +    { >> +      ctx = tsubst_pack_expansion (ctx, args, complain, in_decl); >> +      if (ctx == error_mark_node >> +      || TREE_VEC_LENGTH (ctx) > 1) >> +    return error_mark_node; >> +      if (TREE_VEC_LENGTH (ctx) == 0) >> +    { >> +      if (complain & tf_error) >> +        error ("%qD is instantiated for an empty pack", >> +           TYPENAME_TYPE_FULLNAME (t)); >> +      return error_mark_node; >> +    } >> +      ctx = TREE_VEC_ELT (ctx, 0); >> +    } >> +  else >> +    ctx = tsubst_aggr_type (ctx, args, complain, in_decl, >> +                /*entering_scope=*/1); >> +  if (ctx == error_mark_node) >> +    return error_mark_node; >> + >> +  tree f = tsubst_copy (TYPENAME_TYPE_FULLNAME (t), args, >> +            complain, in_decl); >> +  if (f == error_mark_node) >> +    return error_mark_node; >> + >> +  if (!MAYBE_CLASS_TYPE_P (ctx)) >> +    { >> +      if (complain & tf_error) >> +    error ("%qT is not a class, struct, or union type", ctx); >> +      return error_mark_node; >> +    } >> +  else if (!uses_template_parms (ctx) && !TYPE_BEING_DEFINED (ctx)) >> +    { >> +      /* Normally, make_typename_type does not require that the CTX >> +     have complete type in order to allow things like: >> + >> +       template struct S { typename S::X Y; }; >> + >> +     But, such constructs have already been resolved by this >> +     point, so here CTX really should have complete type, unless >> +     it's a partial instantiation.  */ >> +      if (!complete_type_or_maybe_complain (ctx, NULL_TREE, complain)) >> +    return error_mark_node; >> +    } >> + >> +  f = make_typename_type (ctx, f, typename_type, complain, >> +              /*keep_type_decl=*/true, template_ok); >> +  if (f == error_mark_node) >> +    return f; >> +  if (TREE_CODE (f) == TYPE_DECL) >> +    { >> +      complain |= tf_ignore_bad_quals; >> +      f = TREE_TYPE (f); >> +    } >> + >> +  if (TREE_CODE (f) != TYPENAME_TYPE) >> +    { >> +      if (TYPENAME_IS_ENUM_P (t) && TREE_CODE (f) != ENUMERAL_TYPE) >> +    { >> +      if (complain & tf_error) >> +        error ("%qT resolves to %qT, which is not an enumeration type", >> +           t, f); >> +      else >> +        return error_mark_node; >> +    } >> +      else if (TYPENAME_IS_CLASS_P (t) && !CLASS_TYPE_P (f)) >> +    { >> +      if (complain & tf_error) >> +        error ("%qT resolves to %qT, which is not a class type", >> +           t, f); >> +      else >> +        return error_mark_node; >> +    } >> +    } >> + >> +  return cp_build_qualified_type >> +    (f, cp_type_quals (f) | cp_type_quals (t), complain); >> +} >> + >>   /* Map from a FUNCTION_DECL to a vec of default argument >> instantiations, >>      indexed in reverse order of the parameters.  */ >> @@ -15193,10 +15281,13 @@ tsubst_decl (tree t, tree args, >> tsubst_flags_t complain) >>           && VAR_HAD_UNKNOWN_BOUND (t) >>           && type != error_mark_node) >>             type = strip_array_domain (type); >> -        tsubst_flags_t tcomplain = complain; >> -        if (VAR_P (t)) >> -          tcomplain |= tf_tst_ok; >> -        type = tsubst (type, args, tcomplain, in_decl); >> +        if (VAR_P (t) >> +        && TREE_CODE (type) == TYPENAME_TYPE >> +        && !typedef_variant_p (type)) >> +          type = tsubst_typename_type (type, args, complain, in_decl, >> +                       /*template_ok=*/true); >> +        else >> +          type = tsubst (type, args, complain, in_decl); >>           /* Substituting the type might have recursively instantiated >> this >>              same alias (c++/86171).  */ >>           if (gen_tmpl && DECL_ALIAS_TEMPLATE_P (gen_tmpl) >> @@ -15889,9 +15980,6 @@ tsubst (tree t, tree args, tsubst_flags_t >> complain, tree in_decl) >>     bool fndecl_type = (complain & tf_fndecl_type); >>     complain &= ~tf_fndecl_type; >> -  bool tst_ok = (complain & tf_tst_ok); >> -  complain &= ~tf_tst_ok; >> - >>     if (type >>         && code != TYPENAME_TYPE >>         && code != TEMPLATE_TYPE_PARM >> @@ -16424,89 +16512,7 @@ tsubst (tree t, tree args, tsubst_flags_t >> complain, tree in_decl) >>         } >>       case TYPENAME_TYPE: >> -      { >> -    tree ctx = TYPE_CONTEXT (t); >> -    if (TREE_CODE (ctx) == TYPE_PACK_EXPANSION) >> -      { >> -        ctx = tsubst_pack_expansion (ctx, args, complain, in_decl); >> -        if (ctx == error_mark_node >> -        || TREE_VEC_LENGTH (ctx) > 1) >> -          return error_mark_node; >> -        if (TREE_VEC_LENGTH (ctx) == 0) >> -          { >> -        if (complain & tf_error) >> -          error ("%qD is instantiated for an empty pack", >> -             TYPENAME_TYPE_FULLNAME (t)); >> -        return error_mark_node; >> -          } >> -        ctx = TREE_VEC_ELT (ctx, 0); >> -      } >> -    else >> -      ctx = tsubst_aggr_type (ctx, args, complain, in_decl, >> -                  /*entering_scope=*/1); >> -    if (ctx == error_mark_node) >> -      return error_mark_node; >> - >> -    tree f = tsubst_copy (TYPENAME_TYPE_FULLNAME (t), args, >> -                  complain, in_decl); >> -    if (f == error_mark_node) >> -      return error_mark_node; >> - >> -    if (!MAYBE_CLASS_TYPE_P (ctx)) >> -      { >> -        if (complain & tf_error) >> -          error ("%qT is not a class, struct, or union type", ctx); >> -        return error_mark_node; >> -      } >> -    else if (!uses_template_parms (ctx) && !TYPE_BEING_DEFINED (ctx)) >> -      { >> -        /* Normally, make_typename_type does not require that the CTX >> -           have complete type in order to allow things like: >> - >> -         template struct S { typename S::X Y; }; >> - >> -           But, such constructs have already been resolved by this >> -           point, so here CTX really should have complete type, unless >> -           it's a partial instantiation.  */ >> -        if (!complete_type_or_maybe_complain (ctx, NULL_TREE, complain)) >> -          return error_mark_node; >> -      } >> - >> -    tsubst_flags_t tcomplain = complain | tf_keep_type_decl; >> -    if (tst_ok) >> -      tcomplain |= tf_tst_ok; >> -    f = make_typename_type (ctx, f, typename_type, tcomplain); >> -    if (f == error_mark_node) >> -      return f; >> -    if (TREE_CODE (f) == TYPE_DECL) >> -      { >> -        complain |= tf_ignore_bad_quals; >> -        f = TREE_TYPE (f); >> -      } >> - >> -    if (TREE_CODE (f) != TYPENAME_TYPE) >> -      { >> -        if (TYPENAME_IS_ENUM_P (t) && TREE_CODE (f) != ENUMERAL_TYPE) >> -          { >> -        if (complain & tf_error) >> -          error ("%qT resolves to %qT, which is not an enumeration >> type", >> -             t, f); >> -        else >> -          return error_mark_node; >> -          } >> -        else if (TYPENAME_IS_CLASS_P (t) && !CLASS_TYPE_P (f)) >> -          { >> -        if (complain & tf_error) >> -          error ("%qT resolves to %qT, which is not a class type", >> -             t, f); >> -        else >> -          return error_mark_node; >> -          } >> -      } >> - >> -    return cp_build_qualified_type >> -      (f, cp_type_quals (f) | cp_type_quals (t), complain); >> -      } >> +      return tsubst_typename_type (t, args, complain, in_decl); >>       case UNBOUND_CLASS_TEMPLATE: >>         { >> @@ -17391,10 +17397,14 @@ tsubst_copy (tree t, tree args, >> tsubst_flags_t complain, tree in_decl) >>       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 type = TREE_TYPE (t); >> +    if (code == CAST_EXPR >> +        && TREE_CODE (type) == TYPENAME_TYPE >> +        && !typedef_variant_p (type)) >> +      type = tsubst_typename_type (type, args, complain, in_decl, >> +                       /*template_ok=*/true); >> +    else >> +      type = tsubst (type, args, complain, in_decl); >>       tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, >> in_decl); >>       return build1 (code, type, op0); >>         } >> @@ -20430,13 +20440,16 @@ tsubst_copy_and_build (tree t, >>       case DYNAMIC_CAST_EXPR: >>       case STATIC_CAST_EXPR: >>         { >> -    tree type; >> +    tree type = TREE_TYPE (t); >>       tree op, r = NULL_TREE; >> -    tsubst_flags_t tcomplain = complain; >> -    if (TREE_CODE (t) == CAST_EXPR) >> -      tcomplain |= tf_tst_ok; >> -    type = tsubst (TREE_TYPE (t), args, tcomplain, in_decl); >> +    if (TREE_CODE (t) == CAST_EXPR >> +        && TREE_CODE (type) == TYPENAME_TYPE >> +        && !typedef_variant_p (type)) >> +      type = tsubst_typename_type (type, args, complain, in_decl, >> +                       /*template_ok=*/true); >> +    else >> +      type = tsubst (type, args, complain, in_decl); >>       op = RECUR (TREE_OPERAND (t, 0)); >> @@ -21421,10 +21434,14 @@ tsubst_copy_and_build (tree t, >>           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); >> +    tree type = TREE_TYPE (t); >> +    if (COMPOUND_LITERAL_P (t) >> +        && TREE_CODE (type) == TYPENAME_TYPE >> +        && !typedef_variant_p (type)) >> +      type = tsubst_typename_type (type, args, complain, in_decl, >> +                       /*template_ok=*/true); >> +    else >> +      type = tsubst (type, args, complain, in_decl); >>       if (type == error_mark_node) >>         RETURN (error_mark_node); >