rsmith added inline comments.
================ Comment at: include/clang/AST/TypeLoc.h:1992-1993 + unsigned getExtraLocalDataAlignment() const { + static_assert(alignof(TransformTraitTypeLoc) >= alignof(TypeSourceInfo *), + "not enough alignment for tail-allocated data"); + return alignof(TypeSourceInfo *); ---------------- This assert doesn't make sense: the extra `TypeSourceInfo*`s are not stored after a `TransformTraitTypeLoc`. Rather, `*TypeLoc` is a non-owning veneer around separately-allocated data. `ConcreteTypeLoc` takes care of adding any necessary padding to get to the right alignment for you. ================ Comment at: include/clang/ASTMatchers/ASTMatchers.h:5262-5266 +AST_MATCHER(Type, unaryTransformType) { + if (const auto *T = Node.getAs<TransformTraitType>()) + return T->getNumArgs() == 1; + return false; +} ---------------- Generally I think we want the primitive AST matchers to pretty directly correspond to the Clang AST. I think it would make sense to replace this matcher with a generalized `transformTrait` matcher -- but in a separate commit from this one. This is OK for now. ================ Comment at: lib/AST/ASTContext.cpp:4630 -/// getUnaryTransformationType - We don't unique these, since the memory +/// getTransformTraitType - We don't unique these, since the memory /// savings are minimal and these are rare. ---------------- You can just drop the function name (and hyphen) here; that's an obsolescent style. ================ Comment at: lib/AST/ItaniumMangle.cpp:3250-3251 - mangleType(T->getBaseType()); + for (auto Ty : T->getArgs()) + mangleType(Ty); } ---------------- EricWF wrote: > EricWF wrote: > > rsmith wrote: > > > We need manglings to be self-delimiting, and we can't tell where the end > > > of the argument list is with this mangling approach. :( > > > > > > (Eg, `f(__raw_invocation_type(T1, T2, T3))` and > > > `f(__raw_invocation_type(T1, T2), T3)` would mangle the same with this > > > mangling.) > > > > > > Maybe drop an `E` on the end? (Or maybe do that only for traits that > > > don't require exactly one argument, but that would create pain for > > > demanglers....) > > Makes sense. Thanks for the explanation. I'll go ahead and drop and E on > > the end. > > > > However, will this cause ABI issues? Either with existing code or with GCC? > > If so, perhaps we should maintain the current mangling for > > `__underlying_type`. > To answer my own question, GCC doesn't mangle `__underlying_type` yet. And I > doubt that people are currently depending on the dependent mangling of > `__underlying_type`. Perhaps I'm wrong about that last assumption though. The existing `U3eut` mangling (with no terminating `E`) was approximately OK, following http://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.qualified-type. It would be better to use `U17__underlying_type`, though. This is not exactly right, since it treats `__underlying_type` as a type qualifier rather than a type function, but that's not too far off. The new mangling doesn't match the Itanium ABI rule for vendor extensions. We basically have a choice between `u` <source-name> (which doesn't give us a good way to encode the arguments) or `U` <source-name> <template-args> <type> (which could work, but we'd need to make a somewhat-arbitrary choice of which type we consider to be the "main" type to which the qualifier applies). We could arbitrarily say: * the first type in the trait is the "main" type * the rest of the trait is mangled as a qualifier * the qualifier is given template-args if and only if there is more than one argument So: * `__underlying_type(T)` would mangle as `U17__underlying_type1T` (or approximately `T __underlying_type`) * `__raw_invocation_type(F, A1, A2)` would mangle as `U21__raw_invocation_typeI2A12A2E1F` (or approximately `F __raw_invocation_type<A1, A2>`) * `__raw_invocation_type(F)` would mangle as `U21__raw_invocation_type1F` (or approximately `F __raw_invocation_type`) Or we could track here whether the trait can accept >1 argument, and always use the template-args formulation if so. I have no strong opinions either way. Or we could suggest an Itanium ABI extension to permit <template-args> for the `u...` vendor extension type form, for vendor type traits. That'd lead to: * `__underlying_type(T)` would mangle as `u17__underlying_typeI1TE` (or approximately `__underlying_type<T>`) * `__raw_invocation_type(F, A1, A2)` would mangle as `u21__raw_invocation_typeI1F2A12A2E` (or approximately `__raw_invocation_type<F, A1, A2>`) * `__raw_invocation_type(F)` would mangle as `u21__raw_invocation_typeI1FE` (or approximately `__raw_invocation_type<F>`) ... and we could encourage demanglers to use parens instead of angles for those arguments. The final of the above options seems best to me. What do you think? ================ Comment at: lib/AST/Type.cpp:3081-3082 + new ((void *)(ArgStorage + I)) QualType(T); + if (T->isDependentType()) + this->setDependent(true); + if (T->isInstantiationDependentType()) ---------------- This should come from the transformed type, not the base type. (A transform trait could turn a dependent type into a non-depnedent type.) ================ Comment at: lib/AST/Type.cpp:3085-3086 + this->setInstantiationDependent(true); + if (T->isVariablyModifiedType()) + this->setVariablyModified(true); + if (T->containsUnexpandedParameterPack()) ---------------- Likewise, there's no reason to think that variably-modifiedness of the arguments has anything to do with variably-modifiedness of the transformed type. (Eg, `__raw_invocation_type(Fn, VLAType)` is not going to be a VLA type.) ================ Comment at: lib/AST/Type.cpp:3089 + this->setContainsUnexpandedParameterPack(true); + } +} ---------------- (And just for clarity: instantiation-dependence and contains-unexpanded-parameter-pack are concerned with the syntactic form of the type, not the semantics, so those two *should* be based on the corresponding properties of the arguments.) ================ Comment at: lib/Lex/PPMacroExpansion.cpp:1245 //.Case("cxx_concepts", LangOpts.CPlusPlusTSConcepts) // FIXME: Should this be __has_feature or __has_extension? // Type traits ---------------- Remove this fixme too, please. (But let's hold off on this change until you land the `__raw_invocation_type` patch to keep this patch more focused.) ================ Comment at: lib/Parse/ParseDeclCXX.cpp:1058 + if (Ty.isInvalid()) { + Parens.skipToEnd(); + return; ---------------- Call `DS.SetTypeSpecError()` here and at other points where you bail out of this function. ================ Comment at: lib/Parse/ParseDeclCXX.cpp:1073-1074 + if (!HasPackExpand) { + auto PDiag = Actions.CheckTransformTraitArity(KindInfo.first, Args.size(), + SourceRange(StartLoc)); + if (PDiag.hasValue()) { ---------------- Instead of performing an ad-hoc arity check here, perform the check in the code that converts a kind + list of arguments into a `TransformTraitType`. That way there's no way to forget these checks and no side channel state to track about whether you've already done them. ================ Comment at: lib/Sema/DeclSpec.cpp:369-370 case TST_underlyingType: + return false; + ---------------- This is going to be problematic down the line -- I expect there are going to be type transform traits that can produce function types. I think this strongly argues that we need to have already converted the trait into a `QualType` by the time we get here, rather than representing it as a trait kind and a list of types in the `DeclSpec` and deferring the conversion to later. ================ Comment at: lib/Sema/SemaType.cpp:1508-1509 + break; + default: + llvm_unreachable("unhandled case"); + } ---------------- EricWF wrote: > rsmith wrote: > > Try to avoid `default:` cases like this, as they suppress the warning > > notifying the user to update this switch when new traits are added. > Understood, but surely this is a case where `default:` is warranted. We're > switching over a range of values much larger than > `TransformTraitType::TTKind` in order to transform it into the `TTKind` > > Do you have suggestions for improving it? I think you should convert the trait to a `QualType` when you parse it, rather than waiting until now, which would make this moot. ================ Comment at: lib/Sema/SemaType.cpp:8014-8022 + if (DiagSelect.hasValue()) { + auto Info = DiagSelect.getValue(); + PartialDiagnostic PD = PDiag(diag::err_type_trait_arity); + PD << Info.ReqNumArgs << Info.SelectOne << (Info.ReqNumArgs != 1) + << (int)NumArgs << R; + return PD; + } ---------------- Do you really need a `PartialDiagnostic` here? (Could you directly emit the diagnostic instead?) ================ Comment at: lib/Sema/SemaType.cpp:8027-8029 + auto MakeTrait = [&](QualType TransformedType) { + return Context.getTransformTraitType(ArgTypes, TransformedType, TKind); + }; ---------------- I think this would be clearer if split into a function that computes and returns the result type of the trait and code to build the trait from that type, rather than this factored-out lambda approach -- this use of a lambda hides the fundamental behavior of this function, which is "first figure out the type and then build a trait producing that type". ================ Comment at: lib/Sema/SemaType.cpp:8036 - DiagnoseUseOfDecl(ED, Loc); + // Delay all checking while any of the arguments are instantiation dependent. + if (IsInstantDependent) ---------------- This doesn't seem right; you should delay if any of the arguments is dependent, but instantiation-dependent / containing a pack don't seem like they should matter here. https://reviews.llvm.org/D45131 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits