File: templateresolver.cpp

package info (click to toggle)
kdevelop 4%3A4.7.0-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 18,360 kB
  • ctags: 11,484
  • sloc: cpp: 105,371; python: 522; ansic: 488; lex: 422; sh: 139; ruby: 120; makefile: 51; xml: 42; php: 12
file content (351 lines) | stat: -rw-r--r-- 15,415 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
/*
   Copyright 2012 Olivier de Gaalon <olviier.jg@gmail.com>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include "templateresolver.h"
#include "cpptypes.h"
#include "templatedeclaration.h"
#include "templateparameterdeclaration.h"

using namespace Cpp;
using namespace KDevelop;

static bool isConstBased(const AbstractType::Ptr& type)
{
  if (type->modifiers() & AbstractType::ConstModifier)
    return true;
  if (ArrayType::Ptr arrayType = type.cast<ArrayType>())
    return arrayType->elementType() ? isConstBased(arrayType->elementType()) : false;
  if (PointerType::Ptr ptrType = type.cast<PointerType>())
    return ptrType->baseType() ? isConstBased(ptrType->baseType()) : false;
  return false;
}

static bool isConstBased(const IndexedTypeIdentifier& type)
{
  return type.isConstant();
}

static bool isVolatileBased(const AbstractType::Ptr& type)
{
  if (type->modifiers() & AbstractType::VolatileModifier)
    return true;
  if (ArrayType::Ptr arrayType = type.cast<ArrayType>())
    return arrayType->elementType() ? isVolatileBased(arrayType->elementType()) : false;
  if (PointerType::Ptr ptrType = type.cast<PointerType>())
    return ptrType->baseType() ? isVolatileBased(ptrType->baseType()) : false;
  return false;
}

static bool isVolatileBased(const IndexedTypeIdentifier& type)
{
  return type.isVolatile();
}

template<typename A, typename B, typename C>
static bool matchCV(const A& parameterType, const B& argumentType, C* res)
{
  if (isConstBased(parameterType))
  {
    if (!argumentType.template cast<PointerType>() && isConstBased(argumentType))
      res->constMatch = true;
    else
    {
      res->valid = false;
      return false; //Invalid, param is const and arg is either non-const or has a different ptr-depth
    }
  }
  if (isVolatileBased(parameterType))
  {
    if (!argumentType.template cast<PointerType>() && isVolatileBased(argumentType))
      res->volatileMatch = true;
    else
    {
      res->valid = false;
      return false; //Invalid, param is volatile and arg is either non-volatile or has a different ptr-depth
    }
  }
  return true;
}

TemplateResolver::TemplateResolver(const TopDUContext* topContext)
:m_topContext(topContext) { }

uint TemplateResolver::matchTemplateParameterTypes( const AbstractType::Ptr& argumentType, const AbstractType::Ptr& parameterType, QMap< IndexedString, AbstractType::Ptr >& instantiatedTypes ) const
{
  if ( !argumentType && !parameterType )
    return 1;
  if ( !argumentType || !parameterType )
    return 0;

  TemplateMatchType matchResult;
  matchTemplateParameterTypesInternal(argumentType, parameterType, instantiatedTypes, matchResult);
  return matchResult.toUint();
}

bool TemplateResolver::templateHandleConstIntegralType(const AbstractType::Ptr& argumentType, const AbstractType::Ptr& parameterType, TemplateMatchType& res) const
{
  ConstantIntegralType::Ptr argumentIntegral = argumentType.cast<ConstantIntegralType>();
  ConstantIntegralType::Ptr parameterIntegral = parameterType.cast<ConstantIntegralType>();
  if (argumentIntegral && parameterIntegral)
  {
    if (argumentIntegral->plainValue() != parameterIntegral->plainValue())
      res.valid = false;
    return true; //Handled, valid if integral types match, invalid otherwise
  }
  else if (parameterIntegral)
  {
    //Nothing but an equal integral will match an integral parameter
    res.valid = false;
    return true; //Handled, invalid.
  }
  else if (argumentIntegral && !parameterType.cast<DelayedType>())
  {
    res.valid = false; //A const integral arg can only match an equal const integral or replace a delayed type
    return true; //Handled, invalid
  }
  return false;
}

bool TemplateResolver::templateHandleDelayedType ( const AbstractType::Ptr& argumentType, const AbstractType::Ptr& parameterType, QMap< IndexedString, AbstractType::Ptr >& instantiatedTypes, TemplateMatchType& res ) const
{
  DelayedType::Ptr delayed = parameterType.cast<DelayedType>();
  if ( !delayed )
    return false; //Not delayed type, not handled

  IndexedTypeIdentifier paramDelayedId = delayed->identifier();
  //Delayed id should never have pointer depth, or it would be a pointerType and not a delayedType
  //If it's possible somehow for it to be both, it's not correctly handled here
  Q_ASSERT(!paramDelayedId.pointerDepth());
  matchCV(paramDelayedId, argumentType, &res);
  ///TODO: the code only uses the last identifier and used to verify that
  ///      only one Identifier is actually contained in the QualifiedIdentifier
  ///      in the paramDelayedId
  ///      This caused issues for __gnu_cxx::_S_mutex, _S_single, _S_atomic etc.
  ///      it's not clear whether this is actually a bug or not - someone should
  ///      investigate. But rather don't assert for now!
  IndexedString identifier = paramDelayedId.identifier().identifier().last().identifier();
  if ( instantiatedTypes.contains( identifier ) )
    instantiatedTypes[identifier] = argumentType;
  else
    res.valid = false;

  return true; //Parameter was delayed type, delayed type handled
}

bool TemplateResolver::templateHandleReferenceType(const AbstractType::Ptr& argumentType, const AbstractType::Ptr& parameterType, QMap< IndexedString, AbstractType::Ptr >& instantiatedTypes, TemplateMatchType& res) const
{
  ReferenceType::Ptr argumentRef = argumentType.cast<ReferenceType>();
  ReferenceType::Ptr parameterRef = parameterType.cast<ReferenceType>();
  if ( argumentRef && parameterRef && argumentRef->isRValue() == parameterRef->isRValue() )
  {
    ///In case of references on both sides, match the target-types
    res.referenceMatch = true;
    matchTemplateParameterTypesInternal( argumentRef->baseType(), parameterRef->baseType(), instantiatedTypes, res );
    return true; //Handled by matching base types
  }
  else if (argumentRef)
  {
    //Argument is a reference of some sort, but will match non-reference CppTemplateParameterType
    if (parameterType.cast<CppTemplateParameterType>())
      matchTemplateParameterTypesInternal( argumentRef->baseType(), parameterType, instantiatedTypes, res);
    else
      res.valid = false;
    return true; //Handled by matching argument base type against template param,
                 //or invalidated because argument is ref and param isn't
  }
  else if ( parameterRef )
  {
    res.valid = false;
    return true; //Handled, invalid as the parameter is a reference type but the argument is not
  }

  return false; //No references, not handled
}

bool TemplateResolver::templateHandlePointerType(const AbstractType::Ptr& argumentType, const AbstractType::Ptr& parameterType, QMap< IndexedString, AbstractType::Ptr >& instantiatedTypes, TemplateMatchType& res) const
{
  ///In case of pointers on both sides, match the target-types
  PointerType::Ptr argumentPointer = argumentType.cast<PointerType>();
  PointerType::Ptr parameterPointer = parameterType.cast<PointerType>();
  if ( argumentPointer && parameterPointer && (( argumentPointer->modifiers() & AbstractType::ConstModifier ) == ( parameterPointer->modifiers() & AbstractType::ConstModifier ) ) )
  {
    ++res.pointerMatchDepth;
    matchTemplateParameterTypesInternal( argumentPointer->baseType(), parameterPointer->baseType(), instantiatedTypes, res );
    return true; //Handled by matching base types
  }
  else if (argumentPointer)
  {
    if (!parameterPointer && !isConstBased(parameterType) && parameterType.cast<CppTemplateParameterType>())
    {
        matchTemplateParameterTypesInternal( argumentPointer->baseType(), parameterType, instantiatedTypes, res );
        return true; //Handled by matching argument base type
    }
    //If argument is a pointer (const or otherwise), it will not match a non-pointer const parameter
    //Even if parameter isn't const, unless it's a CppTemplateParameterType it won't match
    res.valid = false;
    return true; //Handled, invalid
  }
  else if (parameterPointer)
  {
    res.valid = false;
    return true; //Handled, invalid as parameter had addition unmatched pointer depth
  }

  return false; //Not handled, neither argument nor parameter are pointers
}

bool TemplateResolver::templateHandleArrayType(const AbstractType::Ptr& argumentType, const AbstractType::Ptr& parameterType, QMap< IndexedString, AbstractType::Ptr >& instantiatedTypes, TemplateMatchType& res) const
{
  ArrayType::Ptr argumentArray = argumentType.cast<ArrayType>();
  ArrayType::Ptr parameterArray = parameterType.cast<ArrayType>();
  if ( argumentArray && parameterArray
      && ( argumentArray->modifiers() & (AbstractType::VolatileModifier | AbstractType::ConstModifier ) )
          == ( parameterArray->modifiers() & (AbstractType::VolatileModifier | AbstractType::ConstModifier ) ) )
    {
    if ( argumentArray->modifiers() & AbstractType::ConstModifier )
      res.constMatch = true;
    if ( argumentArray->modifiers() & AbstractType::VolatileModifier )
      res.volatileMatch = true;
    res.arrayMatch = true;
    matchTemplateParameterTypesInternal( argumentArray->elementType(), parameterArray->elementType(), instantiatedTypes, res );
    return true;
  }
  else if (argumentArray)
  {
    //Argument type is array, but will match non-array CppTemplateParameterType
    if (parameterType.cast<CppTemplateParameterType>())
      matchTemplateParameterTypesInternal( argumentArray->elementType(), parameterType, instantiatedTypes, res );
    else
      res.valid = false;
    return true; //Handled, either by matching argument elementType or by invalidation because parameter type cannot match
  }
  else if (parameterArray)
  {
    res.valid = false; //Parameter is array type, argument must be array type
    return true;
  }
  return false;
}

bool TemplateResolver::templateHandleIdentifiedType(const AbstractType::Ptr& argumentType, const AbstractType::Ptr& parameterType, QMap< IndexedString, AbstractType::Ptr >& instantiatedTypes, TemplateMatchType& res) const
{
  ///Match assigned template-parameters, for example when matching QList<int> to QList<T>, assign int to T.
  const IdentifiedType* identifiedArgument = dynamic_cast<const IdentifiedType*>( argumentType.unsafeData() );
  const IdentifiedType* identifiedParameter = dynamic_cast<const IdentifiedType*>( parameterType.unsafeData() );

  if ( identifiedArgument && identifiedParameter )
  {
    Declaration* argumentDeclaration = identifiedArgument->declaration( m_topContext );
    Declaration* parameterDeclaration = identifiedParameter->declaration( m_topContext );
    if (!argumentDeclaration || !parameterDeclaration)
    {
      //TODO: Very rare case which may be a bug elsewhere and could use a test.
      return false; //Unhandled, let it be accepted or rejected later
    }
    TemplateDeclaration* argumentTemplateDeclaration = dynamic_cast<TemplateDeclaration*>( argumentDeclaration );
    TemplateDeclaration* parameterTemplateDeclaration = dynamic_cast<TemplateDeclaration*>( parameterDeclaration );
    if ( !argumentTemplateDeclaration || !parameterTemplateDeclaration )
    {
      //Is this as correct as using the indexedType of the non-template declarations?
      if (argumentDeclaration != parameterDeclaration)
        res.valid = false; //Two different non-template declarations == two different types
      return true; //Handled, with either invalid mismatched types or valid matched types
    }

    if ( argumentTemplateDeclaration->instantiatedFrom() == parameterTemplateDeclaration->instantiatedFrom() && argumentTemplateDeclaration->instantiatedFrom() )
    {
      InstantiationInformation argumentInstantiatedWith = argumentTemplateDeclaration->instantiatedWith().information();
      InstantiationInformation parameterInstantiatedWith = parameterTemplateDeclaration->instantiatedWith().information();
      if ( argumentInstantiatedWith.templateParametersSize() != parameterInstantiatedWith.templateParametersSize() )
      {
        res.valid = false;
        return true; //Handled, invalid
      }

      for ( uint a = 0; a < argumentInstantiatedWith.templateParametersSize(); ++a )
      {
        if ( !matchTemplateParameterTypes( argumentInstantiatedWith.templateParameters()[a].abstractType(), parameterInstantiatedWith.templateParameters()[a].abstractType(), instantiatedTypes) )
        {
          res.valid = false;
          return true; //Handled, invalid
        }
      }
      res.templateArgsMatch = true;
      return true; //Handled, valid
    }
  }
  else if (identifiedArgument || identifiedParameter)
  {
    //Is there any case wherein an identifiedArgument will go up against a CppTemplateParameterType?
    //If This is possible we need a test case
    Q_ASSERT(!parameterType.cast<CppTemplateParameterType>());
    res.valid = false;
    return true; //Handled, invalid. If only one is identified, it's not a match
  }

  return false;
}

void TemplateResolver::matchTemplateParameterTypesInternal ( const AbstractType::Ptr& argumentType, const AbstractType::Ptr& parameterType, QMap< IndexedString, AbstractType::Ptr >& instantiatedTypes, TemplateMatchType& res ) const
{
  if (!argumentType || !parameterType)
  {
    kWarning() << "Invalid Type Encountered";
    res.valid = false;
    return;
  }

  if (templateHandleConstIntegralType(argumentType, parameterType, res))
    return;
  if (templateHandleDelayedType(argumentType, parameterType, instantiatedTypes, res))
    return;
  if (templateHandleReferenceType(argumentType, parameterType, instantiatedTypes, res))
    return;
  if (templateHandlePointerType(argumentType, parameterType, instantiatedTypes, res))
    return;
  if (templateHandleArrayType(argumentType, parameterType, instantiatedTypes, res))
    return;

  if (!matchCV(parameterType, argumentType, &res)) {
    return;
  }

  if ( CppTemplateParameterType::Ptr templateParam = parameterType.cast<CppTemplateParameterType>() )
  {
    Declaration* decl = templateParam->declaration( m_topContext );
    if ( decl )
    {
      //Should not be possible to have a CPPTemplateParameterType with template ids..?
      Q_ASSERT(decl->identifier().templateIdentifiersCount() == 0);
      IndexedString id = decl->identifier().identifier();
      //FIXME: Sometimes when matching templates within templates, delayedType will set the identifier first
      //The other way around is also probably possible
      //This needs more work to make sure the right type is set here
      //Q_ASSERT(instantiatedTypes[id].isNull());
      instantiatedTypes[id] = argumentType;
      return;
    }
  }

  if (templateHandleIdentifiedType(argumentType, parameterType, instantiatedTypes, res))
    return;

  //This /should/ be correct for all unhandled cases
  if (parameterType->indexed() != argumentType->indexed())
    res.valid = false; //Invalid, types don't match
}