Dart: Constant evaluation error. The method '[]' can't be invoked in a constant expression - dart

I am getting an error on constant evaluation.
please take a look at this code:
class A {
final num a;
const A(this.a);
}
class B {
final A a;
const B(this.a);
}
main() {
const a = A(12);
const b = B(a); // this works fine
// I believe everything inside a const List is considered constant,
// please correct me if that is wrong
const aL = [ A(12), A(13) ]; // [ a, A(13) ] will not work either
const b2 = B(
aL[0], // here the error is happening
);
}
Error:
lib/logic/dartTest.dart:22:14: Error: Constant evaluation error:
const b2 = B(
^
lib/logic/dartTest.dart:23:7: Context: The method '[]' can't be invoked on '<A>[A {a: 12}, A {a: 13}]' in a constant expression. - 'A' is from 'package:t_angband/logic/dartTest.dart' ('lib/logic/dartTest.dart').
aL[0],
^
lib/logic/dartTest.dart:22:9: Context: While analyzing:
const b2 = B(
^
The list contains constant Object, then why the constant evaluation is failing? Shouldn't this be an analyzer issue? Am I missing something?
Thankyou.

Constant expressions can only build data, it cannot deconstruct it. You cannot call any methods on the constant objects except a handful of operations on numbers (and String.length, which also creates a number).
So, aL[0] is simply not a valid compile-time constant expression.
A possible fix may be to make b2 not constant!

Related

I know it is not null, but Dart doesn't know that. How do I tell it?

The code Dart is complaining about:
Map<String,int> get_frequency(String text) {
Map<String,int> retval = {};
for (int i = 0; i<text.length; ++i) {
retval[text[i]] = retval[text[i]] ?? 0;
retval[text[i]]++; //<---- this is where dart is complaining.*
}
return retval;
}
void main() {
const paragraphOfText = 'Once upon a time there was a Dart programmer who '
'had a challenging challenge to solve. Though the challenge was great, '
'a solution did come. The end.';
var data = get_frequency(paragraphOfText);
print(data);
}
Obviously the line marked with (*) can not be null, so how do I tell that to Dart? I tried the null assertion operator (!), but that didn't work.
Null Safety is enabled.
Error message:
challenge2.dart:5:20: Error: Operator '+' cannot be called on 'int?' because it is potentially null.
retval[text[i]]++;
^
challenge2.dart:5:20: Error: A value of type 'num' can't be assigned to a variable of type 'int'.
retval[text[i]]++;
^
A possible solution is to change the line
retval[text[i]]++; //<---- this is where dart is complaining.*
into
retval[text[i]] = retval[text[i]]! + 1;
Just figured it out.

clang-8: getting typedef information from DeclRefExpr node in AST

I have below test code:
typedef void (*funcPtrType)()
funcPtrType FPT;
void myFunc(){
}
int main(){
FPT = myFunc;
FPT();
return 0;
}
And following is the part of AST dump of this code:
My question is, from which API can I get the 'void (*)()' information from DeclRefExpr node?
Already tried dynamic casting this node to VarDecl but from it I could not reach the information I mentioned.
Thanks in advance.
If you have a DeclRefExpr, that is an expression that refers to a declared entity. Call the getDecl method to get the associated ValueDecl, which is the declaration itself. On that object, call getType to get the QualType, which is the type, possibly including cv-qualifiers.
For example:
DeclRefExpr const *dre = ...; // wherever you got it
ValueDecl const *decl = dre->getDecl();
QualType type = decl->getType();
In this case, the type is a typedef. To inspect the underlying type, call getTypePtr to get the unqualified type, then getUnqualifiedDesugaredType to skip typedefs:
clang::Type const *underType = type.getTypePtr()->getUnqualifiedDesugaredType();
You can then call, for example, underType->isPointerType() to find out if it is a pointer type, etc. See the documentation for clang::Type for other ways to query it.
If you want to get a string representation of underType, use the static QualType::print method, something like this:
LangOptions lo;
PrintingPolicy pp(lo);
std::string s;
llvm::raw_string_ostream rso(s);
QualType::print(underType, Qualifiers(), rso, lo, llvm::Twine());
errs() << "type as string: \"" << rso.str() << "\"\n";
For your example, this will print:
type as string: "void (*)()"

When is const optional in Dart 2?

In Dart Object() constructor is declared as const so:
identical(const Object(), const Object()); //true
I know that in Dart 2 the keyword const is optional and I thought that the previous statement was equivalent to:
identical(Object(), Object()); //false
But actually it seems to be equivalent to:
identical(new Object(), new Object()); //false
Now my doubts are:
1) When is const keyword optional?
2) Is there any way to ensure instances of my classes to be always constant without const keyword? So that I can obtain:
indentical(MyClass(), MyClass()); //true (is it possible?)
Dart 2 allows you to omit new everywhere. Anywhere you used to write new, you can now omit it.
Dart 2 also allows you to omit const in positions where it's implied by the context. Those positions are:
Inside a const object creations, map or list literal (const [1, [2, 3]]).
Inside a const object creation in metadata (#Foo(Bar()))
In the initializer expression of a const variable (const x = [1];).
In a switch case expression (case Foo(2):...).
There are two other locations where the language requires constant expressions, but which are not automatically made constant (for various reasons):
Optional parameter default values
initializer expressions of final fields in classes with const constructors
1 is not made const because we want to keep the option of making those expressions not need to be const in the future. 2 is because it's a non-local constraint - there is nothing around the expression that signifies that it must be const, so it's too easy to, e.g., remove the const from the constructor without noticing that it changes the behavior of the field initializer.
const is optional in a const context. Basically a const context is created when the expression has to be const to avoid compilation error.
In the following snippet you can see some place where const is optional:
class A {
const A(o);
}
main(){
// parameters of const constructors have to be const
var a = const A(const A());
var a = const A(A());
// using const to create local variable
const b = const A();
const b = A();
// elements of const lists have to be const
var c = const [const A()];
var c = const [A()];
// elements of const maps have to be const
var d = const {'a': const A()};
var d = const {'a': A()};
}
// metadatas are const
#A(const A())
#A(A())
class B {}
You can find more details in Optional new/const and Implicit Creation.

Case Insensitive String Comparison of Boost::Spirit Token Text in Semantic Action

I've got a tokeniser and a parser. the parser has a special token type, KEYWORD, for keywords (there are ~50). In my parser I want to ensure that the tokens are what I'd expect, so I've got rules for each. Like so:
KW_A = tok.KEYWORDS[_pass = (_1 == "A")];
KW_B = tok.KEYWORDS[_pass = (_1 == "B")];
KW_C = tok.KEYWORDS[_pass = (_1 == "C")];
This works well enough, but it's not case insensitive (and the grammar I'm trying to handle is!). I'd like to use boost::iequals, but attempts to convert _1 to an std::string result in the following error:
error: no viable conversion from 'const _1_type' (aka 'const actor<argument<0> >') to 'std::string' (aka 'basic_string<char>')
How can I treat these keywords as strings and ensure they're the expected text irrespective of case?
A little learning went a long way. I added the following to my lexer:
struct normalise_keyword_impl
{
template <typename Value>
struct result
{
typedef void type;
};
template <typename Value>
void operator()(Value const& val) const
{
// This modifies the original input string.
typedef boost::iterator_range<std::string::iterator> iterpair_type;
iterpair_type const& ip = boost::get<iterpair_type>(val);
std::for_each(ip.begin(), ip.end(),
[](char& in)
{
in = std::toupper(in);
});
}
};
boost::phoenix::function<normalise_keyword_impl> normalise_keyword;
// The rest...
};
And then used phoenix to bind the action to the keyword token in my constructor, like so:
this->self =
KEYWORD [normalise_keyword(_val)]
// The rest...
;
Although this accomplishes what I was after, It modifies the original input sequence. Is there some modification I could make so that I could use const_iterator instead of iterator, and avoid modifying my input sequence?
I tried returning an std::string copied from ip.begin() to ip.end() and uppercased using boost::toupper(...), assigning that to _val. Although it compiled and ran, there were clearly some problems with what it was producing:
Enter a sequence to be tokenised: select a from b
Input is 'select a from b'.
result is SELECT
Token: 0: KEYWORD ('KEYWOR')
Token: 1: REGULAR_IDENTIFIER ('a')
result is FROM
Token: 0: KEYWORD ('KEYW')
Token: 1: REGULAR_IDENTIFIER ('b')
Very peculiar, it appears I have some more learning to do.
Final Solution
Okay, I ended up using this function:
struct normalise_keyword_impl
{
template <typename Value>
struct result
{
typedef std::string type;
};
template <typename Value>
std::string operator()(Value const& val) const
{
// Copy the token and update the attribute value.
typedef boost::iterator_range<std::string::const_iterator> iterpair_type;
iterpair_type const& ip = boost::get<iterpair_type>(val);
auto result = std::string(ip.begin(), ip.end());
result = boost::to_upper_copy(result);
return result;
}
};
And this semantic action:
KEYWORD [_val = normalise_keyword(_val)]
With (and this sorted things out), a modified token_type:
typedef std::string::const_iterator base_iterator;
typedef boost::spirit::lex::lexertl::token<base_iterator, boost::mpl::vector<std::string> > token_type;
typedef boost::spirit::lex::lexertl::actor_lexer<token_type> lexer_type;
typedef type_system::Tokens<lexer_type> tokens_type;
typedef tokens_type::iterator_type iterator_type;
typedef type_system::Grammar<iterator_type> grammar_type;
// Establish our lexer and our parser.
tokens_type lexer;
grammar_type parser(lexer);
// ...
The important addition being boost::mpl::vector<std::string> >. The result:
Enter a sequence to be tokenised: select a from b
Input is 'select a from b'.
Token: 0: KEYWORD ('SELECT')
Token: 1: REGULAR_IDENTIFIER ('a')
Token: 0: KEYWORD ('FROM')
Token: 1: REGULAR_IDENTIFIER ('b')
I have no idea why this has corrected the problem so if someone could chime in with their expertise, I'm a willing student.

How does the const constructor actually work?

I've noticed it's possible to create a const constructor in Dart. In the documentation, it says that const word is used to denote something a compile time constant.
I was wondering what happens when I use a const constructor to create an object. Is this like an immutable object which is always the same and available at compile time? How does the concept of const constructor actually work? How is a const constructor different from a regular constructor?
Const constructor creates a "canonicalized" instance.
That is, all constant expressions begin canonicalized, and later these "canonicalized" symbols are used to recognize equivalence of these constants.
Canonicalization:
A process for converting data that has more than one possible representation into a "standard" canonical representation. This can be done to compare different representations for equivalence, to count the number of distinct data structures, to improve the efficiency of various algorithms by eliminating repeated calculations, or to make it possible to impose a meaningful sorting order.
This means that const expressions like const Foo(1, 1) can represent any usable form that is useful for comparison in virtual machine.
The VM only needs to take into account the value type and arguments in the order in which they occur in this const expression. And, of course, they are reduced for optimization.
Constants with the same canonicalized values:
var foo1 = const Foo(1, 1); // #Foo#int#1#int#1
var foo2 = const Foo(1, 1); // #Foo#int#1#int#1
Constants with different canonicalized values (because signatures differ):
var foo3 = const Foo(1, 2); // $Foo$int$1$int$2
var foo4 = const Foo(1, 3); // $Foo$int$1$int$3
var baz1 = const Baz(const Foo(1, 1), "hello"); // $Baz$Foo$int$1$int$1$String$hello
var baz2 = const Baz(const Foo(1, 2), "hello"); // $Baz$Foo$int$1$int$2$String$hello
Constants are not recreated each time. They are canonicalized at compile time and stored in special lookup tables (where they are hashed by their canonical signatures) from which they are later reused.
P.S.
The form #Foo#int#1#int#1 used in these samples is only used for comparison purposes and it is not a real form of canonicalization (representation) in Dart VM;
But the real canonicalization form must be "standard" canonical representation.
I find Lasse's answer on Chris Storms blog a great explanation.
Dart Constant Constructors
I hope they don't mind that I copy the content.
This is a fine explanation of final fields, but it doesn't really
explain const constructors. Nothing in these examples actually use
that the constructors are const constructors. Any class can have final
fields, const constructors or not.
A field in Dart is really an anonymous storage location combined with
an automatically created getter and setter that reads and updates the
storage, and it can also be initialized in a constructor's initializer
list.
A final field is the same, just without the setter, so the only way to
set its value is in the constructor initializer list, and there is no
way to change the value after that - hence the "final".
The point of const constructors is not to initialize final fields, any
generative constructor can do that. The point is to create
compile-time constant values: Objects where the all field values are
known already at compile time, without executing any statements.
That puts some restrictions on the class and constructor. A const
constructor can't have a body (no statements executed!) and its class
must not have any non-final fields (the value we "know" at compile
time must not be able to change later). The initializer list must also
only initialize fields to other compile-time constants, so the
right-hand sides are limited to "compile-time constant
expressions"[1]. And it must be prefixed with "const" - otherwise you
just get a normal constructor that happens to satisfy those
requirements. That is perfectly fine, it's just not a const
constructor.
In order to use a const constructor to actually create a compile-time
constant object, you then replace "new" with "const" in a
"new"-expression. You can still use "new" with a const-constructor,
and it will still create an object, but it will just be a normal new
object, not a compile-time constant value. That is: A const
constructor can also be used as a normal constructor to create objects
at runtime, as well as creating compile-time constant objects at
compilation time.
So, as an example:
class Point {
static final Point ORIGIN = const Point(0, 0);
final int x;
final int y;
const Point(this.x, this.y);
Point.clone(Point other): x = other.x, y = other.y; //[2]
}
main() {
// Assign compile-time constant to p0.
Point p0 = Point.ORIGIN;
// Create new point using const constructor.
Point p1 = new Point(0, 0);
// Create new point using non-const constructor.
Point p2 = new Point.clone(p0);
// Assign (the same) compile-time constant to p3.
Point p3 = const Point(0, 0);
print(identical(p0, p1)); // false
print(identical(p0, p2)); // false
print(identical(p0, p3)); // true!
}
Compile-time constants are canonicalized. That means the no matter how
many times you write "const Point(0,0)", you only create one object.
That may be useful - but not as much as it would seem, since you can
just make a const variable to hold the value and use the variable
instead.
So, what are compile-time constants good for anyway?
They are useful for enums.
You can use compile-time constant values in switch cases.
They are used as annotations.
Compile-time constants used to be more important before Dart switched
to lazily initializing variables. Before that, you could only declare
an initialized global variable like "var x = foo;" if "foo" was a
compile-time constant. Without that requirement, most programs can be
written without using any const objects
So, short summary: Const constructors are just for creating
compile-time constant values.
/L
[1] Or really: "Potentially compile-time constant expressions"
because it may also refer to the constructor parameters.
[2] So yes, a class can have both const and non-const constructors at the same time.
This topic was also discussed in https://github.com/dart-lang/sdk/issues/36079 with some interesting comments.
Very well explained in detail but for the users who are actually looking for the usage of a const constructor
It is used to Increase Flutter Performance as it helps Flutter to
rebuild only widgets that should be updated.Means while Using
setState() in StateFulWidgets, only those components will be rebuild
that are non const constructor
Can be explained with example->
class _MyWidgetState extends State<MyWidget> {
String title = "Title";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Column(
children: <Widget>[
const Text("Text 1"),
const Padding(
padding: const EdgeInsets.all(8.0),
child: const Text("Another Text widget"),
),
const Text("Text 3"),
],
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
setState(() => title = 'New Title');
},
),
);
}
}
As In this example, only Text title should get changed, so only this widget should get rebuild, so making all the others widgets as const constructor will help flutter to do the same for increasing performance.
in This video, You will know why we need it. https://www.youtube.com/watch?v=B1fIqdqwWw8&t=558s From 09:18 to 16:09
In Documentation: https://dart.dev/guides/language/language-tour
constant constructors. To create a compile-time constant using a
constant constructor, put the const keyword before the constructor
name:
> var p = const ImmutablePoint(2, 2);
Constructing two identical
compile-time constants results in a single, canonical instance:
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1,1);
assert(identical(a, b)); // They are the same instance!
Within a
constant context, you can omit the const before a constructor or
literal. For example, look at this code, which creates a const map:
// Lots of const keywords here.
const pointAndLine = const {
'point': const [const ImmutablePoint(0, 0)],
'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
You can omit
all but the first use of the const keyword:
// Only one const, which establishes the constant context.
const pointAndLine = {
'point': [ImmutablePoint(0, 0)],
'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)], };
If a constant
constructor is outside of a constant context and is invoked without
const, it creates a non-constant object:
> var a = const ImmutablePoint(1, 1); // Creates a constant
> var b = ImmutablePoint(1, 1); // Does NOT create a constant
>
> assert(!identical(a, b));// NOT the same instance!
For more information you can check these 2 Answers below:
1- https://stackoverflow.com/a/21746692/14409491
2- https://stackoverflow.com/a/21745617/14409491
An example demo that const instance really decide by final field.
And in this case, it cannot be predict in compile-time.
import 'dart:async';
class Foo {
final int i;
final int j = new DateTime.now().millisecond;
const Foo(i) : this.i = i ~/ 10;
toString() => "Foo($i, $j)";
}
void main() {
var f2 = const Foo(2);
var f3 = const Foo(3);
print("f2 == f3 : ${f2 == f3}"); // true
print("f2 : $f2"); // f2 : Foo(0, 598)
print("f3 : $f3"); // f3 : Foo(0, 598)
new Future.value().then((_) {
var f2i = const Foo(2);
print("f2 == f2i : ${f2 == f2i}"); // false
print("f2i : $f2i"); // f2i : Foo(0, 608)
});
}
Now dart will check it.
Dart Analysis:
[dart] Can't define the 'const' constructor because the field 'j' is initialized with a non-constant value
Runtime Error:
/main.dart': error: line 5 pos 17: expression is not a valid compile-time constant
final int j = new DateTime.now().millisecond;

Resources