[cfe-dev] Rudimentary 'auto' type deduction implementation
Douglas Gregor
dgregor at apple.com
Mon Dec 20 22:21:29 PST 2010
On Dec 15, 2010, at 10:23 PM, Michael Price wrote:
> Hi Everyone,
>
> Way back in August, I had a rudimentary implementation of the C++0x type deduction via the 'auto' type. I never got around to adding the appropriate tests and then there was a major refactor of much of the Sema code that I think made my implementation seem out of place. I've finally gotten around to looking at this again and would like the advice of those more experienced in the type system.
>
> Also, I wasn't sure if I should attach my diff as a file or embed it inline, so I've done both.
>
> Thanks ahead of time for your help!
>
>
> Index: include/clang/Sema/Sema.h
> ===================================================================
> --- include/clang/Sema/Sema.h (revision 121900)
> +++ include/clang/Sema/Sema.h (working copy)
> @@ -986,6 +986,13 @@
> bool IsOverload(FunctionDecl *New, FunctionDecl *Old, bool IsForUsingDecl);
>
> bool TryImplicitConversion(InitializationSequence &Sequence,
> + QualType ToType,
> + Expr *From,
> + bool SuppressUserConversions,
> + bool AllowExplicit,
> + bool InOverloadResolution);
> +
> + bool TryImplicitConversion(InitializationSequence &Sequence,
> const InitializedEntity &Entity,
> Expr *From,
> bool SuppressUserConversions,
> Index: include/clang/Sema/Initialization.h
> ===================================================================
> --- include/clang/Sema/Initialization.h (revision 121900)
> +++ include/clang/Sema/Initialization.h (working copy)
> @@ -552,6 +552,8 @@
>
> /// \brief Steps taken by this initialization.
> llvm::SmallVector<Step, 4> Steps;
> +
> + QualType DeducedType;
>
> public:
> /// \brief Describes why initialization failed.
> Index: include/clang/AST/CanonicalType.h
> ===================================================================
> --- include/clang/AST/CanonicalType.h (revision 121900)
> +++ include/clang/AST/CanonicalType.h (working copy)
> @@ -277,6 +277,7 @@
> LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isUnionType)
> LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isComplexIntegerType)
> LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isNullPtrType)
> + LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isUndeducedAutoType)
> LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isDependentType)
> LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isOverloadableType)
> LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isArrayType)
> Index: include/clang/AST/Type.h
> ===================================================================
> --- include/clang/AST/Type.h (revision 121900)
> +++ include/clang/AST/Type.h (working copy)
> @@ -1176,6 +1176,7 @@
> bool isObjCBuiltinType() const; // 'id' or 'Class'
> bool isTemplateTypeParmType() const; // C++ template type parameter
> bool isNullPtrType() const; // C++0x nullptr_t
> + bool isUndeducedAutoType() const; // C++0x yet to be deduced 'auto'
>
> enum ScalarTypeKind {
> STK_Pointer,
> Index: lib/Sema/SemaInit.cpp
> ===================================================================
> --- lib/Sema/SemaInit.cpp (revision 121900)
> +++ lib/Sema/SemaInit.cpp (working copy)
> @@ -3094,12 +3094,6 @@
> // parenthesized list of expressions.
> QualType DestType = Entity.getType();
>
> - if (DestType->isDependentType() ||
> - Expr::hasAnyTypeDependentArguments(Args, NumArgs)) {
> - SequenceKind = DependentSequence;
> - return;
> - }
> -
> for (unsigned I = 0; I != NumArgs; ++I)
> if (Args[I]->getObjectKind() == OK_ObjCProperty)
> S.ConvertPropertyForRValue(Args[I]);
> @@ -3111,6 +3105,19 @@
> if (!isa<InitListExpr>(Initializer))
> SourceType = Initializer->getType();
> }
> +
> + if (S.getLangOptions().CPlusPlus0x) {
> + if (DestType->isUndeducedAutoType() && !SourceType.isNull()) { // TODO: Can 'auto' be initialized with a list?
> + DeducedType = SourceType;
> + DestType = SourceType;
> + }
> + }
There's more to it than just copying the deduced type over. For one, there may not be a source type at this point, e.g., if we are attempting to direct-initialize with multiple arguments. Also, there's some matching that needs to be done.
int x;
auto *y = x; // should be an error
The process is like a restricted form of template argument deduction, but much simpler.
> + if (DestType->isDependentType() ||
> + Expr::hasAnyTypeDependentArguments(Args, NumArgs)) {
> + SequenceKind = DependentSequence;
> + return;
> + }
Are we sure that it's safe to move this check down here? For example, did you verify that ConvertPropertyForRValue does the right thing for type-dependent expressions?
> // - If the initializer is a braced-init-list, the object is
> // list-initialized (8.5.4).
> @@ -3183,7 +3190,7 @@
> (Context.hasSameUnqualifiedType(SourceType, DestType) ||
> S.IsDerivedFrom(SourceType, DestType))))
> TryConstructorInitialization(S, Entity, Kind, Args, NumArgs,
> - Entity.getType(), *this);
> + DestType, *this);
> // - Otherwise (i.e., for the remaining copy-initialization cases),
> // user-defined conversion sequences that can convert from the source
> // type to the destination type or (when a conversion function is
> @@ -3213,7 +3220,7 @@
> // conversions (Clause 4) will be used, if necessary, to convert the
> // initializer expression to the cv-unqualified version of the
> // destination type; no user-defined conversions are considered.
> - if (S.TryImplicitConversion(*this, Entity, Initializer,
> + if (S.TryImplicitConversion(*this, DestType, Initializer,
> /*SuppressUserConversions*/ true,
> /*AllowExplicitConversions*/ false,
> /*InOverloadResolution*/ false))
> @@ -3548,6 +3555,10 @@
> return ExprError();
> }
>
> + if (ResultType && !DeducedType.isNull()) {
> + *ResultType = DeducedType;
> + }
> +
> if (SequenceKind == DependentSequence) {
> // If the declaration is a non-dependent, incomplete array type
> // that has an initializer, then its type will be completed once
> @@ -3609,7 +3620,7 @@
> // FIXME: Ugly hack around the fact that Entity.getType() is not
> // the same as Entity.getDecl()->getType() in cases involving type merging,
> // and we want latter when it makes sense.
> - if (ResultType)
> + if (ResultType && DeducedType.isNull())
> *ResultType = Entity.getDecl() ? Entity.getDecl()->getType() :
> Entity.getType();
>
> Index: lib/Sema/SemaOverload.cpp
> ===================================================================
> --- lib/Sema/SemaOverload.cpp (revision 121900)
> +++ lib/Sema/SemaOverload.cpp (working copy)
> @@ -834,23 +834,37 @@
> }
>
> bool Sema::TryImplicitConversion(InitializationSequence &Sequence,
> - const InitializedEntity &Entity,
> + QualType ToType,
> Expr *Initializer,
> bool SuppressUserConversions,
> bool AllowExplicitConversions,
> bool InOverloadResolution) {
> ImplicitConversionSequence ICS
> - = clang::TryImplicitConversion(*this, Initializer, Entity.getType(),
> + = clang::TryImplicitConversion(*this, Initializer, ToType,
> SuppressUserConversions,
> AllowExplicitConversions,
> InOverloadResolution);
> if (ICS.isBad()) return true;
>
> // Perform the actual conversion.
> - Sequence.AddConversionSequenceStep(ICS, Entity.getType());
> + Sequence.AddConversionSequenceStep(ICS, ToType);
> return false;
> }
>
> +
> +bool Sema::TryImplicitConversion(InitializationSequence &Sequence,
> + const InitializedEntity &Entity,
> + Expr *Initializer,
> + bool SuppressUserConversions,
> + bool AllowExplicitConversions,
> + bool InOverloadResolution) {
> + return TryImplicitConversion(Sequence, Entity.getType(),
> + Initializer, SuppressUserConversions,
> + AllowExplicitConversions, InOverloadResolution);
> +}
> +
> +
> +
> /// PerformImplicitConversion - Perform an implicit conversion of the
> /// expression From to the type ToType. Returns true if there was an
> /// error, false otherwise. The expression From is replaced with the
> Index: lib/AST/Type.cpp
> ===================================================================
> --- lib/AST/Type.cpp (revision 121900)
> +++ lib/AST/Type.cpp (working copy)
> @@ -862,6 +862,12 @@
> return false;
> }
>
> +bool Type::isUndeducedAutoType() const {
> + if (const BuiltinType *BT = getAs<BuiltinType>())
> + return BT->getKind() == BuiltinType::UndeducedAuto;
> + return false;
> +}
Would this also work for reference-to-auto and pointer-to-auto cases?
There's one big thing missing here that I'd like to see addressed in any implementation of "auto": the AST should represent the fact that (1) auto was used in the declaration, and (2) what type was deduced for the "auto" keyword itself. For example, given
int x;
auto *xp = &x;
we want to know that the "auto" was deduced to "int".
- Doug
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20101220/ec7f1ae4/attachment.html>
More information about the cfe-dev
mailing list